Roles & Permissions

UniAuth uses role-based access control (RBAC) to govern what each user can see and do across the platform. Every user is assigned a single role, and that role carries a set of granular permissions that determine access to admin features, API endpoints, and protected resources.

Overview

UniAuth ships with three built-in roles that cover the most common access patterns. Administrators can also create custom roles with tailored permission sets to match their organization's needs. Roles are stored in the roles table and referenced by name in each user record.

Built-in Roles

RoleDescriptionAccess Level
adminFull system accessCan manage all users, roles, sessions, OAuth clients, webhooks, analytics, and system settings. Can impersonate users and rotate OIDC keys.
moderatorRead-only management accessCan view users, sessions, activity logs, and dashboard statistics. Cannot modify accounts, change roles, or manage OAuth clients.
userStandard user (default)Self-service access only. Can manage their own profile, security settings, connected services, and register OAuth applications via the developer console.

Built-in roles are marked as system roles and cannot be deleted. However, administrators can modify their permission sets if the defaults do not fit their requirements.

Permissions

Permissions are stored as a JSON array on each role and follow a resource:action naming convention. The complete list of available permissions:

PermissionDescriptionAdminModerator
users:readView user list and detailsYesYes
users:writeModify user accounts (change role, lock/unlock)YesNo
users:deleteDelete user accountsYesNo
sessions:readView active sessionsYesYes
sessions:revokeRevoke any user's sessionsYesNo
logs:readView activity and audit logsYesYes
roles:readView roles and their permissionsYesNo
roles:writeCreate, modify, and delete custom rolesYesNo
stats:readView dashboard statistics and analyticsYesYes
settings:readView system configurationYesNo
settings:writeModify system settings (SMTP, branding, policies)YesNo
oauth:readView all registered OAuth clientsYesNo
oauth:writeCreate, modify, and delete OAuth clientsYesNo

Role in JWT Tokens

The user's role is embedded in their session JWT, making it available for server-side authorization checks without additional database queries. The JWT payload includes:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Admin",
  "role": "admin",
  "iat": 1700000000,
  "exp": 1700604800
}

This allows your application code or middleware to make authorization decisions based on the role claim without hitting the database on every request.

Middleware Protection

All routes under /admin/* are automatically protected by the UniAuth middleware. The middleware performs two checks:

  1. Authentication — The request must include a valid session cookie. Unauthenticated requests are redirected to the login page.
  2. Role check — The user's role (from the JWT) must be admin or moderator. Users with insufficient permissions receive a 403 response.

This protection runs at the edge before your page or API handler executes, so unauthorized requests are blocked with minimal latency.

API Role Checks

In addition to middleware-level protection, individual API route handlers verify the caller's role using the verifyAdmin() helper function. This provides defense in depth:

// Example: Admin API route handler
import { verifyAdmin } from "@/lib/admin";
import { successResponse, errorResponse } from "@/lib/api-response";

export async function GET(request: Request) {
  // verifyAdmin() checks the session cookie and confirms
  // the user has admin or moderator role
  const adminUser = await verifyAdmin();
  if (!adminUser) {
    return errorResponse("Unauthorized", 403);
  }

  // Proceed with admin-only logic
  const data = await fetchSensitiveData();
  return successResponse(data);
}

For more granular checks, you can inspect the user's specific permissions by loading the role from the database and checking the permissions array.