/** OAuth 2.0 provider configuration. The secret value is stored with the app. */ export type OAuthConfiguration = { authorizationEndpoint: string; tokenEndpoint: string; clientId: string; clientSecretEnv: string; scope: string; authorizationParams?: Record; accessTokenExpireMs?: number; }; /** @internal */ export function validateOAuthConfiguration(config: OAuthConfiguration): void { if (!config.clientId || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(config.clientSecretEnv)) throw new Error("OAuth connections require a client ID and clientSecretEnv secret name."); for (const endpoint of [config.authorizationEndpoint, config.tokenEndpoint]) { const url = new URL(endpoint); if (url.protocol !== "https:" || url.username || url.password || url.hash) throw new Error("OAuth endpoints must be HTTPS URLs without credentials or fragments."); } if ( config.accessTokenExpireMs !== undefined && (!Number.isFinite(config.accessTokenExpireMs) || config.accessTokenExpireMs <= 0) ) throw new Error("OAuth token expiry must be positive."); if ( Object.keys(config.authorizationParams ?? {}).some((key) => [ "state", "redirect_uri", "client_id", "response_type", "code_challenge", "code_challenge_method", "scope", ].includes(key), ) ) throw new Error("OAuth authorization parameters cannot override protocol fields."); }