2025-06-15 15:36:48 +02:00
|
|
|
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 {
|
|
|
|
|
// 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: process.env.KEYCLOAK_CLIENT_SECRET || '',
|
|
|
|
|
code: code as string,
|
2025-06-15 15:43:08 +02:00
|
|
|
redirect_uri: 'https://client.portnimara.dev/api/auth/keycloak/callback'
|
2025-06-15 15:36:48 +02:00
|
|
|
}).toString()
|
|
|
|
|
}) as any
|
|
|
|
|
|
|
|
|
|
console.log('[KEYCLOAK] Token exchange successful')
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Set session cookie
|
|
|
|
|
const sessionData = {
|
|
|
|
|
user: userInfo,
|
|
|
|
|
accessToken: tokenResponse.access_token,
|
|
|
|
|
refreshToken: tokenResponse.refresh_token,
|
|
|
|
|
expiresAt: Date.now() + (tokenResponse.expires_in * 1000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a simple session using a secure cookie
|
|
|
|
|
setCookie(event, 'keycloak-session', JSON.stringify(sessionData), {
|
|
|
|
|
httpOnly: true,
|
|
|
|
|
secure: true,
|
|
|
|
|
sameSite: 'lax',
|
|
|
|
|
maxAge: tokenResponse.expires_in
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
console.log('[KEYCLOAK] Session cookie set, redirecting to dashboard')
|
|
|
|
|
|
|
|
|
|
// Redirect to dashboard
|
|
|
|
|
await sendRedirect(event, '/dashboard')
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('[KEYCLOAK] Token exchange failed:', error)
|
|
|
|
|
throw createError({
|
|
|
|
|
statusCode: 500,
|
|
|
|
|
statusMessage: 'Authentication failed during token exchange'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|