115 lines
4.0 KiB
TypeScript
115 lines
4.0 KiB
TypeScript
// 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}`
|
||
})
|
||
}
|
||
}) |