export default defineEventHandler(async (event) => { const query = getQuery(event) const { code, state, error } = query console.log('[KEYCLOAK] Callback received:', { code: !!code, state, error }) if (error) { console.error('[KEYCLOAK] OAuth error:', error) throw createError({ statusCode: 400, statusMessage: `Authentication failed: ${error}` }) } if (!code) { console.error('[KEYCLOAK] No authorization code received') throw createError({ statusCode: 400, statusMessage: 'No authorization code received' }) } try { // Validate environment variables const clientSecret = process.env.KEYCLOAK_CLIENT_SECRET if (!clientSecret) { console.error('[KEYCLOAK] KEYCLOAK_CLIENT_SECRET not configured') throw createError({ statusCode: 500, statusMessage: 'Authentication service misconfigured' }) } // Exchange authorization code for tokens const tokenResponse = await $fetch('https://auth.portnimara.dev/realms/client-portal/protocol/openid-connect/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: 'client-portal', client_secret: clientSecret, code: code as string, redirect_uri: 'https://client.portnimara.dev/api/auth/keycloak/callback' }).toString() }) as any console.log('[KEYCLOAK] Token exchange successful:', { hasAccessToken: !!tokenResponse.access_token, hasRefreshToken: !!tokenResponse.refresh_token, expiresIn: tokenResponse.expires_in }) // Get user info const userInfo = await $fetch('https://auth.portnimara.dev/realms/client-portal/protocol/openid-connect/userinfo', { headers: { 'Authorization': `Bearer ${tokenResponse.access_token}` } }) as any console.log('[KEYCLOAK] User info retrieved:', { sub: userInfo.sub, email: userInfo.email, username: userInfo.preferred_username, name: userInfo.name }) // Set session cookie with proper configuration const sessionData = { user: { id: userInfo.sub, email: userInfo.email, username: userInfo.preferred_username || userInfo.email, name: userInfo.name || userInfo.preferred_username || userInfo.email, authMethod: 'keycloak' }, accessToken: tokenResponse.access_token, refreshToken: tokenResponse.refresh_token, expiresAt: Date.now() + (tokenResponse.expires_in * 1000), createdAt: Date.now() } // Create session cookie with better security settings setCookie(event, 'nuxt-oidc-auth', JSON.stringify(sessionData), { httpOnly: true, secure: true, sameSite: 'lax', maxAge: tokenResponse.expires_in, domain: '.portnimara.dev', path: '/' }) console.log('[KEYCLOAK] Session cookie set successfully') console.log('[KEYCLOAK] Redirecting to dashboard...') // Redirect to dashboard await sendRedirect(event, '/dashboard') } catch (error: any) { console.error('[KEYCLOAK] Token exchange failed:', error) console.error('[KEYCLOAK] Error details:', { message: error.message, status: error.status, data: error.data }) // Redirect to login with error await sendRedirect(event, '/login?error=auth_failed') } })