Skip to content

feat(auth): migrate to better-auth admin plugin with unified Admin tab#3612

Open
waleedlatif1 wants to merge 9 commits intostagingfrom
waleedlatif1/better-auth-admin
Open

feat(auth): migrate to better-auth admin plugin with unified Admin tab#3612
waleedlatif1 wants to merge 9 commits intostagingfrom
waleedlatif1/better-auth-admin

Conversation

@waleedlatif1
Copy link
Collaborator

@waleedlatif1 waleedlatif1 commented Mar 16, 2026

Summary

  • Replace custom isSuperUser boolean with better-auth's native admin plugin (role, banned, banReason, banExpires on user table, impersonatedBy on session table)
  • Add unified Admin settings tab consolidating super admin toggle, workflow import, and user management (list, set role, ban/unban)
  • Remove redundant /api/user/super-user API route — role now available directly from session via customSession + admin plugin
  • Backfill migration promotes existing is_super_user = true rows to role = 'admin' before dropping the column

Changes

  • Schema: isSuperUserrole text field + banned/banReason/banExpires columns, impersonatedBy on session
  • Auth config: Register admin() server plugin and adminClient() client plugin
  • Session: user.role exposed via better-auth's customSession — no custom override needed
  • Admin tab: New settings tab with React Query hooks (useAdminUsers, useSetUserRole, useBanUser, useUnbanUser) following project patterns
  • Navigation: requiresAdminRole gate (checks role only, not mode toggle) so admin tab stays accessible
  • Cleanup: Delete Debug tab, super-user route, useSuperUserStatus hook

Test plan

  • Verify migration backfills existing super users to admin role
  • Confirm admin tab visible for admin-role users, hidden for regular users
  • Test super admin mode toggle persists correctly
  • Test workflow import from Admin tab
  • Test user management: list, promote/demote, ban/unban
  • Verify session includes user.role field after login

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Mar 16, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 17, 2026 0:18am

Request Review

@cursor
Copy link

cursor bot commented Mar 16, 2026

You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace.

To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR replaces the custom isSuperUser boolean with better-auth's native admin plugin, introducing role, banned, banReason, and banExpires fields on the user table and impersonatedBy on the session table. It consolidates super admin functionality into a new unified Admin settings tab and eliminates the now-redundant /api/user/super-user API route in favour of reading user.role directly from the session.

Key changes:

  • Migration (0177_wise_puma.sql): Adds the new columns, correctly backfills existing is_super_user = true rows to role = 'admin' before dropping the old column.
  • Auth config: admin() server plugin and adminClient() client plugin registered; endpoints are gated by better-auth to role === 'admin'.
  • Admin tab (admin.tsx): New settings tab consolidates super admin mode toggle, workflow import, and user management (list, promote/demote, ban/unban) with pagination; uses React Query hooks following project patterns.
  • Guard (settings.tsx): Non-admin users navigating directly to /settings/admin are redirected to general.
  • Sidebar: Removes the useSuperUserStatus API call and reads session?.user?.role instead, eliminating an extra network round-trip on every settings open.
  • superUserModeEnabled ?? true in templates/page.tsx: The server-side templates page still defaults superUserModeEnabled to true when no settings record exists, while the Admin tab toggle was correctly fixed to default false. This means a new admin user without saved preferences will silently see admin-only templates even though the toggle appears OFF.

Confidence Score: 4/5

  • Safe to merge with the superUserModeEnabled default inconsistency addressed in templates/page.tsx
  • The migration is correct (backfill is present, addressed from prior review), the auth plugin integration is sound, and the admin tab guard prevents unauthorized UI access. The one logic inconsistency — superUserModeEnabled ?? true in templates/page.tsx vs ?? false in the Admin tab — can silently expose admin-only templates to new admins. The missing mutation error feedback is a minor UX gap. All other previous review issues have been addressed.
  • apps/sim/app/workspace/[workspaceId]/templates/page.tsx — the superUserModeEnabled default needs to be aligned to false

Important Files Changed

