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
| Level | Approach |
|---|---|
| Endpoint | Middleware checks |
| Field | Resolver/serializer filtering |
| Row | Query-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
- Centralize authorisation logic: Don't scatter checks everywhere
- Default deny: Require explicit permission grants
- Audit access decisions: Log authorisation checks
- Test thoroughly: Cover permission edge cases
- Separate from authentication: Different concerns, different code
- Review regularly: Permissions drift over time