fix(auth): preserve server-enriched auth fields in PermissionProvider merge#1046
Conversation
The mergeUsers function was overwriting server-enriched permission data
(roles, permissions, features) with client-side Auth0 data that lacks
these fields. This caused Admin users to fail permission checks because
hasFeature('org:manage') would return false despite having the role.
The fix explicitly preserves auth fields from the server (initialUser)
and only allows runtime updates for non-auth fields (name, email, picture).
Fixes visibility settings not working for Admin users.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92dd486d82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (preservedFields.includes(key) && merged[key] !== undefined) { | ||
| return // Keep server value |
There was a problem hiding this comment.
Let refreshed auth fields replace stale initial values
The new preservedFields guard makes roles, permissions, and features effectively immutable once initialUser has any value, so later useUser() refreshes cannot update authorization state. In this app, PermissionProvider is mounted from the shared Main UI layout, which typically keeps initialUser stable during client navigation; if /api/auth/me returns changed permissions (for example after role/feature updates), this merge now ignores them and the UI keeps stale access decisions until a full reload.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid concern, but this is an acceptable trade-off for this codebase for a few reasons:
-
initialUseris already frozen at render time. It comes fromgetCachedSession()on the server — a Next.js layout render. Client navigation doesn't re-render the layout, soinitialUseris already "stale" by design. TheruntimeUsermerge was meant to keep things fresh, but it was the source of the bug. -
Role/feature changes are rare and always require a re-login in practice. Auth0 roles are set at login time and don't change mid-session in this app.
-
The real source of truth for
runtimeUseris broken.useUser()hits/api/auth/mewhich calls Flowise, which reads roles from the access token — and the access token doesn't carryhttps://theanswer.ai/roles. SoruntimeUser.rolesis always[]regardless, meaning there were never any valid mid-session updates being applied here. -
The guard is conditional —
merged[key] !== undefined. IfinitialUserdoesn't have a field set,runtimeUsercan still populate it. The preservation only kicks in when the server already provided a value.
The long-term fix (noted in the PR description) is to add https://theanswer.ai/roles to the access token via an Auth0 Action — then the full chain works correctly and mid-session updates would be reliable.
…s token lacks claim The access token sent to Flowise does not carry the https://theanswer.ai/roles custom claim (only the ID token does). verifyAAIToken extracts roles from the access token and gets roles=[], causing enrichUserWithAAIData to return empty roles/permissions/features, which then overwrites the correct roles from the Auth0 session in the merge. Save https://theanswer.ai/roles from the session before the Flowise merge and restore it afterward if Flowise returned an empty roles array. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ider Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Fixes the visibility controls issue where Admin users could not click or toggle chatflow visibility checkboxes. The root cause was a two-layer problem in the auth enrichment pipeline.
Root Cause
The real bug is in
enrichSessionWithFlowise(packages-answers/utils/src/auth/enrichSession.ts).The Auth0 access token does not carry the
https://theanswer.ai/rolescustom claim — only the ID token does. WhenenrichSessionWithFlowisecalls Flowise/api/v1/auth/me, Flowise'sverifyAAITokenextracts roles from the access token and getsroles = []. It then returnsroles: [],permissions: [],features: {}for the user.The merge
session.user = { ...session.user, ...enrichedUser }then overwrites the correctroles: ['Admin'](from the ID token session) withroles: []. This is whatuseUser()returns toPermissionProviderasruntimeUser, breakinghasFeature('org:manage')for Admins.This was introduced by the 3.0.11 merge (Jan 4, 2026) which added the custom
/api/auth/mehandler withenrichSessionWithFlowise. Before that,useUser()returned the raw Auth0 session (ID token claims only) — no Flowise overwrite, roles were always correct.Changes
Fix 1 — Root cause:
packages-answers/utils/src/auth/enrichSession.tsBefore the Flowise merge, captures
https://theanswer.ai/rolesfrom the Auth0 session (always reliable, from the ID token). After the merge, restores it if Flowise returned empty roles.Fix 2 — Defensive layer:
packages-answers/ui/src/PermissionProvider.tsxModified
mergeUsers()to add apreservedFieldsguard — server-enriched fields (roles,permissions,features,org_id,organizationId) ininitialUserare never overwritten byruntimeUser. This is a second layer of protection that makesPermissionProviderresilient regardless of whatuseUser()returns.Impact
Long-term recommendation
Add
https://theanswer.ai/rolesto the access token (not just the ID token) via an Auth0 Action. This fixes the root cause at the Auth0 level —verifyAAITokenwould then extract roles correctly and the entire enrichment chain would work without workarounds.Test plan
/api/auth/meresponse — confirmroles: ['Admin']is presenthasFeature('org:manage')returns true for Admin users in browser console🤖 Generated with Claude Code