Docs / Admin panel / Admin access
Admin access
The admin panel under /admin/* is a separate surface from the user dashboard, gated by one global role: superadmin. This is a completely different axis from the per-team owner / admin / member roles, a team owner has no admin-panel access, and a superadmin is not automatically privileged inside any given team. The role column on user (set via Better Auth's admin plugin in lib/auth.ts) holds it; SUPERADMIN_ROLE in lib/dal/admin.ts is the constant.
The gate
Every /api/admin/* route calls requireSuperadmin() first, the admin-panel counterpart to authorizeTeamAccess:
const access = await requireSuperadmin();
if (!access.ok) return adminAccessErrorResponse(access);
// access.userId is the acting superadmin from here on
It returns 401 for no session and 403 for a signed-in non-superadmin. The split matters: a 401 tells the client to redirect to sign-in, a 403 is a real "you're logged in but not allowed", the page-level admin routes behave the same way (unauthenticated redirects, authenticated-but-unauthorized hits a 403 page).
Every action is audited
Admin actions that touch a user's account write one row to audit_log through logAdminAction:
await logAdminAction({
actorUserId: access.userId,
targetUserId: id,
action: "user.impersonated",
});
The user-detail Activity tab reads these back scoped to targetUserId, so each account carries its own moderation history. Write the audit row only after the underlying action succeeds, logging it before (or unconditionally) records a false trail when the action was actually rejected.
Superadmins can't act on each other
Better Auth blocks impersonating another superadmin, but banUser and delete have no equivalent check. isSuperadminUser(id) in lib/dal/admin-users.ts is the rank guard that closes that gap, without it a rogue or compromised superadmin could ban or delete every other superadmin. Any destructive admin action against an account must call it first.
Navigation
lib/admin-nav.ts is the single source for the admin sidebar, command palette, and breadcrumbs (same role lib/app-nav.ts plays for the dashboard), listing the three admin routes: Users, Subscriptions, and Settings. See Users & moderation and Feature flags & settings for what each does.