/** * @module * * Implements the OAuth 2.0 Authorization Code grant type, with optional PKCE support * and OpenID Connect extensions. * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 * @see https://datatracker.ietf.org/doc/html/rfc7636 */ import { OAuth2Error } from "../errors.js"; import { TokenTypeValidationResponse } from "../token_types/types.js"; import type { OAuth2Client } from "../types.js"; import { type OAuth2AccessTokenResult, OAuth2Flow, type OAuth2FlowOptions, type OAuth2FlowTokenResponse, OAuth2GetClientFunction, type OAuth2GrantModel, OAuth2RefreshTokenGrantContext, OAuth2RefreshTokenRequest } from "./flow.js"; /** * Represents an authenticated end-user associated with an authorization code request. * Extend via declaration merging to add application-specific user properties. */ export interface AuthorizationCodeUser { [key: string]: unknown; } /** * Represents additional request data submitted by the user at the authorization endpoint * (e.g. login form fields, consent selections). * Extend via declaration merging or the `AuthReqData` type parameter to add typed fields. */ export interface AuthorizationCodeReqData { [key: string]: unknown; } /** * Handles the Authorization Code grant type. * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 */ export interface AuthorizationCodeGrant { /** The grant type identifier. */ readonly grantType: "authorization_code"; } /** * Validation context for authorization code grant, * which can be used by the model's generateAccessToken() method * to generate tokens with appropriate lifetimes, etc. */ export interface AuthorizationCodeGrantContext { /** The authenticated client exchanging the authorization code. */ client: OAuth2Client; /** The grant type identifier. Always `"authorization_code"`. */ grantType: "authorization_code"; /** The token type prefix (e.g. `"Bearer"`, `"DPoP"`). */ tokenType: string; /** The access token lifetime in seconds. */ accessTokenLifetime: number; /** The authorization code being exchanged for tokens. */ code: string; /** The origin of the request, used for validation and security purposes. */ origin: string; /** The result of the token type validation. */ tokenTypeValidation: TokenTypeValidationResponse; /** The PKCE code verifier, if PKCE was used in the authorization request. */ codeVerifier?: string; /** The redirect URI presented at the token endpoint, if provided. */ redirectUri?: string; } /** * Raw token request parameters for authorization code grant. */ export interface AuthorizationCodeTokenRequest { /** The client identifier. */ clientId: string; /** The grant type value. Always `"authorization_code"`. */ grantType: "authorization_code"; /** The authorization code received from the authorization endpoint. */ code: string; /** The origin of the request, used for validation and security purposes. */ origin: string; /** The result of the token type validation. */ tokenTypeValidation: TokenTypeValidationResponse; /** The client authentication method used for this request, if any. */ clientAuthMethod?: string | undefined; /** The client authentication data extracted from the request, if any. */ clientAuthData?: Partial | undefined; /** The PKCE code verifier, if PKCE was used in the authorization request. */ codeVerifier?: string; /** The client secret, if the client is confidential. */ clientSecret?: string; /** * The redirect URI presented at the token endpoint. * * Per RFC 6749 §4.1.3, if a `redirect_uri` was included in the authorization * request, the same value MUST be provided here and your `getClient()` * implementation MUST verify that it matches the URI stored with the * authorization code. Failing to do so allows authorization code injection * across redirect URIs. * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 */ redirectUri?: string; } /** * Validation context for authorization code authentication (authorization endpoint request), * which can be used by the model's generateAuthorizationCode() method * to generate an authorization code with appropriate scope, etc. */ export interface AuthorizationCodeEndpointContext { /** The client requesting authorization. */ client: OAuth2Client; /** The response type. Always `"code"` for the authorization code grant. */ responseType: "code"; /** The redirect URI to send the authorization code to after authorization. */ redirectUri: string; /** The validated scopes requested by the client. */ scope: string[]; /** The origin of the request, used for validation and security purposes. */ origin: string; /** An opaque value used to maintain state between the request and callback. */ state?: string; /** PKCE code challenge (if provided). */ codeChallenge?: string; /** PKCE code challenge method (`plain` | `S256`). */ codeChallengeMethod?: "plain" | "S256"; } /** * Raw authentication request parameters for authorization code grant. */ export interface AuthorizationCodeEndpointRequest { /** The client identifier from the authorization request query string. */ clientId: string; /** The response type. Always `"code"` for the authorization code grant. */ responseType: "code"; /** The redirect URI from the authorization request query string. */ redirectUri: string; /** The origin of the request, used for validation and security purposes. */ origin: string; /** The requested scopes, if provided. */ scope?: string[]; /** The state value from the authorization request, if provided. */ state?: string; /** PKCE code challenge (if provided). */ codeChallenge?: string; /** PKCE code challenge method (`plain` | `S256`). */ codeChallengeMethod?: "plain" | "S256"; } /** * Result returned by `processAuthorization()` when further interaction is needed * before an authorization code can be issued (e.g. multi-step consent flows). * * @template C - The authorization endpoint context type. */ export interface AuthorizationCodeEndpointContinueResponse { /** The authorization endpoint context at the time of the continue response. */ context: C; /** The authenticated user. */ user: AuthorizationCodeUser; /** The scopes granted to the user. */ scope: string[]; /** An optional message describing the next step required. */ message?: string; /** Discriminator ensuring this is not an error response. */ error?: never; /** Additional application-specific fields. */ [key: string]: unknown; } /** * Result returned by `processAuthorization()` when an authorization code has been successfully generated. * * @template C - The authorization endpoint context type. */ export interface AuthorizationCodeEndpointCodeResponse { /** The authorization endpoint context at the time the code was issued. */ context: C; /** The authenticated user. */ user: AuthorizationCodeUser; /** The generated authorization code. */ code: string; /** Discriminator ensuring this is not an error response. */ error?: never; /** Additional application-specific fields. */ [key: string]: unknown; } /** * The union of all possible outcomes from `handleAuthorizationEndpoint()`. * * - `GET / initiated`: Authorization request was validated; render a login/consent UI. * - `POST / code`: An authorization code was issued; redirect the user to `redirectUri`. * - `POST / continue`: Further user interaction is required before a code can be issued. * - `POST / unauthenticated`: User authentication failed; re-render the login UI. * - `error`: A protocol error occurred. * * @template C - The authorization endpoint context type. */ export type AuthorizationCodeEndpointResponse = { method: "GET"; type: "initiated"; context: C; } | { method: "POST"; type: "code"; authorizationCodeResponse: AuthorizationCodeEndpointCodeResponse; } | { method: "POST"; type: "continue"; continueResponse: AuthorizationCodeEndpointContinueResponse; } | { method: "POST"; type: "unauthenticated"; context: C; message?: string; } | { type: "error"; error: OAuth2Error; /** Whether the error can be communicated by redirecting the user to `redirectUri`. */ redirectable: boolean; client?: OAuth2Client; redirectUri?: string; state?: string; }; /** * The result of `initiateAuthorization()` - the first step of the authorization code flow. * * On success, contains the validated {@link AuthorizationCodeEndpointContext} to pass to * `processAuthorization()`. * * @template C - The authorization endpoint context type. */ export type AuthorizationCodeInitiationResponse = { success: true; context: C; } | { success: false; error: OAuth2Error; redirectable: false; }; /** * The result of `processAuthorization()` - the second step of the authorization code flow, * after the user has submitted credentials or consent. * * @template C - The authorization endpoint context type. */ export type AuthorizationCodeProcessResponse = { type: "continue"; continueResponse: AuthorizationCodeEndpointContinueResponse; } | { type: "code"; authorizationCodeResponse: AuthorizationCodeEndpointCodeResponse; } | { type: "unauthenticated"; context: C; message?: string; } | { type: "error"; error: OAuth2Error; /** Whether the error can be communicated by redirecting the user to `redirectUri`. */ redirectable: boolean; client?: OAuth2Client; redirectUri?: string; state?: string; }; /** * The access token result shape for the authorization code grant. * Extends the base result with optional refresh token, scope, and ID token fields. */ export interface AuthorizationCodeAccessTokenResult extends OAuth2AccessTokenResult { /** * Necessary to return the scope to the client. */ scope?: string[]; refreshToken?: string; /** * For OpenID Connect, an ID token can also be returned from the token endpoint when exchanging the authorization code for tokens, and it should be included in the access token result so that it can be returned to the client in the token response. * @see https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint */ idToken?: string; } /** * The result returned by a {@link GetUserForAuthenticationFunction}. * * - `authenticated`: The user was successfully identified; contains the user object. * - `unauthenticated`: Authentication failed; contains an optional message for the UI. */ export type GetUserForAuthenticationResult = { type: "authenticated"; user: AuthorizationCodeUser; } | { type: "unauthenticated"; message?: string; }; /** * A function that authenticates an end-user at the authorization endpoint. * * Called during `processAuthorization()` after the client has been validated. * Should verify the user's submitted credentials against the application's user store. * * @template TContext - The authorization endpoint context type. * @template AuthReqData - The shape of the user-submitted request data (e.g. login form fields). * * @param context - The validated authorization endpoint context. * @param reqData - The user-submitted data from the authorization endpoint request. * @param request - The original HTTP request. * @returns The authentication result, or `undefined` to indicate unauthenticated. */ export interface GetUserForAuthenticationFunction { (context: TContext, reqData: AuthReqData, request: Request): Promise | GetUserForAuthenticationResult | undefined; } /** * The result returned by a {@link GenerateAuthorizationCodeFunction}. * * - `code`: An authorization code was generated successfully. * - `continue`: Further interaction is needed before a code can be issued. * - `deny`: The request was explicitly denied (e.g. user declined consent). */ export type GenerateAuthorizationCodeResult = { type: "code"; code: string; } | { type: "continue"; message?: string; } | { type: "deny"; message?: string; }; /** * A function that generates an authorization code for an authenticated user. * * Called during `processAuthorization()` after the user has been successfully authenticated. * Should persist the code along with the associated context (client, scope, PKCE params, etc.) * for later retrieval at the token endpoint. * * @template TContext - The authorization endpoint context type. * * @param context - The validated authorization endpoint context. * @param user - The authenticated end-user. * @returns The generation result, or `undefined` on failure. */ export interface GenerateAuthorizationCodeFunction { (context: TContext, user: AuthorizationCodeUser): Promise | GenerateAuthorizationCodeResult | undefined; } /** * Model interface that must be implemented by the consuming application * to provide persistence for clients and tokens related to the authorization code grant. */ export interface AuthorizationCodeModel extends OAuth2GrantModel { /** * Retrieve and validate the client for an authorization code or refresh token request. * * When `tokenRequest.grantType === "authorization_code"`, implementations MUST: * 1. Verify the `code` is valid and has not already been used (one-time use). * 2. Verify the `clientId` matches the client that requested the code. * 3. If `redirectUri` is present, verify it is identical to the `redirect_uri` * used in the original authorization request (RFC 6749 §4.1.3). Omitting * this check enables authorization code injection attacks. * 4. If `codeVerifier` is present, verify it against the stored `code_challenge` * using the stored `code_challenge_method` (RFC 7636 §4.6). * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.6 */ getClient: OAuth2GetClientFunction; /** * Retrieve and validate the client for an authorization endpoint request. * Should verify the `clientId` and `redirectUri` are registered and permitted. */ getClientForAuthentication: OAuth2GetClientFunction; /** * Authenticate the end-user from the submitted request data. * See {@link GetUserForAuthenticationFunction} for the full contract. */ getUserForAuthentication: GetUserForAuthenticationFunction; /** * Generate (or deny) an authorization code for the authenticated user. * Should persist the code and associated context for later token exchange. * See {@link GenerateAuthorizationCodeFunction} for the full contract. */ generateAuthorizationCode: GenerateAuthorizationCodeFunction; } /** * Options for configuring the authorization code grant flow. */ export interface AuthorizationCodeFlowOptions extends OAuth2FlowOptions { /** The model implementation providing client lookup, user authentication, and token generation. */ model: AuthorizationCodeModel; /** The URL of the authorization endpoint. Defaults to `"/authorize"`. */ authorizationEndpoint?: string; } /** * Abstract base class for the Authorization Code flow. * * Provides the full request handling pipeline for both the authorization endpoint * (`initiateAuthorization`, `processAuthorization`, `handleAuthorizationEndpoint`) * and the token endpoint (`token`). Subclasses must implement `toOpenAPISecurityScheme()`. * * @template AuthReqData - The shape of user-submitted data at the authorization endpoint. * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 */ export declare abstract class AbstractAuthorizationCodeFlow extends OAuth2Flow implements AuthorizationCodeGrant { readonly grantType: "authorization_code"; protected readonly model: AuthorizationCodeModel; protected authorizationEndpoint: string; constructor(options: AuthorizationCodeFlowOptions); /** * Sets the URL of the authorization endpoint. * * @param url - The authorization endpoint URL (absolute or relative). */ setAuthorizationEndpoint(url: string): this; /** * Returns the URL of the authorization endpoint. */ getAuthorizationEndpoint(): string; protected getAuthorizationCodeEndpointContext(request: Request): Promise; /** * Validates an incoming authorization endpoint `GET` request and returns the * authorization context. Does not interact with the user yet. * * Call this as the first step when the user arrives at the authorization endpoint. * On success, store the returned context and render a login/consent UI. * * @param request - The incoming `GET` request to the authorization endpoint. * @returns The initiation response with the validated context, or a non-redirectable error. */ initiateAuthorization(request: Request): Promise; /** * Processes the user's submitted credentials or consent at the authorization endpoint. * * Call this after the user has submitted the login/consent form. Authenticates the user * via `model.getUserForAuthentication()` and, if successful, generates an authorization * code via `model.generateAuthorizationCode()`. * * @param request - The incoming HTTP request (typically `POST`) to the authorization endpoint. * @param reqData - The user-submitted data (e.g. login form fields, consent selections). * @returns The process response - a code, a continue prompt, an unauthenticated result, or an error. */ processAuthorization(request: Request, reqData: AuthReqData): Promise; /** * Unified handler for `GET` and `POST` requests to the authorization endpoint. * * Delegates `GET` to `initiateAuthorization()` and `POST` to `processAuthorization()`. * Returns a discriminated union suitable for rendering a response in any HTTP framework. * * @param request - The incoming HTTP request to the authorization endpoint. * @param reqData - The user-submitted data (used for `POST` requests only). * @returns The endpoint response - a discriminated union of all possible outcomes. */ handleAuthorizationEndpoint(request: Request, reqData: AuthReqData): Promise; /** * Validates the token endpoint request (both `authorization_code` and `refresh_token` grant types) * and returns the resolved grant context without yet generating tokens. * * Useful when you need to inspect the context before deciding how to generate tokens. * Most callers should use `token()` directly instead. * * @param request - The incoming token endpoint HTTP request. * @returns The grant context on success, or a failure with an error. */ initiateToken(request: Request): Promise<{ success: true; context: AuthorizationCodeGrantContext | OAuth2RefreshTokenGrantContext; } | { success: false; error: OAuth2Error; }>; /** * Handles a token request for the authorization code grant type (or refresh token grant). * Validates the authorization code and generates an access token if valid. * Returns an appropriate error response if validation fails. * * @param request - The incoming token endpoint HTTP request. * @returns A token response with the generated access token, or a failure with an error. */ token(request: Request): Promise; } /** * Concrete Authorization Code flow implementation. * * Extends {@link AbstractAuthorizationCodeFlow} with an OpenAPI security scheme * definition for the `authorizationCode` OAuth 2.0 flow type. * * @template AuthReqData - The shape of user-submitted data at the authorization endpoint. * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 */ export declare class AuthorizationCodeFlow extends AbstractAuthorizationCodeFlow { /** * Returns the OpenAPI security scheme definition for this flow. * Uses the `oauth2` scheme type with an `authorizationCode` flow. * * @returns An object keyed by the security scheme name with the scheme definition. */ toOpenAPISecurityScheme(): Record; tokenUrl: string; }; }; }>; } //# sourceMappingURL=authorization_code.d.ts.map