type IdTokenSignerConfig = { /** * private (asymmetric) signing key */ signingKey: any | string | Uint8Array; /** * JWS alg, e.g. `'RS256'` / `'ES256'` / `'EdDSA'` (never `none`/HS*) */ alg: string; /** * `kid` header so a client picks the right JWKS key */ kid?: string | undefined; /** * id_token lifetime (default `'10m'`) */ expiresIn?: string | number | undefined; }; type ClientRegistry = { getClient: (clientId: string) => (Client | undefined) | Promise; }; type ClientConfig = { clientId: string; /** * confidential clients only */ clientSecret?: string | undefined; /** * exact-match allowlist (RFC 6749 §3.1.2) */ redirectUris: string[]; /** * default `['authorization_code','refresh_token']` */ grantTypes?: string[] | undefined; /** * default `['code']` */ responseTypes?: string[] | undefined; /** * one of {@link AUTH_METHODS} */ tokenEndpointAuthMethod?: string | undefined; /** * scopes the client may request (omit = server default) */ scope?: string[] | undefined; /** * for `private_key_jwt` / signed request objects */ jwksUri?: string | undefined; /** * inline JWKS alternative to `jwksUri` */ jwks?: object | undefined; /** * require DPoP for this client (RFC 9449 §5.2) */ dpopBoundAccessTokens?: boolean | undefined; /** * require PAR (RFC 9126 §2) */ requirePushedAuthorizationRequests?: boolean | undefined; /** * expected cert subject DN for `tls_client_auth` (RFC 8705 §2.1) */ tlsClientAuthSubjectDn?: string | undefined; /** * base64url `x5t#S256` for `self_signed_tls_client_auth` (RFC 8705 §2.2) */ certificateThumbprint?: string | undefined; }; type Client = Readonly; /** * @typedef {Object} AuthCodeRecord * @property {string} clientId * @property {string} redirectUri * @property {string} codeChallenge * @property {string[]} scope * @property {string} [nonce] * @property {string} [subject] * @property {number} [authTime] unix seconds of the authentication event → OIDC `auth_time` * @property {string} [resource] * @property {unknown} [authorizationDetails] * @property {string} [dpopJkt] DPoP `jkt` bound at the authorize step (RFC 9449 §10) * * @typedef {Object} AuthCodeStore * @property {(code: string, record: AuthCodeRecord, ttlMs: number) => void | Promise} save * @property {(code: string) => (AuthCodeRecord | undefined) | Promise} consume */ /** @returns {AuthCodeStore} */ declare function createAuthCodeStore(): AuthCodeStore; /** * @typedef {Object} RefreshRecord * @property {string} token the opaque refresh token (primary key) * @property {string} familyId shared across every rotation of one grant * @property {string} clientId * @property {string[]} scope * @property {string} [subject] * @property {string} [resource] * @property {string} [dpopJkt] * @property {number} [expiresAt] epoch ms after which the token is expired * @property {boolean} [used] set when rotated away — a later presentation is reuse * @property {boolean} [revoked] set when the whole family was burned (reuse detected) * * @typedef {Object} RefreshStore * @property {(record: RefreshRecord) => Promise} save * @property {(token: string) => Promise} get * @property {(oldToken: string, next: RefreshRecord) => Promise} rotate * @property {(familyId: string) => Promise} revokeFamily */ /** @returns {RefreshStore} */ declare function createRefreshStore(): RefreshStore; /** * @typedef {Object} ParStore * @property {(requestUri: string, params: Record, ttlMs: number) => void | Promise} save * @property {(requestUri: string) => (Record | undefined) | Promise | undefined>} consume */ /** @returns {ParStore} */ declare function createParStore(): ParStore; /** * @typedef {Object} DeviceRecord * @property {string} deviceCode * @property {string} userCode * @property {string} clientId * @property {string[]} scope * @property {'pending' | 'approved' | 'denied' | 'redeemed'} status * @property {string} [subject] set on approval * @property {number} [lastPolledAt] for slow_down enforcement * * @typedef {Object} DeviceStore * @property {(record: DeviceRecord, ttlMs: number) => void | Promise} save * @property {(deviceCode: string) => (DeviceRecord | undefined) | Promise} getByDeviceCode * @property {(userCode: string) => (DeviceRecord | undefined) | Promise} getByUserCode * @property {(deviceCode: string, patch: Partial) => void | Promise} update */ /** @returns {DeviceStore} */ declare function createDeviceStore(): DeviceStore; type AuthCodeRecord = { clientId: string; redirectUri: string; codeChallenge: string; scope: string[]; nonce?: string | undefined; subject?: string | undefined; /** * unix seconds of the authentication event → OIDC `auth_time` */ authTime?: number | undefined; resource?: string | undefined; authorizationDetails?: unknown; /** * DPoP `jkt` bound at the authorize step (RFC 9449 §10) */ dpopJkt?: string | undefined; }; type AuthCodeStore = { save: (code: string, record: AuthCodeRecord, ttlMs: number) => void | Promise; consume: (code: string) => (AuthCodeRecord | undefined) | Promise; }; type RefreshRecord = { /** * the opaque refresh token (primary key) */ token: string; /** * shared across every rotation of one grant */ familyId: string; clientId: string; scope: string[]; subject?: string | undefined; resource?: string | undefined; dpopJkt?: string | undefined; /** * epoch ms after which the token is expired */ expiresAt?: number | undefined; /** * set when rotated away — a later presentation is reuse */ used?: boolean | undefined; /** * set when the whole family was burned (reuse detected) */ revoked?: boolean | undefined; }; type RefreshStore = { save: (record: RefreshRecord) => Promise; get: (token: string) => Promise; rotate: (oldToken: string, next: RefreshRecord) => Promise; revokeFamily: (familyId: string) => Promise; }; type ParStore = { save: (requestUri: string, params: Record, ttlMs: number) => void | Promise; consume: (requestUri: string) => (Record | undefined) | Promise | undefined>; }; type DeviceRecord = { deviceCode: string; userCode: string; clientId: string; scope: string[]; status: "pending" | "approved" | "denied" | "redeemed"; /** * set on approval */ subject?: string | undefined; /** * for slow_down enforcement */ lastPolledAt?: number | undefined; }; type DeviceStore = { save: (record: DeviceRecord, ttlMs: number) => void | Promise; getByDeviceCode: (deviceCode: string) => (DeviceRecord | undefined) | Promise; getByUserCode: (userCode: string) => (DeviceRecord | undefined) | Promise; update: (deviceCode: string, patch: Partial) => void | Promise; }; type RawRequest$1 = { method?: string | undefined; /** * full URL or path (`?query` parsed when `query` is absent) */ url?: string | undefined; headers?: Record | undefined; /** * pre-parsed query, else derived from `url` */ query?: Record | undefined; /** * raw string or a framework-parsed object */ body?: string | Record | undefined; /** * mTLS client cert (RFC 8705) */ clientCertificate?: any | Record; }; type ServerRequest = { method: string; /** * lower-cased names */ headers: Record; query: Record; /** * parsed POST body params */ form: Record; header: (name: string) => string | undefined; /** * query ∪ form (form wins) */ param: (name: string) => string | undefined; clientCertificate: object | undefined; }; type ServerResponse$1 = { status: number; headers: Record; body: string; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. * * @typedef {Object} ServerSecurity * @property {string|number} [authorizationCodeTtl='1m'] * @property {string|number} [refreshTokenTtl='30d'] lifetime of an issued refresh token * @property {{ required?: boolean, algs?: string[], nonce?: boolean }} [dpop] RFC 9449 * @property {{ required?: boolean, ttl?: string|number }} [par] RFC 9126 * @property {boolean} [fapi] FAPI 2.0 profile (bundles PAR + PKCE + DPoP/mTLS + iss) * @property {(hostname: string, url: URL) => boolean} [allowJwksHost] gate the hosts the server will fetch a client's `jwks_uri` from; return false to refuse. Recommended wherever dynamic client registration is enabled, since the URI is then chosen by the registrant. * * @typedef {Object} ServerConfig * @property {string} issuer AS issuer identifier (https, no trailing slash) * @property {import('./clients.js').ClientConfig[] | import('./clients.js').ClientRegistry} clients * @property {{ issue: Function, introspect: Function }} tokens an issuer strategy (jwtIssuer / pasetoIssuer) * @property {string} [jwksUri] where the AS publishes its signing JWKS (advertised only) * @property {(req: import('./request.js').ServerRequest) => (UserAuthn | null) | Promise} authenticateUser * @property {string[]} [grants] allowed grant types (default code + refresh + client_credentials) * @property {string[]} [scopes] supported scopes (advertised; also the escalation ceiling) * @property {Partial} [endpoints] override the endpoint URLs advertised in metadata * @property {Partial} [stores] swap in Redis-backed stores * @property {ServerSecurity} [security] * @property {OidcConfig} [oidc] make this an OpenID Provider (issue id_token on the `openid` scope) * @property {RegistrationConfig} [registration] enable RFC 7591 dynamic client registration (opt-in) * * @typedef {Object} RegistrationConfig * @property {string} [initialAccessToken] bearer required to register (secure default; omit only with `open`) * @property {boolean} [open] allow unauthenticated registration (NOT recommended) * @property {(req: import('./request.js').ServerRequest) => boolean | Promise} [authorize] custom gate * @property {Partial} [defaults] defaults layered under each registration * * @typedef {Object} OidcConfig * @property {import('./issuers/id-token.js').IdTokenSignerConfig} idToken id_token JWS signer (signingKey + alg) * @property {string} [userinfoEndpoint] advertised in metadata as `userinfo_endpoint` * @property {string[]} [claimsSupported] advertised as `claims_supported` * @property {string[]} [subjectTypes] advertised as `subject_types_supported` (default `['public']`) * * @typedef {Object} UserAuthn * @property {string} subject resource-owner identifier → `sub` * @property {number} [authTime] unix seconds of the authentication event (`auth_time`) * * @typedef {Object} Endpoints * @property {string} authorization * @property {string} token * @property {string} [introspection] * @property {string} [revocation] * @property {string} [par] * @property {string} [deviceAuthorization] * * @typedef {Object} ServerStores * @property {ReturnType} authCode * @property {ReturnType} refresh * @property {ReturnType} par * @property {ReturnType} device * * @typedef {Object} ResolvedServerConfig * @property {string} issuer * @property {Endpoints} endpoints * @property {string} [jwksUri] * @property {string[]} grantTypes * @property {string[]} [scopes] * @property {string[]} authMethods * @property {string[]} [dpopAlgs] * @property {boolean} requirePar */ /** * @param {ServerConfig} config */ declare function createServer(config: ServerConfig): { /** RFC 8414 authorization-server metadata document. */ metadata: () => ServerResponse$1; /** Authorization endpoint (RFC 6749 §4.1.1) — code grant, PKCE-required. */ authorize: (raw: RawRequest$1) => Promise; /** Token endpoint (RFC 6749 §3.2) — code / refresh / client_credentials grants. */ token: (raw: RawRequest$1) => Promise; /** Token revocation endpoint (RFC 7009). */ revoke: (raw: RawRequest$1) => Promise; /** Token introspection endpoint (RFC 7662). */ introspect: (raw: RawRequest$1) => Promise; /** Pushed authorization request endpoint (RFC 9126). */ par: (raw: RawRequest$1) => Promise; /** Device authorization endpoint (RFC 8628). */ deviceAuthorization: (raw: RawRequest$1) => Promise; /** Dynamic client registration endpoint (RFC 7591) — opt-in. */ register: (raw: RawRequest$1) => Promise; /** * Device-approval API for the host's verification page — look up a * pending request by `user_code`, then `approve({ subject })` or * `deny()` it (RFC 8628 §3.3). */ device: { getByUserCode(userCode: string): Promise; approve(userCode: string, approval: { subject: string; }): Promise; deny(userCode: string): Promise; }; /** @internal exposed for tests and the framework adapters */ _config: ResolvedServerConfig & { registry: any; tokens: any; stores: any; security: any; requirePkce: any; requirePar: any; dpopRequired: any; authenticateUser: any; authorizationCodeTtlMs: any; parTtlMs: any; fapi: any; }; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type ServerSecurity = { authorizationCodeTtl?: string | number | undefined; /** * lifetime of an issued refresh token */ refreshTokenTtl?: string | number | undefined; /** * RFC 9449 */ dpop?: { required?: boolean; algs?: string[]; nonce?: boolean; } | undefined; /** * RFC 9126 */ par?: { required?: boolean; ttl?: string | number; } | undefined; /** * FAPI 2.0 profile (bundles PAR + PKCE + DPoP/mTLS + iss) */ fapi?: boolean | undefined; /** * gate the hosts the server will fetch a client's `jwks_uri` from; return false to refuse. Recommended wherever dynamic client registration is enabled, since the URI is then chosen by the registrant. */ allowJwksHost?: ((hostname: string, url: URL) => boolean) | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type ServerConfig = { /** * AS issuer identifier (https, no trailing slash) */ issuer: string; clients: ClientConfig[] | ClientRegistry; /** * an issuer strategy (jwtIssuer / pasetoIssuer) */ tokens: { issue: Function; introspect: Function; }; /** * where the AS publishes its signing JWKS (advertised only) */ jwksUri?: string | undefined; authenticateUser: (req: ServerRequest) => (UserAuthn | null) | Promise; /** * allowed grant types (default code + refresh + client_credentials) */ grants?: string[] | undefined; /** * supported scopes (advertised; also the escalation ceiling) */ scopes?: string[] | undefined; /** * override the endpoint URLs advertised in metadata */ endpoints?: Partial | undefined; /** * swap in Redis-backed stores */ stores?: Partial | undefined; security?: ServerSecurity | undefined; /** * make this an OpenID Provider (issue id_token on the `openid` scope) */ oidc?: OidcConfig | undefined; /** * enable RFC 7591 dynamic client registration (opt-in) */ registration?: RegistrationConfig | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type RegistrationConfig = { /** * bearer required to register (secure default; omit only with `open`) */ initialAccessToken?: string | undefined; /** * allow unauthenticated registration (NOT recommended) */ open?: boolean | undefined; /** * custom gate */ authorize?: ((req: ServerRequest) => boolean | Promise) | undefined; /** * defaults layered under each registration */ defaults?: Partial | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type OidcConfig = { /** * id_token JWS signer (signingKey + alg) */ idToken: IdTokenSignerConfig; /** * advertised in metadata as `userinfo_endpoint` */ userinfoEndpoint?: string | undefined; /** * advertised as `claims_supported` */ claimsSupported?: string[] | undefined; /** * advertised as `subject_types_supported` (default `['public']`) */ subjectTypes?: string[] | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type UserAuthn = { /** * resource-owner identifier → `sub` */ subject: string; /** * unix seconds of the authentication event (`auth_time`) */ authTime?: number | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type Endpoints = { authorization: string; token: string; introspection?: string | undefined; revocation?: string | undefined; par?: string | undefined; deviceAuthorization?: string | undefined; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type ServerStores = { authCode: ReturnType; refresh: ReturnType; par: ReturnType; device: ReturnType; }; /** * PKCE S256 is always required on the code grant (OAuth 2.1) — there is no * off-switch, so `requirePkce` is intentionally not a knob here. */ type ResolvedServerConfig = { issuer: string; endpoints: Endpoints; jwksUri?: string | undefined; grantTypes: string[]; scopes?: string[] | undefined; authMethods: string[]; dpopAlgs?: string[] | undefined; requirePar: boolean; }; type ServerResponse = { status: number; headers: Record; body: string; }; type RawRequest = { method?: string | undefined; /** * full URL or path (`?query` parsed when `query` is absent) */ url?: string | undefined; headers?: Record | undefined; /** * pre-parsed query, else derived from `url` */ query?: Record | undefined; /** * raw string or a framework-parsed object */ body?: string | Record | undefined; /** * mTLS client cert (RFC 8705) */ clientCertificate?: any | Record; }; /** * Wrap a single server handler as an Express `(req, res)` handler. * * @param {(raw: import('../request.js').RawRequest) => Promise} handler * @returns {(req: any, res: any) => Promise} */ declare function expressHandler(handler: (raw: RawRequest) => Promise): (req: any, res: any) => Promise; /** * The per-endpoint Express handlers for an authorization server — mount * them on your own routes (symmetric with `oauthLogin` on the RP side): * * const oauth2 = oauth2Handlers(server); * app.get('/authorize', oauth2.authorize); * app.post('/token', rateLimit, oauth2.token); // your own middleware, your own paths * * @param {ReturnType} server * @returns {{ metadata: Function, authorize: Function, token: Function, revoke: Function, introspect: Function, par: Function, deviceAuthorization: Function }} */ declare function oauth2Handlers(server: ReturnType): { metadata: Function; authorize: Function; token: Function; revoke: Function; introspect: Function; par: Function; deviceAuthorization: Function; }; /** * Register every authorization-server endpoint on an Express app — the * one-call form of {@link oauth2Handlers}, mounting each at the path * portion of its configured URL (so a custom `endpoints` override stays * consistent with the advertised metadata and the DPoP `htu`). The * discovery document is served from both well-known paths. * * @param {any} app an Express application / router * @param {ReturnType} server * @param {{ basePath?: string }} [options] */ declare function mountOAuth2Server(app: any, server: ReturnType, options?: { basePath?: string; }): void; /** * The path portion of each configured endpoint URL, so the adapter mounts * exactly where the metadata says the endpoint lives. * * @param {ReturnType} server * @returns {Record} */ declare function endpointPaths(server: ReturnType): Record; export { endpointPaths, expressHandler, mountOAuth2Server, oauth2Handlers };