Files
headplane/app/routes/auth/oidc-start.ts
T
Aarnav Tale 3a3e5ca65e feat: pkce
2025-12-04 11:37:22 -05:00

85 lines
2.1 KiB
TypeScript

import * as oidc from 'openid-client';
import { data, redirect } from 'react-router';
import { HeadplaneConfig } from '~/server/config/config-schema';
import { createOidcStateCookie } from '~/utils/oidc-state';
import type { Route } from './+types/oidc-start';
export async function loader({ request, context }: Route.LoaderArgs) {
try {
await context.sessions.auth(request);
return redirect('/');
} catch {}
if (!context.oidcConnector?.isValid) {
throw data('OIDC is not enabled or misconfigured', { status: 501 });
}
const cookie = createOidcStateCookie(context.config);
const redirect_uri = getRedirectUri(context.config, request);
const nonce = oidc.randomNonce();
const verifier = oidc.randomPKCECodeVerifier();
const state = oidc.randomState();
const url = oidc.buildAuthorizationUrl(context.oidcConnector.client, {
...(context.oidcConnector.extraParams ?? {}),
scope: context.oidcConnector.scope,
redirect_uri,
state,
nonce,
...(context.oidcConnector.usePKCE
? {
code_challenge_method: 'S256',
code_challenge: await oidc.calculatePKCECodeChallenge(verifier),
}
: {}),
});
return redirect(url.href, {
status: 302,
headers: {
'Set-Cookie': await cookie.serialize({
state,
nonce,
verifier,
redirect_uri,
}),
},
});
}
function getRedirectUri(config: HeadplaneConfig, req: Request): string {
if (config.server.base_url != null) {
const url = new URL(`${__PREFIX__}/oidc/callback`, config.server.base_url);
return url.href;
}
if (config.oidc?.redirect_uri != null) {
const url = new URL(
`${__PREFIX__}/oidc/callback`,
config.oidc.redirect_uri,
);
return url.href;
}
const url = new URL(`${__PREFIX__}/oidc/callback`, req.url);
let host = req.headers.get('Host');
if (!host) {
host = req.headers.get('X-Forwarded-Host');
}
if (!host) {
throw data(
'Cannot determine redirect URI: no Host or X-Forwarded-Host header',
{
status: 500,
},
);
}
const proto = req.headers.get('X-Forwarded-Proto');
url.protocol = proto ?? 'http:';
url.host = host;
return url.href;
}