fix: resolve type errors in OAuth and API utils

Fix null vs undefined in GitHub OAuth, use arctic's
createAuthorizationURLWithPKCE for OIDC, loosen api-utils type
signature to avoid SvelteKit route-specific generics, add @types/zxcvbn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-09 01:10:37 +01:00
committed by Fred Cabassut
co-authored by Claude Opus 4.6
parent 333bd4912d
commit 506e6b9c87
3 changed files with 25 additions and 41 deletions
+7 -3
View File
@@ -1,11 +1,15 @@
import { error, type RequestEvent } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
export function getUserId(event: RequestEvent): number {
type EventWithLocals = {
locals: App.Locals;
};
export function getUserId(event: EventWithLocals): number {
if (!event.locals.user) throw error(401, 'Unauthorized');
return event.locals.user.id;
}
export function requireAdmin(event: RequestEvent) {
export function requireAdmin(event: EventWithLocals) {
const user = event.locals.user;
if (!user || user.role !== 'admin') throw error(403, 'Forbidden');
return user;
+1 -1
View File
@@ -37,7 +37,7 @@ export async function validateGithubCallback(
if (emailsRes.ok) {
const emails = (await emailsRes.json()) as { email: string; primary: boolean; verified: boolean }[];
const primary = emails.find((e) => e.primary && e.verified);
email = primary?.email || emails.find((e) => e.verified)?.email || null;
email = primary?.email || emails.find((e) => e.verified)?.email || undefined;
}
}
+17 -37
View File
@@ -53,18 +53,15 @@ export async function createOidcAuthUrl(): Promise<{ url: URL; state: string; co
const state = arctic.generateState();
const codeVerifier = arctic.generateCodeVerifier();
const scopes = ['openid', 'profile', 'email'];
const url = new URL(oidcConfig.authorizationEndpoint);
url.searchParams.set('response_type', 'code');
url.searchParams.set('client_id', getOidcEnv().clientId);
url.searchParams.set('redirect_uri', `${getBaseUrl()}/api/auth/oauth/oidc/callback`);
url.searchParams.set('state', state);
url.searchParams.set('scope', 'openid profile email');
// PKCE
const codeChallenge = await arctic.generateCodeChallenge(codeVerifier);
url.searchParams.set('code_challenge', codeChallenge);
url.searchParams.set('code_challenge_method', 'S256');
const url = client.createAuthorizationURLWithPKCE(
oidcConfig.authorizationEndpoint,
state,
arctic.CodeChallengeMethod.S256,
codeVerifier,
scopes
);
return { url, state, codeVerifier };
}
@@ -76,34 +73,16 @@ export async function validateOidcCallback(
const client = await ensureOidcClient();
if (!client || !oidcConfig) return null;
const env = getOidcEnv();
const redirectURI = `${getBaseUrl()}/api/auth/oauth/oidc/callback`;
// Exchange code for tokens
const body = new URLSearchParams({
grant_type: 'authorization_code',
const tokens = await client.validateAuthorizationCode(
oidcConfig.tokenEndpoint,
code,
redirect_uri: redirectURI,
client_id: env.clientId,
client_secret: env.clientSecret,
code_verifier: codeVerifier
});
const tokenRes = await fetch(oidcConfig.tokenEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
});
if (!tokenRes.ok) return null;
const tokenData = (await tokenRes.json()) as {
access_token: string;
id_token?: string;
};
codeVerifier
);
// Try to decode ID token first
if (tokenData.id_token) {
const claims = arctic.decodeIdToken(tokenData.id_token) as {
const idToken = tokens.idToken();
if (idToken) {
const claims = arctic.decodeIdToken(idToken) as {
sub: string;
email?: string;
name?: string;
@@ -118,8 +97,9 @@ export async function validateOidcCallback(
}
// Fallback to userinfo endpoint
const accessToken = tokens.accessToken();
const userRes = await fetch(oidcConfig.userinfoEndpoint, {
headers: { Authorization: `Bearer ${tokenData.access_token}` }
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!userRes.ok) return null;