diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index bfd53b5..c76bd8c 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -31,7 +31,7 @@ spec: spec: containers: - name: my-ui - image: registry.gosec.internal/gsc-my/ui:v0.1.1 + image: registry.gosec.internal/gsc-my/ui:v0.1.2 imagePullPolicy: Always ports: - containerPort: 3000 diff --git a/src/app/api/pam/whoami/route.ts b/src/app/api/pam/whoami/route.ts new file mode 100644 index 0000000..8ad850d --- /dev/null +++ b/src/app/api/pam/whoami/route.ts @@ -0,0 +1,49 @@ +// Debug endpoint — returns the session shape as the server sees it. +// Safe to call by the user themselves; reveals nothing about anyone +// else. Remove once eligibility plumbing is verified working. + +import { auth } from "@/auth"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + const session = await auth(); + const u = session?.user as + | { + id?: string; + keycloakId?: string; + tenantId?: string; + customerId?: string; + email?: string; + roles?: string[]; + accessToken?: string; + } + | undefined; + if (!u?.id) return Response.json({ error: "unauthorized" }, { status: 401 }); + + // Re-decode the access token here so we can compare what NextAuth + // wrote into session.user.roles vs. what's currently in realm_access. + let accessTokenRoles: string[] = []; + try { + const payload = u.accessToken?.split(".")[1]; + if (payload) { + const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4); + const decoded = Buffer.from(padded, "base64").toString("utf8"); + const json = JSON.parse(decoded) as { realm_access?: { roles?: string[] } }; + accessTokenRoles = json.realm_access?.roles ?? []; + } + } catch { + /* ignore */ + } + + return Response.json({ + id: u.id, + keycloakId: u.keycloakId, + tenantId: u.tenantId, + customerId: u.customerId, + email: u.email, + sessionRoles: u.roles ?? [], + accessTokenRoles, + eligibleSuffixMatches: (u.roles ?? []).filter((r) => r.endsWith("_eligible")), + }); +}