Authorization
Learn how to check permissions and control access in your application.
Permission Checking
Check Permission
The most common way to authorize users:
const canCreatePost = await guard.authorize.checkPermission(
userId,
'create',
'posts',
);
if (canCreatePost) {
// User can create posts
} else {
// User cannot create posts
}Check Role
Check if a user has a specific role:
const isAdmin = await guard.authorize.checkRole(userId, 'admin');
if (isAdmin) {
// User has admin role
}Authorization Patterns
Middleware Pattern
async function authorize(
userId: string,
action: string,
resource: string,
): Promise<void> {
const hasPermission = await guard.authorize.checkPermission(
userId,
action,
resource,
);
if (!hasPermission) {
throw new Error(`Unauthorized: ${action} ${resource}`);
}
}
// Usage
await authorize(userId, 'delete', 'posts');
// Proceed with deletionGuard Decorator Pattern
function Authorize(action: string, resource: string) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const userId = args[0]; // Assuming first arg is userId
const hasPermission = await guard.authorize.checkPermission(
userId,
action,
resource,
);
if (!hasPermission) {
throw new Error('Unauthorized');
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
// Usage
class PostService {
@Authorize('delete', 'posts')
async deletePost(userId: string, postId: string) {
// Delete logic
}
}Policy Pattern
interface Policy {
canPerform(userId: string): Promise<boolean>;
}
class PostPolicy {
constructor(
private guard: SequelizeGuard,
private post: any,
) {}
async canView(userId: string): Promise<boolean> {
// Public posts can be viewed by anyone
if (this.post.isPublic) return true;
// Author can always view
if (this.post.authorId === userId) return true;
// Check permission
return await this.guard.authorize.checkPermission(userId, 'read', 'posts');
}
async canEdit(userId: string): Promise<boolean> {
// Must be author or have update permission
const isAuthor = this.post.authorId === userId;
const hasPermission = await this.guard.authorize.checkPermission(
userId,
'update',
'posts',
);
return isAuthor || hasPermission;
}
async canDelete(userId: string): Promise<boolean> {
// Must be admin or author
const isAdmin = await this.guard.authorize.checkRole(userId, 'admin');
const isAuthor = this.post.authorId === userId;
return isAdmin || isAuthor;
}
}
// Usage
const policy = new PostPolicy(guard, post);
if (await policy.canDelete(userId)) {
await post.destroy();
}Resource Ownership
Check Ownership
async function canAccessResource(
userId: string,
resource: any,
action: string,
): Promise<boolean> {
// Owner can always access their resources
if (resource.userId === userId) {
return true;
}
// Check if user is admin
const isAdmin = await guard.authorize.checkRole(userId, 'admin');
if (isAdmin) {
return true;
}
// Check specific permission
return await guard.authorize.checkPermission(userId, action, resource.type);
}Ownership + Permission
async function authorizeWithOwnership(
userId: string,
resource: any,
action: string,
): Promise<boolean> {
// Check permission first
const hasPermission = await guard.authorize.checkPermission(
userId,
action,
resource.type,
);
if (!hasPermission) {
return false;
}
// If updating or deleting, must be owner or admin
if (action === 'update' || action === 'delete') {
const isOwner = resource.userId === userId;
const isAdmin = await guard.authorize.checkRole(userId, 'admin');
return isOwner || isAdmin;
}
return true;
}Hierarchical Authorization
Role Hierarchy
const roleHierarchy = {
superadmin: 100,
admin: 80,
moderator: 60,
editor: 40,
user: 20,
guest: 10,
};
async function canAccessByHierarchy(
userId: string,
requiredRole: string,
): Promise<boolean> {
const user = await guard.users.getUserWithRoles(userId);
const requiredLevel = roleHierarchy[requiredRole] || 0;
for (const role of user.roles) {
const userLevel = roleHierarchy[role.name] || 0;
if (userLevel >= requiredLevel) {
return true;
}
}
return false;
}Permission Inheritance
async function checkPermissionWithInheritance(
userId: string,
action: string,
resource: string,
): Promise<boolean> {
// Check exact permission
const hasExact = await guard.authorize.checkPermission(
userId,
action,
resource,
);
if (hasExact) return true;
// Check wildcard permissions
const hasWildcardAction = await guard.authorize.checkPermission(
userId,
'*',
resource,
);
if (hasWildcardAction) return true;
const hasWildcardResource = await guard.authorize.checkPermission(
userId,
action,
'*',
);
return hasWildcardResource;
}Complex Authorization
Multiple Permission Check
async function requireAllPermissions(
userId: string,
permissions: Array<{ action: string; resource: string }>,
): Promise<boolean> {
const checks = await Promise.all(
permissions.map((p) =>
guard.authorize.checkPermission(userId, p.action, p.resource),
),
);
return checks.every((check) => check === true);
}
async function requireAnyPermission(
userId: string,
permissions: Array<{ action: string; resource: string }>,
): Promise<boolean> {
const checks = await Promise.all(
permissions.map((p) =>
guard.authorize.checkPermission(userId, p.action, p.resource),
),
);
return checks.some((check) => check === true);
}
// Usage
const canManage = await requireAllPermissions(userId, [
{ action: 'create', resource: 'posts' },
{ action: 'update', resource: 'posts' },
{ action: 'delete', resource: 'posts' },
]);Conditional Authorization
async function authorizeWithConditions(
userId: string,
action: string,
resource: any,
conditions: Record<string, any>,
): Promise<boolean> {
// Check base permission
const hasPermission = await guard.authorize.checkPermission(
userId,
action,
resource.type,
);
if (!hasPermission) return false;
// Apply additional conditions
if (conditions.mustBeOwner && resource.userId !== userId) {
return false;
}
if (conditions.mustBePublished && !resource.isPublished) {
const isAdmin = await guard.authorize.checkRole(userId, 'admin');
if (!isAdmin) return false;
}
if (conditions.requiredRole) {
const hasRole = await guard.authorize.checkRole(
userId,
conditions.requiredRole,
);
if (!hasRole) return false;
}
return true;
}Authorization Utilities
Permission Checker Class
class PermissionChecker {
constructor(
private guard: SequelizeGuard,
private userId: string,
) {}
async can(action: string, resource: string): Promise<boolean> {
return this.guard.authorize.checkPermission(this.userId, action, resource);
}
async hasRole(roleName: string): Promise<boolean> {
return this.guard.authorize.checkRole(this.userId, roleName);
}
async canAll(
permissions: Array<{ action: string; resource: string }>,
): Promise<boolean> {
const checks = await Promise.all(
permissions.map((p) => this.can(p.action, p.resource)),
);
return checks.every((check) => check);
}
async canAny(
permissions: Array<{ action: string; resource: string }>,
): Promise<boolean> {
const checks = await Promise.all(
permissions.map((p) => this.can(p.action, p.resource)),
);
return checks.some((check) => check);
}
async isAdmin(): Promise<boolean> {
return this.hasRole('admin');
}
}
// Usage
const checker = new PermissionChecker(guard, userId);
if (await checker.can('delete', 'posts')) {
// Delete post
}
if (await checker.isAdmin()) {
// Admin actions
}Best Practices
- Check Early: Authorize as early as possible in your request flow
- Fail Secure: Deny access by default
- Cache Results: Use caching for frequently checked permissions
- Log Denials: Log authorization failures for security audits
- Clear Errors: Provide clear error messages for debugging
- Test Thoroughly: Test all authorization paths