import { jwtDecode } from 'jwt-decode' import type { KeycloakTokenPayload, Organization } from '~~/types/keycloak' import { createLogger } from '~~/shared/utils/logger' export default defineOAuthKeycloakEventHandler({ async onSuccess(event, { user, tokens }) { const config = useRuntimeConfig() const logger = createLogger({ level: config.public.logLevel, tag: 'auth', fancy: import.meta.env.MODE !== 'production' }) const rawAccessToken = tokens?.access_token let decodedJwt: KeycloakTokenPayload | null = null try { decodedJwt = jwtDecode(rawAccessToken!) } catch (err) { logger.warn('Failed to decode access token:', err) } const organizations = decodedJwt ? extractOrganizations(decodedJwt) : [] const roles = decodedJwt ? extractRoles(decodedJwt) : [] await setUserSession(event, { user: { keycloakId: user.sub, name: user.preferred_username, organizations, roles }, jwt: { accessToken: tokens.access_token, refreshToken: tokens.refresh_token, expiresIn: tokens.expires_in }, loggedInAt: Date.now() }) return sendRedirect(event, '/') }, onError(event) { const config = useRuntimeConfig() const logger = createLogger({ level: config.public.logLevel, tag: 'auth' }) logger.error('Error during keycloak authentication') return sendRedirect(event, '/login') } }) function extractOrganizations(decoded: KeycloakTokenPayload): Organization[] { const organizations: Organization[] = [] const orgClaim = decoded?.organization ?? null if (orgClaim && typeof orgClaim === 'object') { Object.entries(orgClaim).forEach(([name, meta]) => { if (!name || !meta?.id) return organizations.push({ name: name, id: meta.id }) }) } return organizations } function extractRoles(decoded: KeycloakTokenPayload): string[] { return decoded?.resource_access?.legalconsenthub?.roles ?? [] }