export function normalizeBaseUrl(url: string) { return url.trim().replace(/\/+$/, ''); } export function resolveOAuthApiBase(settings: Record, fallback = 'http://localhost:3000') { const publicApi = settings.PUBLIC_API_URL?.trim(); if (publicApi) { return normalizeBaseUrl(publicApi); } const domain = settings.PROJECT_DOMAIN?.trim(); if (domain) { if (domain.startsWith('http://') || domain.startsWith('https://')) { return normalizeBaseUrl(domain); } return `https://${domain.replace(/^\/+/, '')}`; } return normalizeBaseUrl(fallback); } export function resolveFrontendBase(settings: Record, fallback = 'http://localhost:3002') { const publicFrontend = settings.PUBLIC_FRONTEND_URL?.trim(); if (publicFrontend) { return normalizeBaseUrl(publicFrontend); } const domain = settings.PROJECT_DOMAIN?.trim(); if (domain) { if (domain.startsWith('http://') || domain.startsWith('https://')) { return normalizeBaseUrl(domain); } return `https://${domain.replace(/^\/+/, '')}`; } return normalizeBaseUrl(fallback); } export interface OAuthEndpoints { issuer: string; authorizationEndpoint: string; tokenEndpoint: string; userInfoEndpoint: string; openIdConfigurationUrl: string; jwksUrl: string; webIdentityUrl: string; fedcmConfigUrl: string; fedcmAccountsUrl: string; fedcmIdAssertionUrl: string; } export function buildOAuthEndpoints(apiBase: string): OAuthEndpoints { const base = normalizeBaseUrl(apiBase); return { issuer: base, authorizationEndpoint: `${base}/oauth/authorize`, tokenEndpoint: `${base}/oauth/token`, userInfoEndpoint: `${base}/oauth/userinfo`, openIdConfigurationUrl: `${base}/.well-known/openid-configuration`, jwksUrl: `${base}/.well-known/jwks.json`, webIdentityUrl: `${base}/.well-known/web-identity`, fedcmConfigUrl: `${base}/fedcm/config.json`, fedcmAccountsUrl: `${base}/fedcm/accounts`, fedcmIdAssertionUrl: `${base}/fedcm/id_assertion` }; } export function buildAuthorizeUrl( apiBase: string, params: { clientId: string; redirectUri: string; scope: string; state?: string; codeChallenge?: string; codeChallengeMethod?: string } ) { const url = new URL(`${normalizeBaseUrl(apiBase)}/oauth/authorize`); url.searchParams.set('client_id', params.clientId); url.searchParams.set('redirect_uri', params.redirectUri); url.searchParams.set('response_type', 'code'); url.searchParams.set('scope', params.scope); if (params.state) url.searchParams.set('state', params.state); if (params.codeChallenge) url.searchParams.set('code_challenge', params.codeChallenge); if (params.codeChallengeMethod) url.searchParams.set('code_challenge_method', params.codeChallengeMethod); return url.toString(); }