import { randomBytes, createHash } from "node:crypto"; function base64url(input: Buffer): string { return input .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); } export function generateCodeVerifier(): string { return base64url(randomBytes(48)); } export function generateCodeChallenge(verifier: string): string { return base64url(createHash("sha256").update(verifier).digest()); } export function generateState(): string { return base64url(randomBytes(16)); } interface PendingAuth { platform: string; codeVerifier: string; redirectUri: string; requestedScope?: string; expiresAt: number; } const PENDING_TTL_MS = 10 * 60 * 1000; const pendingAuths = new Map(); export function storePendingAuth(state: string, entry: Omit): void { for (const [key, value] of pendingAuths) { if (value.expiresAt < Date.now()) pendingAuths.delete(key); } pendingAuths.set(state, { ...entry, expiresAt: Date.now() + PENDING_TTL_MS }); } export function consumePendingAuth(state: string): PendingAuth | null { const entry = pendingAuths.get(state); if (!entry) return null; pendingAuths.delete(state); if (entry.expiresAt < Date.now()) return null; return entry; }