Admin Overview

Overview of the admin panel features including user management, organization oversight, and platform analytics.

The Admin Panel provides system administrators with tools to manage users, organizations, and monitor platform activity. Access it at /admin when signed in with an admin account.

This documentation covers the admin system built on Better Auth's admin plugin with the TanStack Start Prisma SaaS Kit's RBAC layer.

Getting Started

Creating Your First Admin

After running pnpm seed, use these test credentials:

  • Email: admin1@makerkit.dev
  • Password: testingpassword

To promote an existing user to admin:

  1. Run Prisma Studio: pnpm --filter "@kit/database" run prisma:studio
  2. Open the users table
  3. Set the role column to admin for your user
  4. Sign out and back in to refresh the session

Alternatively, use the Better Auth CLI or API to update roles programmatically.

Admin Role Verification

When a user accesses /admin/* routes, the system verifies admin status through multiple layers:

  1. Route guard — the /admin route's beforeLoad calls requireAdminAuth (apps/web/src/lib/auth/guards.ts), redirecting anonymous users to sign-in and hiding the surface (404) from authenticated non-admins
  2. Loaders / server functions use requireAdmin() to validate before returning data
  3. Mutations use the createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware) / withAdminPermission() middleware, which verify the user's role before executing

Dashboard Metrics

The admin dashboard at /admin displays four key metrics:

MetricDescription
Total UsersAll registered user accounts
Active SessionsSessions created in the last 24 hours
Banned UsersAccounts with restricted access
New This WeekSign-ups from the past 7 days

These metrics are loaded server-side in the /admin/ route loader, which calls a createServerFn({ method: 'GET' }) wrapper (fetchAdminStats in apps/web/src/lib/admin.functions.ts). Duplicate session lookups within a request are deduped by getSession()'s own memoization.

Architecture

The admin system is organized across several packages:

packages/admin/ # Admin UI components and logic
├── src/
│ ├── dashboard/ # Dashboard page and metrics
│ │ ├── lib/
│ │ │ ├── functions/ # Server functions
│ │ │ ├── loaders/ # Data loaders
│ │ │ ├── schemas/ # Zod schemas
│ │ │ └── services/ # Business logic
│ │ └── page.tsx # Dashboard component
│ ├── users/ # User management
│ ├── organizations/ # Organization management
│ └── hooks/ # Client-side hooks
packages/rbac/ # Role-based access control
├── src/
│ ├── admin/ # Admin RBAC utilities
│ │ ├── factory.ts # Config factory
│ │ ├── defaults.ts # Default resources/functions
│ │ └── client.ts # Permission checking
│ └── admin-rbac.config.ts # Your customization file
packages/auth/ # Authentication utilities
├── src/utils/
│ ├── require-admin.ts # Server-side admin check (requireAdmin/isUserAdmin)
│ └── is-admin-role.ts # Role validation
packages/function-middleware/ # Server function authorization
├── src/
│ ├── middleware.server.ts # adminMiddleware + withAdminPermission
│ └── client.ts # createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware) / withAdminPermission() middleware
apps/web/src/routes/admin/ # Admin file routes (route.tsx, index, users, organizations)
apps/web/src/lib/auth/guards.ts # requireAdminAuth (beforeLoad guard)
apps/web/src/lib/admin.functions.ts # createServerFn loaders for admin routes

Core Features

User Management

The Users section (/admin/users) provides full control over user accounts:

  • Search: Find users by name or email
  • Filter: By role (user/admin) and status (active/banned)
  • Sort: By name, email, creation date, role, or status
  • Paginated results: 25 users per page

Available functions per user:

FunctionDescription
View DetailsFull user profile with sessions and subscriptions
Change RolePromote to admin or demote to user
ImpersonateSign in as the user for debugging (restrictions apply)
Ban/UnbanRestrict or restore account access
RemovePermanently delete the account
View SessionsSee all active sessions
Revoke SessionsEnd specific or all sessions
Admin Users TableClick to expand

See User Management for detailed documentation.

Organization Management

The Organizations section (/admin/organizations) lets you oversee all teams:

  • Search: Find organizations by name
  • View Details: See organization info, members, and subscriptions
  • Member Access: Click through to any member's user details
Admin Organizations TableClick to expand

See Organization Management for detailed documentation.

Role-Based Access Control

Admin permissions are configured via RBAC in packages/rbac/src/admin-rbac.config.ts. The default configuration provides a single admin role with full permissions. You can add custom roles with restricted access:

import { defineAdminRBACConfig } from './admin/factory';
export default defineAdminRBACConfig({
// Add custom roles with limited permissions
roles: {
support: 50, // Can help users but not delete
moderator: 30, // Can ban but limited access
},
permissions: {
support: {
user: ['list', 'get', 'ban'],
session: ['list', 'revoke'],
organizations: ['list', 'view'],
dashboard: ['view'],
},
},
});

See RBAC Permissions for detailed documentation on customizing admin roles.

The admin sidebar includes:

  • Dashboard (/admin) - Overview and statistics
  • Users (/admin/users) - User management
  • Organizations (/admin/organizations) - Organization oversight

Click "Back to Dashboard" in the sidebar header to return to your regular account.

Security Layers

The admin panel includes multiple security layers:

1. Route Guard (beforeLoad)

The /admin route attaches requireAdminAuth in beforeLoad, blocking non-admin access to every /admin/* route:

// apps/web/src/routes/admin/route.tsx
import { createFileRoute } from '@tanstack/react-router';
import { requireAdminAuth } from '#/lib/auth/guards';
export const Route = createFileRoute('/admin')({
beforeLoad: requireAdminAuth,
// ...
});

requireAdminAuth redirects anonymous users to sign-in and throws notFound() for authenticated non-admins.

2. Loader / Server-Function Checks

requireAdmin() verifies admin status inside loaders and server functions. It redirects anonymous users to sign-in and throws notFound() for authenticated non-admins:

import { requireAdmin } from '@kit/auth/require-admin';
export const loadReportsData = async () => {
await requireAdmin(); // redirect / notFound if not admin
// ... return data
};

3. Mutation Protection

createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware) protects all admin mutations:

import { createServerFn } from '@tanstack/react-start';
import { adminFunctionMiddleware } from '@kit/function-middleware/functions';
export const myAdminFunction = createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware)
.validator(schema)
.handler(async ({ data, context }) => {
// context.user is guaranteed to be admin
});

4. Permission Checks

withAdminPermission() enforces granular RBAC permissions. Layer it on createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware), by layering withAdminPermission(requirements):

import { adminFunctionMiddleware } from '@kit/function-middleware/functions';
import { withAdminPermission } from '@kit/function-middleware/server';
export const banUserFunction = createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware)
.middleware([
withAdminPermission({ user: ['ban'] }),
])
.validator(schema)
.handler(async ({ data, context }) => {
// Only admins with 'user:ban' permission reach here
});
import { createServerFn } from '@tanstack/react-start';
import { adminFunctionMiddleware } from '@kit/function-middleware/functions';
import { withAdminPermission } from '@kit/function-middleware/server';
export const banUser = createServerFn({ method: 'POST' }).middleware([
...adminFunctionMiddleware,
withAdminPermission({ user: ['ban'] }),
])
.validator(schema)
.handler(async ({ data, context }) => { /* ... */ });