Filename Overview
packages/db/migrations/0177_wise_puma.sql Migration correctly adds role/banned/ban_reason/ban_expires columns to user and impersonated_by to session, with the backfill UPDATE promoting existing super users before dropping is_super_user.
packages/db/schema.ts Schema correctly removes isSuperUser (notNull boolean) and adds role (nullable text), banned, banReason, banExpires; role column is nullable (no .notNull()) unlike the previous boolean, but better-auth sets it on user creation so it should be fine in practice.
apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx New Admin settings tab is well-structured with pagination and ban/unban flows; promote/demote and unban mutations have no user-facing error display, only logger output.
apps/sim/hooks/queries/admin-users.ts Well-structured React Query hooks wrapping better-auth admin client calls with proper cache invalidation on settled mutations.
apps/sim/app/workspace/[workspaceId]/templates/page.tsx Updated to use role === 'admin' instead of isSuperUser, but superUserModeEnabled still defaults to true server-side while the new Admin tab toggle defaults to false, creating a mismatch for new admin users without saved settings.
apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx Adds client-side admin guard that redirects non-admin users away from /settings/admin; handles session loading gracefully.
apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx Correctly replaces the useSuperUserStatus API call with session?.user?.role === 'admin' and adds requiresAdminRole filter; eliminates a redundant network round-trip.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User navigates to /settings/admin] --> B{useSession: role === 'admin'?}
    B -- No --> C[effectiveSection = 'general'\nRenders General component]
    B -- Yes --> D[effectiveSection = 'admin'\nRenders Admin component]

    E[Settings Sidebar renders nav items] --> F{item.requiresAdminRole?}
    F -- No --> G[Show item]
    F -- Yes --> H{session?.user?.role === 'admin'?}
    H -- No --> I[Hide item]
    H -- Yes --> J[Show Admin tab]

    D --> K[Super Admin Mode Toggle\nupdates generalSettings]
    D --> L[Workflow Import\nvia useImportWorkflow]
    D --> M[User Management\nuseAdminUsers / useSetUserRole / useBanUser / useUnbanUser]

    M --> N[client.admin.listUsers]
    M --> O[client.admin.setRole]
    M --> P[client.admin.banUser / unbanUser]

    N & O & P --> Q[better-auth admin plugin\ngates on role === 'admin']
Loading

Comments Outside Diff (1)

  1. apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx, line 379-394 (link)

    No user-facing error feedback for promote/demote and unban

    The setUserRole and unbanUser mutations only log errors to the console via logger.error in the hook's onError handler. If a promote/demote or unban fails (e.g. a network error or a permission error from the server), the UI silently does nothing — the user list refreshes (since onSettled invalidates the query) and the action appears to have been a no-op with no indication of failure.

    In contrast, usersError is rendered for the list query and importWorkflow.error is displayed inline. Consider surfacing errors for these mutations similarly, e.g. by reading setUserRole.error / unbanUser.error and rendering them in the affected row or as a shared error banner at the top of the User Management section.

Last reviewed commit: 0665b93

Consolidate superuser features into a single Admin settings tab:
- Super admin mode toggle (moved from General)
- Workflow import (moved from Debug)
- User management via better-auth admin (list, set role, ban/unban)

Replace Debug tab with Admin tab gated by requiresAdminRole.
Add React Query hooks for admin user operations.
Add UPDATE statement to promote is_super_user=true rows to role='admin'
before dropping the is_super_user column, preventing silent demotion.
- Fix cn import path to @/lib/core/utils/cn
- Use valid Badge variants (blue/gray/red/green instead of secondary/destructive)
- Type setRole param as 'user' | 'admin' union
Include user.role in customSession so it's available client-side.
Replace all useSuperUserStatus() calls with session.user.role === 'admin'.
Delete the now-redundant /api/user/super-user endpoint.
The admin plugin already includes role on the user object.
No need to manually spread it in customSession.
… practices

- Remove unsafe unknown/Record casting, use better-auth typed response
- Add placeholderData: keepPreviousData for paginated variable-key query
- Remove nullable types where defaults are always applied
@waleedlatif1 waleedlatif1 changed the title feat(auth): migrate to better-auth admin plugin feat(auth): migrate to better-auth admin plugin with unified Admin tab Mar 16, 2026
@waleedlatif1
Copy link
Collaborator Author

@greptile

- Fix superUserModeEnabled default to false (matches sidebar behavior)
- Reset banReason when switching ban target to prevent state bleed
- Guard admin section render with session role check for direct URL access
@waleedlatif1
Copy link
Collaborator Author

@greptile

Three places defaulted to true while admin tab and sidebar used false.
Align all to false so new admins see consistent behavior.
@cursor
Copy link

cursor bot commented Mar 17, 2026

You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace.

To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard.

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