import type { APIContext } from "astro"; import { getOptions } from "../runtime.js"; import { generateCodeVerifier, codeChallengeS256, generateState, generateNonce, } from "../lib/pkce.js"; import { serializeCookie } from "../lib/cookies.js"; async function discover(issuer: string) { const res = await fetch( new URL("/.well-known/openid-configuration", issuer).toString(), ); if (!res.ok) throw new Error(`OIDC discovery failed: ${res.status}`); return (await res.json()) as { authorization_endpoint: string; }; } function inferRedirectUri( options: ReturnType, reqUrl: URL, ): string { if ("absolute" in options.redirectUri) return options.redirectUri.absolute; const u = new URL(options.routes.callback, reqUrl); return u.toString(); } export async function GET(ctx: APIContext) { const options = getOptions(); const { url } = ctx; const verifier = generateCodeVerifier(); const challenge = await codeChallengeS256(verifier); const state = generateState(); const nonce = generateNonce(); const returnTo = url.searchParams.get("return_to") || undefined; const initPayload = JSON.stringify({ state, nonce, verifier, returnTo, }); const initCookieName = `${options.cookie.name}_init`; const cookie = serializeCookie(initCookieName, initPayload, { path: options.cookie.path, domain: options.cookie.domain, sameSite: "Lax", secure: options.cookie.secure, httpOnly: true, maxAge: 5 * 60, // 5 minutes }); const disco = await discover(options.issuer); const redirectUri = inferRedirectUri(options, url); const authorize = new URL(disco.authorization_endpoint); authorize.searchParams.set("client_id", options.clientId); authorize.searchParams.set("redirect_uri", redirectUri); authorize.searchParams.set("response_type", "code"); authorize.searchParams.set("scope", options.scopes); authorize.searchParams.set("code_challenge", challenge); authorize.searchParams.set("code_challenge_method", "S256"); authorize.searchParams.set("state", state); authorize.searchParams.set("nonce", nonce); return new Response(null, { status: 302, headers: { Location: authorize.toString(), "Set-Cookie": cookie, }, }); }