import * as client from "openid-client"; export function normalizeIssuer(input: string): URL { const url = new URL(input); if (url.search || url.hash || url.username || url.password) { throw new Error("Invalid tessera OIDC issuer URL"); } const isLoopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "::1"; if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) { throw new Error("Tessera OIDC issuer must use HTTPS outside loopback development"); } url.pathname = url.pathname.replace(/\/+$/g, ""); return url; } export async function discoverTessera(env: Env): Promise { const issuer = normalizeIssuer(env.TESSERA_OIDC_ISSUER); const options = issuer.protocol === "http:" ? { execute: [client.allowInsecureRequests], } : undefined; return await client.discovery( issuer, env.TESSERA_OIDC_CLIENT_ID, env.TESSERA_OIDC_CLIENT_SECRET, client.ClientSecretBasic(env.TESSERA_OIDC_CLIENT_SECRET), options, ); } export function controlPlaneCallbackUrl(requestUrl: string): string { const url = new URL(requestUrl); url.pathname = "/api/v1/auth/oidc/callback"; url.search = ""; url.hash = ""; return url.toString(); } export function validateReturnTo(value: string | null, requestUrl: string): string { if (!value) return "/"; const requestOrigin = new URL(requestUrl).origin; if (value.startsWith("/") && !value.startsWith("//")) return value; const url = new URL(value); if (url.origin !== requestOrigin) throw new Error("Invalid return_to origin"); return `${url.pathname}${url.search}${url.hash}`; } export function profileFromClaims(claims: client.IDToken): { sub: string; email?: string | null; displayName?: string | null; } { if (!claims.sub) throw new Error("ID token is missing sub"); const email = typeof claims.email === "string" ? claims.email : null; const displayName = typeof claims.name === "string" ? claims.name : email; return { sub: claims.sub, email, displayName }; }