diff --git a/composables/useAuth.ts b/composables/useAuth.ts index 53906f6..1a9890f 100644 --- a/composables/useAuth.ts +++ b/composables/useAuth.ts @@ -50,15 +50,23 @@ export const useAuth = () => { console.log('✅ Login response received:', response); - if (response.success && response.user) { - user.value = response.user; - console.log('👤 User set in composable:', user.value); + if (response.success) { + // After successful login, get the user data from the session + console.log('🔄 Getting user data from session...'); + const sessionSuccess = await checkAuth(); - // Redirect to dashboard or intended page - console.log('🔄 Redirecting to:', response.redirectTo || '/dashboard'); - await navigateTo(response.redirectTo || '/dashboard'); - - return { success: true }; + if (sessionSuccess) { + console.log('👤 User data retrieved from session:', user.value); + + // Redirect to dashboard or intended page + console.log('🔄 Redirecting to:', response.redirectTo || '/dashboard'); + await navigateTo(response.redirectTo || '/dashboard'); + + return { success: true }; + } else { + console.warn('❌ Failed to get user data from session after login'); + return { success: false, error: 'Login succeeded but failed to get user data' }; + } } console.warn('❌ Login response indicates failure:', response); diff --git a/server/api/auth/direct-login.post.ts b/server/api/auth/direct-login.post.ts index f7d33ed..752c74a 100644 --- a/server/api/auth/direct-login.post.ts +++ b/server/api/auth/direct-login.post.ts @@ -146,7 +146,7 @@ export default defineEventHandler(async (event) => { client_secret: config.keycloak.clientSecret, username, password, - scope: 'openid email profile' + scope: 'openid email profile roles' }) }); @@ -180,6 +180,17 @@ export default defineEventHandler(async (event) => { const tokens = await tokenResponse.json(); console.log('✅ Token exchange successful'); + // Decode the access token to see what's inside + let tokenPayload = null; + try { + const tokenParts = tokens.access_token.split('.'); + const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString()); + tokenPayload = payload; + console.log('🔍 Access token payload:', JSON.stringify(payload, null, 2)); + } catch (err) { + console.warn('⚠️ Could not decode access token:', err); + } + // Get user info from Keycloak const userResponse = await fetch(`${config.keycloak.issuer}/protocol/openid-connect/userinfo`, { headers: { @@ -197,19 +208,74 @@ export default defineEventHandler(async (event) => { } const userInfo = await userResponse.json(); - console.log('✅ User info retrieved:', { - sub: userInfo.sub, - email: userInfo.email, - name: userInfo.name - }); + console.log('✅ User info retrieved:', JSON.stringify(userInfo, null, 2)); + + // Extract groups/roles from multiple possible locations + const extractGroups = (tokenPayload: any, userInfo: any): string[] => { + const allGroups: string[] = []; + + // Check userInfo for groups (from userinfo endpoint) + if (userInfo.groups && Array.isArray(userInfo.groups)) { + allGroups.push(...userInfo.groups); + console.log('📋 Found groups in userInfo.groups:', userInfo.groups); + } + + // Check userInfo for roles + if (userInfo.roles && Array.isArray(userInfo.roles)) { + allGroups.push(...userInfo.roles); + console.log('📋 Found roles in userInfo.roles:', userInfo.roles); + } + + // Check token payload for realm_access.roles + if (tokenPayload?.realm_access?.roles && Array.isArray(tokenPayload.realm_access.roles)) { + allGroups.push(...tokenPayload.realm_access.roles); + console.log('📋 Found roles in token realm_access.roles:', tokenPayload.realm_access.roles); + } + + // Check token payload for resource_access roles + if (tokenPayload?.resource_access) { + Object.keys(tokenPayload.resource_access).forEach(clientId => { + const clientRoles = tokenPayload.resource_access[clientId]?.roles; + if (clientRoles && Array.isArray(clientRoles)) { + allGroups.push(...clientRoles); + console.log(`📋 Found roles in token resource_access.${clientId}.roles:`, clientRoles); + } + }); + } + + // Check token payload for groups claim + if (tokenPayload?.groups && Array.isArray(tokenPayload.groups)) { + allGroups.push(...tokenPayload.groups); + console.log('📋 Found groups in token.groups:', tokenPayload.groups); + } + + // Remove duplicates and filter out default Keycloak roles + const uniqueGroups = [...new Set(allGroups)].filter(group => + !['default-roles-monacousa', 'offline_access', 'uma_authorization'].includes(group) + ); + + console.log('📋 Final extracted groups:', uniqueGroups); + return uniqueGroups; + }; // Tier determination logic - admin > board > user priority const determineTier = (groups: string[]): 'user' | 'board' | 'admin' => { - if (groups.includes('admin')) return 'admin'; - if (groups.includes('board')) return 'board'; + console.log('🎯 Determining tier from groups:', groups); + if (groups.includes('admin')) { + console.log('🎯 User has admin tier'); + return 'admin'; + } + if (groups.includes('board')) { + console.log('🎯 User has board tier'); + return 'board'; + } + console.log('🎯 User has default user tier'); return 'user'; // Default tier }; + // Extract groups from all possible sources + const extractedGroups = extractGroups(tokenPayload, userInfo); + // Create session data with extended expiry if remember me const sessionData = { user: { @@ -219,8 +285,8 @@ export default defineEventHandler(async (event) => { firstName: userInfo.given_name, lastName: userInfo.family_name, username: userInfo.preferred_username || username, - tier: determineTier(userInfo.groups || []), - groups: userInfo.groups || ['user'] + tier: determineTier(extractedGroups), + groups: extractedGroups.length > 0 ? extractedGroups : ['user'] }, tokens: { accessToken: tokens.access_token, @@ -259,9 +325,9 @@ export default defineEventHandler(async (event) => { // Ensure we return a proper response with status setResponseStatus(event, 200); + // Return a minimal response to prevent 502 errors return { success: true, - user: sessionData.user, redirectTo: '/dashboard' };