diff --git a/frontend/src/helpers/pkce.ts b/frontend/src/helpers/pkce.ts new file mode 100644 index 000000000..e9bc0df18 --- /dev/null +++ b/frontend/src/helpers/pkce.ts @@ -0,0 +1,30 @@ +/** + * Generate a cryptographically random code_verifier (43-128 chars, RFC 7636 Section 4.1). + * Uses unreserved characters: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" + */ +export function generateCodeVerifier(): string { + const array = new Uint8Array(32) + crypto.getRandomValues(array) + return base64UrlEncode(array) +} + +/** + * Compute code_challenge = BASE64URL(SHA256(code_verifier)) (RFC 7636 Section 4.2). + */ +export async function generateCodeChallenge(verifier: string): Promise { + const encoder = new TextEncoder() + const data = encoder.encode(verifier) + const digest = await crypto.subtle.digest('SHA-256', data) + return base64UrlEncode(new Uint8Array(digest)) +} + +function base64UrlEncode(bytes: Uint8Array): string { + let binary = '' + for (const byte of bytes) { + binary += String.fromCharCode(byte) + } + return btoa(binary) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+$/, '') +}