import { base64url } from 'jose'; function generateVerifier(length: number): string { const buffer = new Uint8Array(length); crypto.getRandomValues(buffer); return base64url.encode(buffer); } async function generateChallenge(verifier: string, method: 'S256' | 'plain') { if (method === 'plain') return verifier; const encoder = new TextEncoder(); const data = encoder.encode(verifier); const hash = await crypto.subtle.digest('SHA-256', data); return base64url.encode(new Uint8Array(hash)); } export async function generatePKCE(length: number = 64) { if (length < 43 || length > 128) { throw new Error('Code verifier length must be between 43 and 128 characters'); } const verifier = generateVerifier(length); const challenge = await generateChallenge(verifier, 'S256'); return { verifier, challenge, method: 'S256' }; } export async function validatePKCE( verifier: string, challenge: string, method: 'S256' | 'plain' = 'S256' ) { const generatedChallenge = await generateChallenge(verifier, method); // timing safe equals? return generatedChallenge === challenge; }