// Pure OIDC-login state helpers (no imports) so they're unit-testable under the // tsx + node:test harness. The SDK-driven flow lives in oidcLoginUtil.ts. /** * Dynamic-registration cache. MAS issues a `client_id` per dynamic registration; * caching by issuer (scoped to the current redirectUri) avoids re-registering on * every login. Invalidated when the redirectUri changes or the provider later * rejects the cached id (`invalid_client`). */ const REG_CACHE_KEY = 'cinny_oidc_dynamic_clients'; type RegEntry = { clientId: string; redirectUri: string }; type RegCache = Record; const readRegCache = (): RegCache => { try { const raw = localStorage.getItem(REG_CACHE_KEY); return raw ? (JSON.parse(raw) as RegCache) : {}; } catch { return {}; } }; const writeRegCache = (cache: RegCache): void => { localStorage.setItem(REG_CACHE_KEY, JSON.stringify(cache)); }; export const getCachedClientId = (issuer: string, redirectUri: string): string | undefined => { const entry = readRegCache()[issuer]; return entry && entry.redirectUri === redirectUri ? entry.clientId : undefined; }; export const cacheClientId = (issuer: string, clientId: string, redirectUri: string): void => { const cache = readRegCache(); cache[issuer] = { clientId, redirectUri }; writeRegCache(cache); }; export const invalidateCachedClient = (issuer: string): void => { const cache = readRegCache(); if (cache[issuer]) { delete cache[issuer]; writeRegCache(cache); } }; /** Parsed shape of the provider's redirect back to our callback URL. */ export type OidcCallbackParams = | { kind: 'success'; code: string; state: string } | { kind: 'error'; error: string; errorDescription?: string } | { kind: 'invalid' }; /** Pure: classify the callback query string into success / error / invalid. */ export const parseOidcCallbackParams = (search: string): OidcCallbackParams => { const params = new URLSearchParams(search); const error = params.get('error'); if (error) { return { kind: 'error', error, errorDescription: params.get('error_description') ?? undefined }; } const code = params.get('code'); const state = params.get('state'); if (code && state) return { kind: 'success', code, state }; return { kind: 'invalid' }; };