import { NuxtAuthHandler } from '#auth' import ZitadelProvider from '@auth/core/providers/zitadel' const config = useRuntimeConfig() export default NuxtAuthHandler({ secret: config.authSecret, providers: [ ZitadelProvider({ clientId: config.zitadelClientId, issuer: config.zitadelDomain, pkce: true, authorization: { params: { scope: `openid email profile offline_access urn:zitadel:iam:org:project:${config.projectId}:aud` } } }) ], session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60, // 30 days }, callbacks: { async jwt({ token, account, user }) { if (account?.provider === 'zitadel') { token.accessToken = account.access_token token.idToken = account.id_token token.roles = (user as any)?.roles /* Extract org roles from ID token claims */ const allOrgRoles: Record>> = {} const idTokenClaims = (() => { try { const parts = (account.id_token || '').split('.') if (parts.length === 3) { const payload = Buffer.from(parts[1], 'base64url').toString('utf8') return JSON.parse(payload) as Record } } catch { /* ignore */ } return null })() if (idTokenClaims) { for (const key of Object.keys(idTokenClaims)) { if (key.startsWith('urn:zitadel:iam:org:project:') && key.endsWith(':roles')) { allOrgRoles[key] = idTokenClaims[key] } } } /* Also check userinfo response for org role claims */ for (const key of Object.keys((user as any) || {})) { if (key.startsWith('urn:zitadel:iam:org:project:') && key.endsWith(':roles')) { allOrgRoles[key] = (user as any)[key] } } token.allOrgRoles = Object.keys(allOrgRoles).length > 0 ? allOrgRoles : undefined } if (user?.id) { token.sub = user.id if (user.name || (user as any).profile?.given_name) { token.name = user.name || ((user as any).profile?.given_name || '') } token.email = user.email || '' token.image = user.image || undefined } return token }, async session({ session, token }) { const user = session.user as any if (user) { user.name = token.name || undefined user.email = token.email || undefined user.image = token.image || undefined user.roles = token.roles as string[] | undefined user.accessToken = token.accessToken as string | undefined user.allOrgRoles = token.allOrgRoles as Record>> | undefined } return session }, async redirect({ url, baseUrl }) { if (url === '/login') return '/login' return url.startsWith(baseUrl) ? url : baseUrl } } })