// 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 existingSessionId = getCookie(event, 'user_session') if (existingSessionId) { console.log('๐Ÿ” Existing session cookie found, validating...') try { const { getSession, deleteSession } = await import('~/server/utils/sessionStore') const session = getSession(existingSessionId) if (session) { // Check if session is expired const isExpired = Date.now() > session.expiresAt if (isExpired) { console.log('๐Ÿงน Cleaning up expired session...') deleteSession(existingSessionId) deleteCookie(event, 'user_session') deleteCookie(event, 'oauth_state') console.log('โœ… Expired session cleared') } else { console.log('โš ๏ธ Valid session exists, clearing to allow fresh login...') deleteSession(existingSessionId) deleteCookie(event, 'user_session') deleteCookie(event, 'oauth_state') console.log('โœ… Existing session cleared for fresh login') } } else { console.log('๐Ÿงน Session cookie exists but no session in store, clearing cookie...') deleteCookie(event, 'user_session') deleteCookie(event, 'oauth_state') console.log('โœ… Stale cookie cleared') } } 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) + '...') // Store state in session cookie setCookie(event, 'oauth_state', state, { httpOnly: true, secure: false, sameSite: 'lax', maxAge: 600 // 10 minutes }) // 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}` }) } })