bug(web): admin page redirects to /chat — role check fails #196

Closed
opened 2026-03-17 02:16:25 +00:00 by jason.woltje · 0 comments
Owner

Description

Navigating to /admin in the web dashboard immediately redirects to /chat instead of showing the admin panel. This happens even for users with role: "admin" in the database.

Redirect Chain

/admin → AdminRoleGuard checks user.role
       → user.role !== "admin" (role not present or wrong value)
       → router.replace("/")
       → / → redirect("/chat")   (apps/web/src/app/page.tsx)

Root Cause Analysis

The AdminRoleGuard component (src/components/admin-role-guard.tsx) reads the role from the BetterAuth session:

const user = session?.user as
  | (NonNullable<typeof session>['user'] & { role?: string })
  | undefined;

if (user?.role !== 'admin') {
  router.replace('/');  // → redirects to /chat
}

The role field is defined as an additionalFields property in packages/auth/src/auth.ts:

user: {
  additionalFields: {
    role: { type: 'string', required: false, defaultValue: 'member', input: false },
  },
},

Possible causes:

  1. BetterAuth session does not include role in the user object — The additionalFields config may not automatically include the role in the session response. The client-side useSession() may return a user object without the role field, causing the user?.role !== 'admin' check to always be true.

  2. Admin plugin session mismatch — The admin() plugin is configured with adminRoles: ['admin'] but the session user object from useSession() might not expose the role the same way the admin plugin expects it.

  3. User actually has role member — The user in the database may not have been promoted to admin. Check the user table for the role column value.

Debugging Steps

  1. Check browser console: await fetch('http://localhost:4000/api/auth/get-session', { credentials: 'include' }).then(r => r.json()) — inspect whether user.role is present
  2. Check DB: SELECT id, email, role FROM "user" — verify the user has role = 'admin'
  3. Check BetterAuth docs for whether additionalFields are included in the session response by default or need explicit configuration

Location

  • apps/web/src/components/admin-role-guard.tsx — role check and redirect
  • apps/web/src/app/page.tsx — root page redirects to /chat
  • packages/auth/src/auth.ts:59role additional field definition
  • packages/auth/src/auth.ts:71 — admin plugin configuration

Steps to Reproduce

  1. Log in to the web dashboard with a user that has role: 'admin' in the database
  2. Navigate to /admin
  3. Page briefly shows "Loading..." then redirects to /chat
## Description Navigating to `/admin` in the web dashboard immediately redirects to `/chat` instead of showing the admin panel. This happens even for users with `role: "admin"` in the database. ## Redirect Chain ``` /admin → AdminRoleGuard checks user.role → user.role !== "admin" (role not present or wrong value) → router.replace("/") → / → redirect("/chat") (apps/web/src/app/page.tsx) ``` ## Root Cause Analysis The `AdminRoleGuard` component (`src/components/admin-role-guard.tsx`) reads the role from the BetterAuth session: ```typescript const user = session?.user as | (NonNullable<typeof session>['user'] & { role?: string }) | undefined; if (user?.role !== 'admin') { router.replace('/'); // → redirects to /chat } ``` The `role` field is defined as an `additionalFields` property in `packages/auth/src/auth.ts`: ```typescript user: { additionalFields: { role: { type: 'string', required: false, defaultValue: 'member', input: false }, }, }, ``` Possible causes: 1. **BetterAuth session does not include `role` in the user object** — The `additionalFields` config may not automatically include the role in the session response. The client-side `useSession()` may return a user object without the `role` field, causing the `user?.role !== 'admin'` check to always be true. 2. **Admin plugin session mismatch** — The `admin()` plugin is configured with `adminRoles: ['admin']` but the session user object from `useSession()` might not expose the role the same way the admin plugin expects it. 3. **User actually has role `member`** — The user in the database may not have been promoted to admin. Check the `user` table for the `role` column value. ## Debugging Steps 1. Check browser console: `await fetch('http://localhost:4000/api/auth/get-session', { credentials: 'include' }).then(r => r.json())` — inspect whether `user.role` is present 2. Check DB: `SELECT id, email, role FROM "user"` — verify the user has `role = 'admin'` 3. Check BetterAuth docs for whether `additionalFields` are included in the session response by default or need explicit configuration ## Location - `apps/web/src/components/admin-role-guard.tsx` — role check and redirect - `apps/web/src/app/page.tsx` — root page redirects to `/chat` - `packages/auth/src/auth.ts:59` — `role` additional field definition - `packages/auth/src/auth.ts:71` — admin plugin configuration ## Steps to Reproduce 1. Log in to the web dashboard with a user that has `role: 'admin'` in the database 2. Navigate to `/admin` 3. Page briefly shows "Loading..." then redirects to `/chat`
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#196