/** RFC 7636:生成 code_verifier 与 S256 code_challenge(与 Go VerifyPKCES256 一致)。 */ function base64Url(buf: ArrayBuffer): string { const bytes = new Uint8Array(buf); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]!); } return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } export async function createPKCEPair(): Promise<{ verifier: string; challenge: string }> { const arr = new Uint8Array(32); crypto.getRandomValues(arr); const verifier = base64Url(arr.buffer); const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)); const challenge = base64Url(hash); return { verifier, challenge }; }