From 506e6b9c87601d1829f4cbd1d5494991287a64c2 Mon Sep 17 00:00:00 2001 From: Frederic Cabassut Date: Mon, 9 Mar 2026 00:38:10 +0100 Subject: [PATCH] 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 --- src/lib/server/api-utils.ts | 10 +++++-- src/lib/server/oauth/github.ts | 2 +- src/lib/server/oauth/oidc.ts | 54 +++++++++++----------------------- 3 files changed, 25 insertions(+), 41 deletions(-) diff --git a/src/lib/server/api-utils.ts b/src/lib/server/api-utils.ts index 6950d5f..3b05fd9 100644 --- a/src/lib/server/api-utils.ts +++ b/src/lib/server/api-utils.ts @@ -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; diff --git a/src/lib/server/oauth/github.ts b/src/lib/server/oauth/github.ts index a4a4be1..93b6e00 100644 --- a/src/lib/server/oauth/github.ts +++ b/src/lib/server/oauth/github.ts @@ -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; } } diff --git a/src/lib/server/oauth/oidc.ts b/src/lib/server/oauth/oidc.ts index 76f67e3..17fda06 100644 --- a/src/lib/server/oauth/oidc.ts +++ b/src/lib/server/oauth/oidc.ts @@ -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;