5. Impersonation Safeguards

The impersonation feature includes restrictions:

  • Cannot impersonate admin users
  • Cannot impersonate banned accounts
  • A banner appears during impersonation showing the original admin

Key Imports

ImportPackagePurpose
requireAdmin@kit/auth/require-adminLoader/server-fn admin check (redirect / notFound)
isUserAdmin@kit/auth/require-adminServer-side check without throwing
requireAdminAuth#/lib/auth/guardsRoute beforeLoad admin guard
isAdminRole@kit/auth/is-admin-roleClient-safe role validation
adminFunctionMiddleware / withAdminPermission@kit/function-middlewareProtected server-function middleware tuples
withAdminPermission@kit/function-middlewarePermission middleware
useAdminPermissions@kit/admin/hooks/use-admin-permissionsClient permission hook

For implementation details on adding custom features, see Extending Admin.

Frequently Asked Questions

How do I create my first admin user?
Run pnpm seed to create test admin credentials, or manually set the role column to 'admin' in the users table using Prisma Studio. Sign out and back in to refresh the session after changing roles.
What's the difference between requireAdmin and adminFunctionMiddleware?
requireAdmin() is for route loaders and server functions - it redirects anonymous users to sign-in and throws notFound() for authenticated non-admins. createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware) (and withAdminPermission()) is for mutations - it throws a masked error instead of redirecting, which is appropriate for API-style calls.
Can I have multiple admin roles with different permissions?
Yes. Configure custom roles in packages/rbac/src/admin-rbac.config.ts using defineAdminRBACConfig(). Each role gets a hierarchy level (number) and explicit permissions. The built-in 'admin' role always has full permissions.
How do I protect a custom admin page?
Three layers: 1) Routes under /admin/* are guarded by requireAdminAuth in the /admin route's beforeLoad, 2) Call requireAdmin() inside your loader/server function, 3) Use createServerFn({ method: 'POST' }).middleware(adminFunctionMiddleware) / withAdminPermission() for any mutations. See Extending Admin for complete examples.
Can admins impersonate other admin users?
No. The impersonation feature has built-in safeguards that prevent impersonating admin users or banned accounts. This is enforced at the Better Auth level.

This admin panel is part of the TanStack Start Prisma SaaS Kit.


Next: User Management