RBAC & Permissions

Role-based access control in AuthSaas — how roles work, how to assign them, and how to enforce them in your app.

Overview#

AuthSaas ships with a fully live role-based access control system. Key properties:

  • Roles are per-app (scoped to a TenantApp) — completely isolated between your different projects.
  • Three default roles are auto-seeded every time you create an app: owner, admin, and user.
  • New users are automatically assigned the user role on registration.
  • Roles are included in the JWT roles array — read them in your app to gate features without an extra API call.

Default roles#

The following roles and permission sets are created automatically for every new app:

Role    Permissions
user    read:profile, write:profile
admin   + read:users, write:users, read:audit, read:sessions
owner   + delete:users, read:roles, write:roles, delete:sessions

The + notation means the role inherits all permissions from the role above it plus the listed additions.

Permissions catalog#

All 10 available permissions and what they grant:

  • read:profile — View own profile
  • write:profile — Edit own profile
  • read:users — List and view users
  • write:users — Create and update users
  • delete:users — Delete users
  • read:roles — View roles and permissions
  • write:roles — Create and update roles
  • read:audit — View audit logs
  • read:sessions — View active sessions
  • delete:sessions — Revoke sessions

Assigning roles#

New users automatically receive the user role when they register. To assign a different role — or add additional roles — use the dashboard UI or call the API directly:

// Assign roles to a user via the API
const response = await fetch(`/users/${userId}/roles`, {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
  },
  body: JSON.stringify({ roles: ['admin'] }),
});

const { user } = await response.json();
console.log(user.roles); // ['admin']

Checking roles#

Read the roles array from the decoded JWT payload to enforce access control in your application:

// Decode the access token in your API route
const payload = verifyAccessToken(req.headers.authorization.slice(7));
// payload.roles = ['admin']

// Gate a feature
if (!payload.roles.includes('admin')) {
  return Response.json({ error: 'Forbidden' }, { status: 403 });
}

Note

Roles in JWT reflect the state at login time. For real-time role changes, call /auth/refresh to get a new token pair.

Custom roles#

You can create custom roles beyond the three defaults. First create the role, then assign permissions to it:

// 1. Create a custom role
const roleRes = await fetch('/roles', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
  },
  body: JSON.stringify({ name: 'moderator' }),
});
const { role } = await roleRes.json();

// 2. Assign permissions to the new role
await fetch(`/roles/${role.id}/permissions`, {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
  },
  body: JSON.stringify({
    permissions: ['read:users', 'read:audit'],
  }),
});

Note

Custom role names are case-sensitive and must be unique per app.

JWT claims#

The full decoded access token payload — all claims your application can rely on:

{
  "sub": "usr_clxxxxxxxxxxxxxxxx",
  "email": "user@example.com",
  "appId": "app_clxxxxxxxxxxxxxxxx",
  "roles": ["admin"],
  "iat": 1709812345,
  "exp": 1709813245
}