Skip to content

fix(auth): preserve server-enriched auth fields in PermissionProvider merge#1046

Merged
diecoscai merged 3 commits into
stagingfrom
fix/preserve-auth-fields
Apr 14, 2026
Merged

fix(auth): preserve server-enriched auth fields in PermissionProvider merge#1046
diecoscai merged 3 commits into
stagingfrom
fix/preserve-auth-fields

Conversation

@diecoscai
Copy link
Copy Markdown

@diecoscai diecoscai commented Mar 27, 2026

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/roles custom claim — only the ID token does. When enrichSessionWithFlowise calls Flowise /api/v1/auth/me, Flowise's verifyAAIToken extracts roles from the access token and gets roles = []. It then returns roles: [], permissions: [], features: {} for the user.

The merge session.user = { ...session.user, ...enrichedUser } then overwrites the correct roles: ['Admin'] (from the ID token session) with roles: []. This is what useUser() returns to PermissionProvider as runtimeUser, breaking hasFeature('org:manage') for Admins.

This was introduced by the 3.0.11 merge (Jan 4, 2026) which added the custom /api/auth/me handler with enrichSessionWithFlowise. 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.ts

Before the Flowise merge, captures https://theanswer.ai/roles from 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.tsx

Modified mergeUsers() to add a preservedFields guard — server-enriched fields (roles, permissions, features, org_id, organizationId) in initialUser are never overwritten by runtimeUser. This is a second layer of protection that makes PermissionProvider resilient regardless of what useUser() returns.

Impact

  • Fixes: Visibility controls for Admin users (reported by Bisma in Slack)
  • Affects: Permission system across the application
  • No breaking changes: Only fixes the broken permission propagation
  • Backward compatible: Other user roles (Member, Builder) unaffected

Long-term recommendation

Add https://theanswer.ai/roles to the access token (not just the ID token) via an Auth0 Action. This fixes the root cause at the Auth0 level — verifyAAIToken would then extract roles correctly and the entire enrichment chain would work without workarounds.

Test plan

  • Deploy to staging environment
  • Log in as Admin user (Bisma or equivalent)
  • Check Network tab → /api/auth/me response — confirm roles: ['Admin'] is present
  • Navigate to chatflow visibility settings
  • Verify "Browser Extension", "Organization", and "Marketplace" checkboxes are enabled and clickable
  • Verify clicking checkboxes updates visibility state
  • Verify changes persist after saving
  • Test with Member and Builder roles to ensure they still have correct restrictions
  • Verify hasFeature('org:manage') returns true for Admin users in browser console

🤖 Generated with Claude Code

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>
@diecoscai diecoscai self-assigned this Mar 27, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
answerai-docs Building Building Preview Mar 27, 2026 7:39pm
the-answerai Building Building Preview Mar 27, 2026 7:39pm

Request Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +34 to +35
if (preservedFields.includes(key) && merged[key] !== undefined) {
return // Keep server value
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid concern, but this is an acceptable trade-off for this codebase for a few reasons:

  1. initialUser is already frozen at render time. It comes from getCachedSession() on the server — a Next.js layout render. Client navigation doesn't re-render the layout, so initialUser is already "stale" by design. The runtimeUser merge was meant to keep things fresh, but it was the source of the bug.

  2. 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.

  3. The real source of truth for runtimeUser is broken. useUser() hits /api/auth/me which calls Flowise, which reads roles from the access token — and the access token doesn't carry https://theanswer.ai/roles. So runtimeUser.roles is always [] regardless, meaning there were never any valid mid-session updates being applied here.

  4. The guard is conditionalmerged[key] !== undefined. If initialUser doesn't have a field set, runtimeUser can 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>
@diecoscai diecoscai merged commit e853a92 into staging Apr 14, 2026
8 of 10 checks passed
@diecoscai diecoscai deleted the fix/preserve-auth-fields branch April 14, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant