exguard-backend
v1.0.40
Published
ExGuard backend SDK for user role and permission validation
Maintainers
Readme
ExGuard Backend SDK
Simple RBAC/ABAC permission guard for NestJS.
Installation
npm install exguard-backendQuick Setup
1. Copy Guard Files
Create src/exguard/exguard.guard.ts:
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ExGuardBackend } from 'exguard-backend';
export const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';
export const EXGUARD_ROLES_KEY = 'exguard_roles';
@Injectable()
export class ExGuardPermissionGuard implements CanActivate {
constructor(
@Optional() @Inject('EXGUARD_INSTANCE') private exGuard: ExGuardBackend,
private reflector: Reflector,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractToken(request);
if (!token) throw new UnauthorizedException('No token provided');
if (!this.exGuard) return true;
const authResult = await this.exGuard.authenticate({ token, request });
if (!authResult.allowed) throw new ForbiddenException(authResult.error || 'Access denied');
if (!authResult.user) throw new ForbiddenException('User not found');
const handler = context.getHandler();
const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
if (permMeta) {
const { permissions, requireAll } = permMeta;
const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];
if (requireAll) {
if (!permissions.every(p => userPermissions.includes(p))) {
throw new ForbiddenException('Insufficient permissions');
}
} else {
if (!permissions.some(p => userPermissions.includes(p))) {
throw new ForbiddenException('Insufficient permissions');
}
}
}
request.user = authResult.user;
return true;
}
private extractToken(request: any): string | null {
const auth = request.headers?.authorization;
return auth?.startsWith('Bearer ') ? auth.substring(7) : request.headers?.['x-access-token'] || null;
}
}
export function RequirePermissions(permissions: string[], requireAll = false) {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
};
}Create src/exguard/exguard.module.ts:
import { Module, Global, DynamicModule } from '@nestjs/common';
import { ExGuardBackend } from 'exguard-backend';
@Global()
@Module({})
export class ExGuardModule {
static forRoot(options: { baseUrl: string; apiKey: string; cache?: { enabled?: boolean; ttl?: number } }): DynamicModule {
const exGuard = new ExGuardBackend({
baseUrl: options.baseUrl,
apiKey: options.apiKey,
cache: options.cache || { enabled: true, ttl: 300000 },
});
return {
module: ExGuardModule,
providers: [
{ provide: 'EXGUARD_INSTANCE', useValue: exGuard },
],
exports: ['EXGUARD_INSTANCE'],
};
}
}2. Configure AppModule
import { Module } from '@nestjs/common';
import { ExGuardModule } from './exguard/exguard.module';
@Module({
imports: [
ExGuardModule.forRoot({
baseUrl: 'https://api.exguard.com',
apiKey: process.env.EXGUARD_API_KEY,
cache: { enabled: true, ttl: 300000 },
}),
],
})
export class AppModule {}3. Use in Controllers
import { Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ExGuardPermissionGuard, RequirePermissions } from '@/exguard/exguard.guard';
@Controller('items')
@UseGuards(ExGuardPermissionGuard)
export class ItemsController {
@Get()
@RequirePermissions(['item:read'])
findAll() { }
@Post()
@RequirePermissions(['item:create'])
create() { }
@Get('drafts')
@RequirePermissions(['item:read_draft', 'item:admin']) // ANY of these
findDrafts() { }
@Delete(':id')
@RequirePermissions(['item:delete', 'admin'], true) // ALL of these
delete() { }
}Token Format
The guard extracts token from:
Authorization: Bearer <token>headerx-access-tokenheader
Configuration
| Option | Type | Default | Description | |--------|------|---------|-------------| | baseUrl | string | required | ExGuard API URL | | apiKey | string | required | Your API key | | cache.enabled | boolean | true | Enable caching | | cache.ttl | number | 300000 | Cache TTL in ms (5 min) |
Express/Fastify (Non-NestJS)
import { createExGuardExpress } from 'exguard-backend';
const guard = createExGuardExpress({
baseUrl: 'https://api.exguard.com',
apiKey: process.env.EXGUARD_API_KEY,
});
// Use as middleware
app.use('/api', guard.requirePermissions(['item:read']));License
MIT
