Files
astro-oidc-rp/src/routes/login.ts

76 lines
2.2 KiB
TypeScript

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<typeof getOptions>,
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,
},
});
}