// server/api/users/current.get.ts // Get current logged-in user data from JWT token // Helper function to decode JWT token payload const decodeTokenPayload = (token: string | undefined): any | null => { if (!token) return null; try { const parts = token.split("."); if (parts.length < 2) return null; const payloadBase64 = parts[1]; return JSON.parse(Buffer.from(payloadBase64, "base64").toString()); } catch (e) { console.error("❌ Failed to decode token payload:", e); return null; } }; export default defineEventHandler(async (event) => { console.log("🔍 Current user endpoint called"); // Get session from session store using session ID from cookie const { getSessionFromCookie } = await import('~/server/utils/sessionStore'); const session = await getSessionFromCookie(event); if (!session) { throw createError({ statusCode: 401, statusMessage: "No session found or session expired", }); } try { // Decode tokens to get full user data const idTokenPayload = decodeTokenPayload(session.idToken); const accessTokenPayload = decodeTokenPayload(session.accessToken); // Extract roles from different sources const realmRoles = accessTokenPayload?.realm_access?.roles || idTokenPayload?.realm_access?.roles || []; const accountRoles = accessTokenPayload?.resource_access?.account?.roles || idTokenPayload?.resource_access?.account?.roles || []; // Extract resource roles (from all resources in resource_access) const resourceRoles: string[] = []; if (accessTokenPayload?.resource_access) { Object.keys(accessTokenPayload.resource_access).forEach(resourceName => { if (resourceName !== 'account') { // Exclude account, already handled const resourceRolesArray = accessTokenPayload.resource_access[resourceName]?.roles || []; resourceRoles.push(...resourceRolesArray.map((role: string) => `${resourceName}:${role}`)); } }); } if (idTokenPayload?.resource_access) { Object.keys(idTokenPayload.resource_access).forEach(resourceName => { if (resourceName !== 'account') { // Exclude account, already handled const resourceRolesArray = idTokenPayload.resource_access[resourceName]?.roles || []; resourceRoles.push(...resourceRolesArray.map((role: string) => `${resourceName}:${role}`)); } }); } // Legacy: Combined roles (for backward compatibility) const roles = [...realmRoles, ...accountRoles, ...resourceRoles]; // Keycloak uses 'groups_join' in access token, not 'groups' const groups = accessTokenPayload?.groups_join || accessTokenPayload?.groups || idTokenPayload?.groups_join || idTokenPayload?.groups || []; // Determine tipeUser from groups or roles if possible // You can customize this mapping based on your business logic let tipeUser = ''; if (Array.isArray(groups) && groups.length > 0) { // Extract tipeUser from groups path (e.g., "/Instalasi STIM/Devops/Superadmin" -> "Superadmin") const lastGroup = groups[groups.length - 1]; if (typeof lastGroup === 'string') { const parts = lastGroup.split('/').filter(Boolean); if (parts.length > 0) { tipeUser = parts[parts.length - 1]; // Get last part of path } } } // Build user data object const userData = { id: idTokenPayload?.sub || session.user?.id, namaLengkap: idTokenPayload?.name || session.user?.name || `${idTokenPayload?.given_name || ''} ${idTokenPayload?.family_name || ''}`.trim() || idTokenPayload?.preferred_username, namaUser: idTokenPayload?.preferred_username || session.user?.preferred_username || idTokenPayload?.email?.split('@')[0], email: idTokenPayload?.email || session.user?.email, given_name: idTokenPayload?.given_name, family_name: idTokenPayload?.family_name, roles: Array.isArray(roles) ? roles : [], realmRoles: Array.isArray(realmRoles) ? realmRoles : [], accountRoles: Array.isArray(accountRoles) ? accountRoles : [], resourceRoles: Array.isArray(resourceRoles) ? resourceRoles : [], groups: Array.isArray(groups) ? groups : [], tipeUser: tipeUser, // Extracted from groups or empty lastLogin: null, // Will be set on sync // Include full token payloads for reference idTokenPayload, accessTokenPayload, }; console.log("✅ Current user data extracted from JWT"); return userData; } catch (parseError: any) { console.error("❌ Failed to parse session or extract user data:", parseError); throw createError({ statusCode: 401, statusMessage: "Invalid session data", }); } });