headplane/app/routes/auth/oidc-callback.ts

70 lines
2.1 KiB
TypeScript
Raw Normal View History

2025-03-22 01:36:27 -04:00
import { type LoaderFunctionArgs, Session, redirect } from 'react-router';
import type { LoadContext } from '~/server';
import type { AuthSession, OidcFlowSession } from '~/server/web/sessions';
2025-02-19 18:09:42 -05:00
import { finishAuthFlow, formatError } from '~/utils/oidc';
import { send } from '~/utils/res';
2025-03-22 01:36:27 -04:00
export async function loader({
request,
context,
}: LoaderFunctionArgs<LoadContext>) {
if (!context.oidc) {
throw new Error('OIDC is not enabled');
}
2024-12-08 13:27:51 -05:00
// Check if we have 0 query parameters
const url = new URL(request.url);
if (url.searchParams.toString().length === 0) {
2025-03-22 01:36:27 -04:00
return redirect('/login');
}
2025-03-22 01:36:27 -04:00
const session = await context.sessions.getOrCreate<OidcFlowSession>(request);
if (session.get('state') !== 'flow') {
return redirect('/login'); // Haven't started an OIDC flow
}
2025-03-22 01:36:27 -04:00
const payload = session.get('oidc')!;
const { code_verifier, state, nonce, redirect_uri } = payload;
if (!code_verifier || !state || !nonce || !redirect_uri) {
return send({ error: 'Missing OIDC state' }, { status: 400 });
}
// Reconstruct the redirect URI using the query parameters
// and the one we saved in the session
2025-03-22 01:36:27 -04:00
const flowRedirectUri = new URL(redirect_uri);
flowRedirectUri.search = url.search;
const flowOptions = {
redirect_uri: flowRedirectUri.toString(),
2025-03-22 01:36:27 -04:00
code_verifier,
state,
nonce: nonce === '<none>' ? undefined : nonce,
2025-02-13 12:35:12 -05:00
};
2024-12-08 13:27:51 -05:00
try {
2025-03-22 01:36:27 -04:00
const user = await finishAuthFlow(context.oidc, flowOptions);
session.unset('oidc');
const userSession = session as Session<AuthSession>;
2024-12-08 13:27:51 -05:00
// TODO: This is breaking, to stop the "over-generation" of API
// keys because they are currently non-deletable in the headscale
// database. Look at this in the future once we have a solution
// or we have permissioned API keys.
2025-03-22 01:36:27 -04:00
userSession.set('user', user);
userSession.set('api_key', context.config.oidc?.headscale_api_key!);
userSession.set('state', 'auth');
return redirect('/machines', {
headers: {
2025-03-22 01:36:27 -04:00
'Set-Cookie': await context.sessions.commit(userSession),
},
});
2024-12-08 13:27:51 -05:00
} catch (error) {
2025-02-13 12:35:12 -05:00
return new Response(JSON.stringify(formatError(error)), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
2024-12-08 13:27:51 -05:00
}
}