At Amlaki (amlakire.com), we have 6 user types on the same platform. How do we build flexible, precise authorization?
The Challenge
Each user type has different permissions. Agency manages everything. Independent owner manages only their properties. Linked owner is read-only. Tenant sees only their data.
The Solution: Custom Guards + Decorators
1. Permission Decorator
import { SetMetadata } from '@nestjs/common';export const RequirePermissions = (...permissions: string[]) =>
SetMetadata('permissions', permissions);
2. Permissions Guard
@Injectable()export class PermissionsGuard implements CanActivate {
async canActivate(context: ExecutionContext) {
const permissions = this.reflector.get(
'permissions', context.getHandler()
);
const { user } = context.switchToHttp().getRequest();
// Full access
if (['SUPER_ADMIN', 'AGENCY_ADMIN'].includes(user.userType))
return true;
// Owner = full on own properties
if (user.userType === 'OWNER') {
const allowed = ['properties.*', 'units.*', 'tenants.*',
'contracts.*', 'payments.*', 'reports.view'];
return permissions.every(p =>
allowed.some(a => this.matches(p, a)));
}
// Linked owner = read only
if (user.userType === 'LINKED_OWNER')
return permissions.every(p => p.endsWith('.view'));
// Staff = role-based
if (user.userType === 'AGENCY_STAFF')
return this.staffService.hasPermission(user.sub, permissions);
return false;
}
}
3. Controller Usage
@Controller('properties')@UseGuards(JwtAuthGuard, PermissionsGuard)
export class PropertiesController {
@Post() @RequirePermissions('properties.create') create() { /* Owner + Agency only */ }
@Get() @RequirePermissions('properties.view') findAll() { /* Everyone except tenant */ }
}
4. Data Isolation in Service
async findAll(user: UserPayload) {
const where: any = {};
if (user.userType === 'AGENCY_ADMIN')
where.agencyId = user.agencyId;
else if (user.userType === 'OWNER')
where.owner = { userId: user.sub };
return this.prisma.property.findMany({ where });
}
Result
- 6 user types on the same codebase
- Each sees only what they're authorized to
- Permissions checked at two layers: Guard (HTTP) + Service (Data)
Built at amlakire.com
Top comments (0)