Authentication

AxilJS RBAC: Role and Permission-Based Access Control

Implement role-based access control (RBAC), permission-based authorization, and resource ownership checks in TypeScript and Node.js applications with AxilJS.

3 min readDocumentationEdit this page

Role-Based Access Control (RBAC)

AxilJS provides authorization middleware for implementing role-based access control (RBAC), permission-based access control, and resource ownership checks in TypeScript and Node.js applications.

These authorization checks can be combined with JWT authentication to protect routes based on the authenticated user's role, permissions, or ownership of a resource.

Role-Based Access

Use requireRole() to restrict a route to one or more roles.

typescript
import { requireRole } from '@axiljs/auth'
 
app.delete(
  '/users/:id',
  authenticate(jwt),
  requireRole('admin'),
  handler
)
 
app.get(
  '/reports',
  authenticate(jwt),
  requireRole(['admin', 'manager']),
  handler
)

A single role can be provided as a string:

typescript
requireRole('admin')

Multiple allowed roles can be provided as an array:

typescript
requireRole(['admin', 'manager'])

The authenticated user must have an allowed role to access the protected route.

Permission-Based Access Control

For more granular authorization, use requirePermission().

typescript
import { requirePermission } from '@axiljs/auth'
 
app.post(
  '/posts',
  authenticate(jwt),
  requirePermission('posts:create'),
  handler
)

Permissions can represent specific application capabilities, such as:

text
posts:create
posts:read
posts:update
posts:delete

This approach allows authorization rules to be defined around specific actions rather than relying only on broad user roles.

Wildcard Permissions

Permission patterns can use wildcards.

typescript
requirePermission('admin:*')

The admin:* pattern matches permissions such as:

text
admin:read
admin:write
admin:delete

This can be useful when a role or user should have access to an entire permission namespace rather than a single operation.

Resource Ownership

Use requireOwnership() when access should depend on whether the authenticated user owns the requested resource.

typescript
import { requireOwnership } from '@axiljs/auth'
 
app.get(
  '/users/:id/profile',
  authenticate(jwt),
  requireOwnership('id'),
  handler
)

The 'id' argument identifies the route parameter used for the ownership check.

For example, with:

text
/users/:id/profile

the middleware can use the id route parameter to determine whether the authenticated user owns the requested resource.

Administrators bypass the ownership check.

Combining Authentication and Authorization

Authentication and authorization can be composed as middleware:

typescript
app.delete(
  '/users/:id',
  authenticate(jwt),
  requireRole('admin'),
  handler
)

The request first passes through JWT authentication and then through the role authorization check.

text
HTTP Request
     │
     ▼
authenticate(jwt)
     │
     ▼
Authenticated User
     │
     ▼
Authorization Check
     │
 ┌───┼─────────────────┐
 ▼   ▼                 ▼
Role Permission   Ownership
 │       │             │
 └───────┴─────────────┘
           │
           ▼
      Route Handler

This separation keeps authentication and authorization responsibilities distinct.

Choosing an Authorization Strategy

Use role-based authorization when access is naturally grouped by roles:

typescript
requireRole('admin')

Use permission-based authorization when you need fine-grained control over individual operations:

typescript
requirePermission('posts:create')

Use ownership checks when access depends on the relationship between the authenticated user and a resource:

typescript
requireOwnership('id')

These mechanisms can be used independently or composed with JWT authentication to enforce application-specific access-control rules.

Common RBAC Patterns

An administrator-only endpoint:

typescript
app.delete(
  '/users/:id',
  authenticate(jwt),
  requireRole('admin'),
  handler
)

An endpoint available to multiple roles:

typescript
app.get(
  '/reports',
  authenticate(jwt),
  requireRole(['admin', 'manager']),
  handler
)

A permission-protected endpoint:

typescript
app.post(
  '/posts',
  authenticate(jwt),
  requirePermission('posts:create'),
  handler
)

An ownership-protected endpoint:

typescript
app.get(
  '/users/:id/profile',
  authenticate(jwt),
  requireOwnership('id'),
  handler
)

Help improve the documentation

AxilJS is open source and documentation improvements are welcome.

AxilJS DocumentationMIT License · Built by SyntaxilitY