import * as _exortek_jwks from '@exortek/jwks'; /** * Define a provider. Returns a factory that a consumer calls with their * per-app credentials; the result is passed to `createOAuth`. * * @param {ProviderDefinition} def * @returns {(appOptions: ProviderAppOptions) => ResolvedProvider} * * @typedef {Object} ProviderDefinition * @property {string} id default provider key (`'google'`) * @property {'oidc'|'oauth2'} kind * @property {string} [authorizationEndpoint] * @property {string} [tokenEndpoint] * @property {string} [userinfoEndpoint] * @property {string} [jwksUri] * @property {string} [revocationEndpoint] * @property {string | ((claimed: string) => boolean)} [issuer] OIDC issuer — exact string, or a validator (multi-tenant) * @property {string} [expectedIssuer] overrides `issuer` for the RFC 9207 `iss` param * @property {boolean} [requireIssParam] require the RFC 9207 `iss` response param (a provider known to send it) * @property {string} [emailEndpoint] secondary email fetch (github) * @property {boolean} [discover] resolve endpoints from `issuer` discovery * @property {boolean} [supportsPkce] default true * @property {string[]} [defaultScopes] * @property {boolean} [autoOpenidScope] prepend `openid` for OIDC (default true; Apple sets false) * @property {string[]} [idTokenAlgs] * @property {import('@exortek/jwks').RemoteJWKSOptions} [jwksOptions] forwarded to the JWKS resolver * @property {'post'|'basic'} [clientAuth] client authentication at the token endpoint (default `post`) * @property {Record} [authorizationParams] extra static auth-request params * @property {Record} [tokenHeaders] extra headers on the token/refresh/revoke calls * @property {Record} [userinfoHeaders] extra headers on the userinfo/email calls (falls back to tokenHeaders) * @property {(raw: Record, claims?: Record) => NormalizedUserFields} mapUser * * @typedef {Object} ProviderAppOptions * @property {string} clientId * @property {string} [clientSecret] * @property {string[]} [scope] * @property {string} [redirectUri] * @property {string} [id] * * @typedef {Object} NormalizedUserFields * @property {string} sub * @property {string} [email] * @property {boolean} [emailVerified] * @property {string} [name] * @property {string} [picture] */ declare function defineProvider(def: ProviderDefinition): (appOptions: ProviderAppOptions) => ResolvedProvider$1; /** * Non-fatal, degraded-but-not-blocked conditions. Surfaced in * `warnings[]` so the caller can react without the flow hard-failing. */ declare const WarningCode: Readonly<{ PKCE_UNSUPPORTED: "PKCE_UNSUPPORTED"; SCOPE_NARROWED: "SCOPE_NARROWED"; EMAIL_UNVERIFIED: "EMAIL_UNVERIFIED"; }>; /** * Define a provider. Returns a factory that a consumer calls with their * per-app credentials; the result is passed to `createOAuth`. */ type ProviderDefinition = { /** * default provider key (`'google'`) */ id: string; kind: "oidc" | "oauth2"; authorizationEndpoint?: string | undefined; tokenEndpoint?: string | undefined; userinfoEndpoint?: string | undefined; jwksUri?: string | undefined; revocationEndpoint?: string | undefined; /** * OIDC issuer — exact string, or a validator (multi-tenant) */ issuer?: string | ((claimed: string) => boolean) | undefined; /** * overrides `issuer` for the RFC 9207 `iss` param */ expectedIssuer?: string | undefined; /** * require the RFC 9207 `iss` response param (a provider known to send it) */ requireIssParam?: boolean | undefined; /** * secondary email fetch (github) */ emailEndpoint?: string | undefined; /** * resolve endpoints from `issuer` discovery */ discover?: boolean | undefined; /** * default true */ supportsPkce?: boolean | undefined; defaultScopes?: string[] | undefined; /** * prepend `openid` for OIDC (default true; Apple sets false) */ autoOpenidScope?: boolean | undefined; idTokenAlgs?: string[] | undefined; /** * forwarded to the JWKS resolver */ jwksOptions?: _exortek_jwks.RemoteJWKSOptions; /** * client authentication at the token endpoint (default `post`) */ clientAuth?: "post" | "basic" | undefined; /** * extra static auth-request params */ authorizationParams?: Record | undefined; /** * extra headers on the token/refresh/revoke calls */ tokenHeaders?: Record | undefined; /** * extra headers on the userinfo/email calls (falls back to tokenHeaders) */ userinfoHeaders?: Record | undefined; mapUser: (raw: Record, claims?: Record) => NormalizedUserFields; }; /** * Define a provider. Returns a factory that a consumer calls with their * per-app credentials; the result is passed to `createOAuth`. */ type ProviderAppOptions = { clientId: string; clientSecret?: string | undefined; scope?: string[] | undefined; redirectUri?: string | undefined; id?: string | undefined; }; /** * Define a provider. Returns a factory that a consumer calls with their * per-app credentials; the result is passed to `createOAuth`. */ type NormalizedUserFields = { sub: string; email?: string | undefined; emailVerified?: boolean | undefined; name?: string | undefined; picture?: string | undefined; }; type Warning$1 = { code: string; message: string; }; type ResolvedProvider$1 = ReturnType>; /** * @typedef {Object} OAuthConfig * @property {string} baseUrl app origin, e.g. `https://app.com` * @property {string} callback callback template, e.g. `/auth/{provider}/callback` * @property {SessionStore} [store] optional server-side session store (keyed by `state`) * @property {{ maxAuthAge?: string|number, clockTolerance?: string|number }} [security] * @property {ResolvedProvider[]} providers provider descriptors from `./providers/*` * * @typedef {Object} SessionStore * @property {(key: string, value: string, ttlMs: number) => unknown} set * @property {(key: string) => (string | null | undefined) | Promise} get * @property {(key: string) => unknown} delete * * @typedef {import('./providers/_base.js').ResolvedProvider} ResolvedProvider */ /** * @param {OAuthConfig} config */ declare function createOAuth(config: OAuthConfig): { /** * Begin the flow. Returns the authorization `url` to redirect to and * an opaque `session` string to stash (cookie / store). When a store * is configured the session is also persisted keyed by `state`. * * @param {string} name * @param {{ scope?: string[], sessionBinding?: string, params?: Record }} [options] */ authorize(name: string, options?: { scope?: string[]; sessionBinding?: string; params?: Record; }): Promise<{ url: string; session: string; warnings: Warning$1[]; }>; /** * Complete the flow. Provide the `session` returned by `authorize`, * or rely on the configured store to look it up by `query.state`. * * @param {string} name * @param {Record} query the callback query params * @param {{ session?: string, sessionBinding?: string }} [options] */ callback(name: string, query: Record, options?: { session?: string; sessionBinding?: string; }): Promise<{ tokens: Record; user: NormalizedUser; warnings: Warning[]; }>; /** * Exchange a refresh token for a fresh access token (RFC 6749 §6). * * @param {string} name * @param {string} refreshToken * @returns {Promise>} */ refresh(name: string, refreshToken: string): Promise>; /** * Revoke an access or refresh token (RFC 7009). * * @param {string} name * @param {string} token * @param {string} [tokenTypeHint] `'access_token'` | `'refresh_token'` * @returns {Promise>} */ revoke(name: string, token: string, tokenTypeHint?: string): Promise>; /** @returns {string[]} the registered provider ids */ readonly providers: string[]; /** @param {string} name @returns {boolean} */ has(name: string): boolean; }; type OAuthConfig = { /** * app origin, e.g. `https://app.com` */ baseUrl: string; /** * callback template, e.g. `/auth/{provider}/callback` */ callback: string; /** * optional server-side session store (keyed by `state`) */ store?: SessionStore | undefined; security?: { maxAuthAge?: string | number; clockTolerance?: string | number; } | undefined; /** * provider descriptors from `./providers/*` */ providers: ResolvedProvider[]; }; type SessionStore = { set: (key: string, value: string, ttlMs: number) => unknown; get: (key: string) => (string | null | undefined) | Promise; delete: (key: string) => unknown; }; type ResolvedProvider = ResolvedProvider$1; /** * Shared base error class — the single error structure behind every * `@exortek/*` package's `errors.js`. * * Every package keeps its own class identity with a one-liner subclass; * codes stay per-package frozen maps, status mapping is declared as a * static field: * * import { BaseError } from '@exortek/shared/errors'; * * export const ErrorCode = Object.freeze({ * INVALID_ARGUMENT: 'INVALID_ARGUMENT', * INVALID_TOKEN: 'INVALID_TOKEN', * }); * * export class JwtError extends BaseError { * static statuses = { INVALID_ARGUMENT: 400, INVALID_TOKEN: 401 }; * static defaultStatus = 500; * } * * Instances carry a stable machine-readable `code` (branch on this, * never on the message), an optional HTTP `status`, an optional * `details` object, and the standard `cause` chain. */ declare class BaseError extends Error { /** * Optional `code → HTTP status` map declared on the subclass. When * absent the instance carries no `status` at all — for HTTP-agnostic * packages like `@exortek/crypto`. * * @type {Record | undefined} */ static statuses: Record | undefined; /** * Fallback status for codes missing from `statuses`. * * @type {number} */ static defaultStatus: number; /** * @param {string} code Stable machine-readable code; branch on this. * @param {string} message Human-readable diagnostic. Free-form; may * change across versions. * @param {{ cause?: unknown, status?: number, details?: Record }} [options] */ constructor(code: string, message: string, options?: { cause?: unknown; status?: number; details?: Record; }); /** @type {string} */ code: string; /** @type {number | undefined} */ status: number | undefined; /** @type {Record | undefined} */ details: Record | undefined; } declare const ErrorCode: Readonly<{ INVALID_ARGUMENT: "INVALID_ARGUMENT"; MISSING_STATE: "MISSING_STATE"; STATE_MISMATCH: "STATE_MISMATCH"; ISSUER_MISMATCH: "ISSUER_MISMATCH"; NONCE_MISMATCH: "NONCE_MISMATCH"; SUB_MISMATCH: "SUB_MISMATCH"; AUDIENCE_MISMATCH: "AUDIENCE_MISMATCH"; ID_TOKEN_INVALID: "ID_TOKEN_INVALID"; CONTEXT_MISMATCH: "CONTEXT_MISMATCH"; SESSION_MISMATCH: "SESSION_MISMATCH"; SESSION_EXPIRED: "SESSION_EXPIRED"; PROVIDER_ERROR: "PROVIDER_ERROR"; TOKEN_EXCHANGE_FAILED: "TOKEN_EXCHANGE_FAILED"; USERINFO_FAILED: "USERINFO_FAILED"; DISCOVERY_FAILED: "DISCOVERY_FAILED"; NETWORK_ERROR: "NETWORK_ERROR"; }>; /** * Every recoverable failure raised by this package. Carries a stable * `code` (from {@link ErrorCode}) and a `status` — the HTTP response * status a middleware layer would use when translating the error. * * Callback-validation failures map to `400` (the request that reached us * is malformed / forged); failures talking to the provider map to `502` * (an upstream we depend on misbehaved). */ declare class OAuth2Error extends BaseError { static statuses: { INVALID_ARGUMENT: number; MISSING_STATE: number; STATE_MISMATCH: number; ISSUER_MISMATCH: number; NONCE_MISMATCH: number; SUB_MISMATCH: number; AUDIENCE_MISMATCH: number; ID_TOKEN_INVALID: number; CONTEXT_MISMATCH: number; SESSION_MISMATCH: number; SESSION_EXPIRED: number; PROVIDER_ERROR: number; TOKEN_EXCHANGE_FAILED: number; USERINFO_FAILED: number; DISCOVERY_FAILED: number; NETWORK_ERROR: number; }; } /** * The `S256` transform: `BASE64URL(SHA256(ASCII(code_verifier)))`. * * @param {string} codeVerifier * @returns {string} the `code_challenge` */ declare function challengeFromVerifier(codeVerifier: string): string; /** * @typedef {Object} PkcePair * @property {string} codeVerifier keep server-side; send at token exchange * @property {string} codeChallenge send on the authorization request * @property {'S256'} codeChallengeMethod */ /** * Generate a fresh `code_verifier` / `code_challenge` pair. * * @returns {PkcePair} */ declare function createPkcePair(): PkcePair; /** * Recompute the challenge from a presented verifier and compare it, * in constant time, against the one bound to the authorization request. * The authorization server calls this at the token endpoint. * * @param {string} codeVerifier presented by the client at token exchange * @param {string} codeChallenge bound to the authorization code * @returns {boolean} */ declare function verifyChallenge(codeVerifier: string, codeChallenge: string): boolean; declare const CODE_CHALLENGE_METHOD: "S256"; type PkcePair = { /** * keep server-side; send at token exchange */ codeVerifier: string; /** * send on the authorization request */ codeChallenge: string; codeChallengeMethod: "S256"; }; /** * @param {number} [bytes] entropy in bytes (default 32 → 256 bits) * @returns {string} base64url-encoded random value */ declare function randomState(bytes?: number): string; /** * @param {number} [bytes] entropy in bytes (default 32 → 256 bits) * @returns {string} base64url-encoded random value */ declare function randomNonce(bytes?: number): string; export { CODE_CHALLENGE_METHOD, ErrorCode, OAuth2Error, WarningCode, challengeFromVerifier, createOAuth, createPkcePair, defineProvider, randomNonce, randomState, verifyChallenge };