Skip to main content

Authorisation

Authorisation determines what an authenticated user is allowed to do. While authentication answers "Who are you?", authorisation answers "What can you access?"

Authorisation Models

Role-Based Access Control (RBAC)

Permissions assigned to roles, roles assigned to users:

const roles = {
admin: ['read', 'write', 'delete', 'manage_users'],
editor: ['read', 'write'],
viewer: ['read'],
};

function canAccess(user, permission) {
return roles[user.role]?.includes(permission);
}

Attribute-Based Access Control (ABAC)

Decisions based on attributes and policies:

// Can edit if: owner OR (editor AND same_department AND during_business_hours)
const policy = {
resource: 'document',
action: 'edit',
condition: (user, resource, context) => {
return resource.ownerId === user.id ||
(user.role === 'editor' &&
user.department === resource.department &&
context.hour >= 9 && context.hour < 17);
},
};

Access Control Lists (ACL)

Explicit permissions per resource instance.

Common Patterns

Middleware Authorisation

const requireRole = (role) => (req, res, next) => {
if (req.user.role !== role) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};

app.delete('/users/:id', requireRole('admin'), deleteUser);

Resource-Level Authorisation

async function updateDocument(userId, docId, changes) {
const doc = await Document.findById(docId);

if (doc.ownerId !== userId && !await hasPermission(userId, docId, 'write')) {
throw new ForbiddenError('Cannot edit this document');
}

return doc.update(changes);
}

Authorisation in APIs

LevelApproach
EndpointMiddleware checks
FieldResolver/serializer filtering
RowQuery-time filtering (WHERE clauses)

What We Like

  • Clear separation: Auth logic separate from business logic
  • Policy engines: Tools like OPA, Casbin, or Oso
  • Declarative policies: Easier to audit and reason about

What We Don't Like

  • Scattered checks: Authorisation spread throughout codebase
  • Performance impact: Fine-grained checks add overhead
  • Testing complexity: Many permission combinations

Best Practices

  1. Centralize authorisation logic: Don't scatter checks everywhere
  2. Default deny: Require explicit permission grants
  3. Audit access decisions: Log authorisation checks
  4. Test thoroughly: Cover permission edge cases
  5. Separate from authentication: Different concerns, different code
  6. Review regularly: Permissions drift over time