// Auth.js v5 — Keycloak provider. // // Dev uses the breakpilot-dev realm from platform/orca-platform/dev. The // realm's dev-portal client is a public PKCE client; we still pass a // non-empty clientSecret because the Keycloak provider requires it // structurally — it's unused in the auth code grant for public clients // when PKCE is enabled. import NextAuth from "next-auth"; import Keycloak from "next-auth/providers/keycloak"; export const { handlers, auth, signIn, signOut } = NextAuth({ providers: [ Keycloak({ clientId: process.env.KEYCLOAK_CLIENT_ID ?? "dev-portal", clientSecret: process.env.KEYCLOAK_CLIENT_SECRET ?? "unused-public-client", issuer: process.env.KEYCLOAK_ISSUER ?? "http://localhost:8080/realms/breakpilot-dev", }), ], callbacks: { async jwt({ token, profile }) { // Pass the breakpilot-dev realm's custom claims through to the session. if (profile) { token.tenant_id = (profile as Record).tenant_id as | string | undefined; token.tenant_slug = (profile as Record).tenant_slug as | string | undefined; token.org_roles = (profile as Record).org_roles as | string[] | undefined; token.products = (profile as Record).products as | string[] | undefined; token.plan = (profile as Record).plan as | string | undefined; token.tenant_status = (profile as Record) .tenant_status as string | undefined; } return token; }, async session({ session, token }) { session.user = session.user ?? { name: null, email: null, image: null }; Object.assign(session, { tenant_id: token.tenant_id, tenant_slug: token.tenant_slug, org_roles: token.org_roles, products: token.products, plan: token.plan, tenant_status: token.tenant_status, }); return session; }, }, });