// Forwards to platform-api's POST /flags/evaluate. The platform-api endpoint // requires an explicit tenantSlug; we derive it from the JWT here so portal // consumers don't have to know which tenant they're "in" — the answer is // always the first non-admin group on their token. // // Caller can override by passing { tenantSlug } in the body, but in practice // the portal serves end users with a single tenant in dev. import { getUserSession } from 'nuxt-oidc-auth/runtime/server/utils/session.js' // Group names that belong to platform-wide admin rather than a single tenant. // Kept here (not env-driven) because the value is fixed by the Authentik // bootstrap script — see services/platform-api/src/users/users.controller.ts // where the same name is used as the admin bootstrap group. const ADMIN_GROUPS = new Set(['dezky-platform-admins', 'authentik Admins']) function decodeJwtClaims(token: string): Record { const parts = token.split('.') if (parts.length < 2) throw new Error('Not a JWT') const payload = parts[1]!.replace(/-/g, '+').replace(/_/g, '/') const padded = payload + '='.repeat((4 - (payload.length % 4)) % 4) return JSON.parse(Buffer.from(padded, 'base64').toString('utf8')) } export default defineEventHandler(async (event) => { const session = await getUserSession(event).catch(() => null) const accessToken = (session as { accessToken?: string } | null)?.accessToken if (!accessToken) throw createError({ statusCode: 401, statusMessage: 'Not signed in' }) // Allow explicit override; otherwise derive from the JWT. const body = (await readBody(event).catch(() => null)) as { tenantSlug?: string } | null let tenantSlug = body?.tenantSlug if (!tenantSlug) { const claims = decodeJwtClaims(accessToken) const groups = (claims.groups as string[] | undefined) ?? [] // Authentik double-lists each group via policy bindings; dedupe + filter. const tenantGroups = Array.from(new Set(groups)).filter((g) => !ADMIN_GROUPS.has(g)) tenantSlug = tenantGroups[0] } if (!tenantSlug) { throw createError({ statusCode: 400, statusMessage: 'No tenant slug available for this user' }) } const base = process.env.PLATFORM_API_INTERNAL_URL ?? 'http://platform-api:3001' return $fetch(`${base}/flags/evaluate`, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}` }, body: { tenantSlug }, }) })