import { redirect, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types.js'; import * as oauthService from '$lib/server/services/oauthService.js'; import * as userService from '$lib/server/services/userService.js'; import * as authService from '$lib/server/services/authService.js'; const COOKIE_BASE = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const, path: '/' }; export const GET: RequestHandler = async ({ url, cookies }) => { try { // Check for error response from the provider const oauthError = url.searchParams.get('error'); if (oauthError) { const description = url.searchParams.get('error_description') || oauthError; throw new Error(`OAuth provider returned an error: ${description}`); } // Ensure we have an authorization code const code = url.searchParams.get('code'); if (!code) { throw new Error('No authorization code received from OAuth provider'); } // Retrieve the code_verifier and state from cookies const codeVerifier = cookies.get('oauth_code_verifier'); if (!codeVerifier) { throw new Error('OAuth session expired. Please try logging in again.'); } const expectedState = cookies.get('oauth_state'); if (!expectedState) { throw new Error('OAuth session expired. Please try logging in again.'); } // Validate the state parameter matches to prevent CSRF const returnedState = url.searchParams.get('state'); if (returnedState !== expectedState) { throw new Error('OAuth state mismatch. Possible CSRF attack. Please try logging in again.'); } // Clear the OAuth cookies cookies.delete('oauth_code_verifier', { path: '/' }); cookies.delete('oauth_state', { path: '/' }); // Exchange the authorization code for tokens and get user info const userInfo = await oauthService.handleCallback(url, codeVerifier, expectedState); // Find or create local user from OAuth info const user = await userService.findOrCreateByOAuth({ email: userInfo.email, displayName: userInfo.name || userInfo.preferred_username || userInfo.email.split('@')[0], avatarUrl: userInfo.picture, groups: userInfo.groups ? [...userInfo.groups] : undefined }); // Issue local JWT tokens (same as local auth flow) const accessToken = authService.signAccessToken({ userId: user.id, email: user.email, role: user.role }); const refreshToken = authService.generateRefreshToken(); await authService.saveRefreshToken(user.id, refreshToken); // Set session cookies cookies.set('access_token', accessToken, { ...COOKIE_BASE, maxAge: 900 // 15 minutes }); cookies.set('refresh_token', refreshToken, { ...COOKIE_BASE, maxAge: 604800 // 7 days }); cookies.set('refresh_user_id', user.id, { ...COOKIE_BASE, maxAge: 604800 // 7 days }); throw redirect(302, '/'); } catch (err) { // Re-throw redirects if (err && typeof err === 'object' && 'status' in err && (err as { status: number }).status === 302) { throw err; } const message = err instanceof Error ? err.message : 'OAuth authentication failed'; throw error(500, message); } };