# pkce ## Purpose Implements RFC 7636 Proof Key for Code Exchange (PKCE) utilities for the OAuth 2.0 authorization code flow. PKCE prevents authorization-code interception attacks by binding the code request to a code verifier that only the original requester knows. All functions are pure and runtime-agnostic — the only platform API required is `globalThis.crypto.subtle` for SHA-256 hashing, which is available in every modern runtime (Node 15+, browser, Bun, Deno, edge workers). ## Features - `base64UrlEncode` — URL-safe base64 encoding without padding (RFC 4648 §5). - `base64UrlDecode` — URL-safe base64 decoding, tolerates missing padding. - `generateCodeVerifier` — 43-character random verifier (256 bits of entropy) via injected `generateRandom`. - `generateCodeChallenge` — async SHA-256 (`S256`) challenge with `plain` fallback. - `verifyCodeChallenge` — server-side PKCE verification. - `generatePkcePair` — convenience wrapper returning verifier + challenge + method. - `generateState` — random OAuth state parameter for CSRF protection. - `generateNonce` — random OIDC nonce. ## Usage: Basic Example ```ts import { generatePkcePair, generateState } from "@suaveplan/oauth-client/utils/pkce"; const random = (n: number) => globalThis.crypto.getRandomValues(new Uint8Array(n)); const { codeVerifier, codeChallenge, codeChallengeMethod } = await generatePkcePair(random); const state = generateState(random); // Store codeVerifier and state in session — they are needed after redirect. ``` ## Usage: Advanced Example ```ts import { generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, } from "@suaveplan/oauth-client/utils/pkce"; const random = (n: number) => globalThis.crypto.getRandomValues(new Uint8Array(n)); // Generate the pair manually to inspect each part. const verifier = generateCodeVerifier(random); const { challenge, method } = await generateCodeChallenge(verifier); // Server-side verification (e.g. in a BFF or test): const valid = await verifyCodeChallenge(verifier, challenge, method); // => true ``` ## API Reference ### `base64UrlEncode(bytes: Uint8Array): string` Encodes a byte array as base64url **without** `=` padding. Returns a URL-safe string safe for use in query parameters. ### `base64UrlDecode(input: string): Uint8Array` Decodes a base64url string (with or without `=` padding) to a `Uint8Array`. ### `generateCodeVerifier(generateRandom: (bytes: number) => Uint8Array): string` Returns a 43-character base64url-encoded code verifier backed by 32 random bytes (256 bits of entropy). ### `generateCodeChallenge(verifier: string): Promise<{ challenge: string; method: CodeChallengeMethod }>` Computes the PKCE code challenge. Uses `S256` (SHA-256) when `globalThis.crypto.subtle` is available, falling back to `plain` otherwise. ### `verifyCodeChallenge(verifier: string, challenge: string, method: CodeChallengeMethod): Promise` Returns `true` when `verifier` corresponds to `challenge` under `method`. Useful for testing or server-side BFF verification. ### `generatePkcePair(generateRandom: (bytes: number) => Uint8Array): Promise` Generates a complete `PkcePair` (verifier + challenge + method) in one call. ### `generateState(generateRandom: (bytes: number) => Uint8Array): string` Returns a 22-character base64url-encoded random OAuth state parameter (16 bytes, 128 bits of entropy). ### `generateNonce(generateRandom: (bytes: number) => Uint8Array): string` Returns a 22-character base64url-encoded random OIDC nonce (16 bytes). ## Implementation Notes The `base64UrlEncode` implementation manually iterates 3-byte groups rather than calling `btoa` to avoid `Buffer` or `atob` edge-cases in non-browser environments. The `base64UrlDecode` function uses `atob` after replacing URL-unsafe characters and restoring padding — `atob` is universally available in every targeted runtime. The SHA-256 digest path uses the `crypto.subtle` Web Cryptography API directly to remain dependency-free; no third-party hash library is imported.