Files
web-antrean/server/api/auth/keycloak-login.ts
T

114 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// server/api/auth/keycloak-login.ts
import { randomBytes } from 'crypto'
export default defineEventHandler(async (event) => {
console.log('🔐 Keycloak Login Handler Called')
console.log('📍 Method:', getMethod(event))
// === STALE SESSION CLEANUP ===
// Check for existing session and clean up if invalid/expired
const existingSessionCookie = getCookie(event, 'user_session')
if (existingSessionCookie) {
console.log('🔍 Existing session cookie found, validating...')
try {
// Decode JWT-based session from cookie
const sessionJson = Buffer.from(existingSessionCookie, 'base64').toString('utf-8');
const session = JSON.parse(sessionJson);
if (session) {
// Check if session is expired
const isExpired = Date.now() > session.expiresAt
if (isExpired) {
console.log('🧹 Cleaning up expired session...')
deleteCookie(event, 'user_session')
deleteCookie(event, 'oauth_state')
console.log('✅ Expired session cleared')
} else {
console.log('⚠️ Valid session exists, clearing to allow fresh login...')
deleteCookie(event, 'user_session')
deleteCookie(event, 'oauth_state')
console.log('✅ Existing session cleared for fresh login')
}
}
} catch (error) {
console.warn('⚠️ Error during session cleanup:', error)
// Clear cookies anyway to be safe
deleteCookie(event, 'user_session')
deleteCookie(event, 'oauth_state')
}
} else {
console.log('️ No existing session found, proceeding with fresh login')
}
// === END STALE SESSION CLEANUP ===
try {
const config = useRuntimeConfig()
// Debug: Log runtime config (without secrets)
console.log('🔧 Runtime Config Check:')
console.log(' - Has keycloakIssuer:', !!config.keycloakIssuer)
console.log(' - Has keycloakClientId:', !!config.keycloakClientId)
console.log(' - Has keycloakSecret:', !!config.keycloakClientSecret)
console.log(' - Issuer value:', config.keycloakIssuer)
// Validate required configuration
if (!config.keycloakIssuer) {
throw new Error('KEYCLOAK_ISSUER is not configured')
}
if (!config.keycloakClientId) {
throw new Error('KEYCLOAK_CLIENT_ID is not configured')
}
// Generate state parameter for security
const state = randomBytes(32).toString('hex')
console.log('🎲 Generated state:', state.substring(0, 8) + '...')
const oauthDuration = (config.oauthStateDurationMinutes || 10) * 60; // Default to 10 minutes
const isSecure = process.env.NODE_ENV === 'production';
// Store state in session cookie
// IMPORTANT: This cookie is ONLY for the login flow (CSRF protection).
// It is NOT the main session duration. It expires quickly to ensure security.
setCookie(event, 'oauth_state', state, {
httpOnly: true,
secure: isSecure,
sameSite: 'lax' as const,
maxAge: oauthDuration
})
// Build Keycloak authorization URL
const redirectUri = `${config.public.authUrl}/api/auth/keycloak-callback`
// Debug: Log the redirect URI being used
console.log('🔧 AUTH_ORIGIN from config:', config.public.authUrl)
console.log('🔗 Redirect URI being sent to Keycloak:', redirectUri)
const authUrl = new URL(`${config.keycloakIssuer}/protocol/openid-connect/auth`)
authUrl.searchParams.set('client_id', config.keycloakClientId)
authUrl.searchParams.set('redirect_uri', redirectUri)
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('scope', 'openid profile email')
authUrl.searchParams.set('state', state)
console.log('🏗️ Auth URL built:', authUrl.toString())
return {
success: true,
data: {
authUrl: authUrl.toString()
}
}
} catch (error: any) {
console.error('❌ Login Error:', error.message)
throw createError({
statusCode: 500,
statusMessage: `Failed to generate authorization URL: ${error.message}`
})
}
})