feat: ServiceKeyGuard — n8n automation via X-Service-Key header
This commit is contained in:
parent
a8f26677a5
commit
bfe9291cf5
1 changed files with 49 additions and 0 deletions
49
src/common/guards/service-key.guard.ts
Normal file
49
src/common/guards/service-key.guard.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import {
|
||||
CanActivate, ExecutionContext, Injectable, UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
|
||||
|
||||
/**
|
||||
* ServiceKeyGuard — accepts X-Service-Key header for automation tools (n8n, cron scripts).
|
||||
* Injects tenantId from N8N_DEFAULT_TENANT_ID env var.
|
||||
* Only activates when a valid key is present; falls through to JWT guard otherwise.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ServiceKeyGuard implements CanActivate {
|
||||
private readonly serviceKey: string;
|
||||
private readonly defaultTenantId: string;
|
||||
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly reflector: Reflector,
|
||||
) {
|
||||
this.serviceKey = config.get('N8N_SERVICE_KEY', '');
|
||||
this.defaultTenantId = config.get('N8N_DEFAULT_TENANT_ID', '');
|
||||
}
|
||||
|
||||
canActivate(ctx: ExecutionContext): boolean {
|
||||
// Skip on @Public() routes
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
ctx.getHandler(),
|
||||
ctx.getClass(),
|
||||
]);
|
||||
if (isPublic) return true;
|
||||
|
||||
if (!this.serviceKey) return false; // service key not configured → fall through
|
||||
|
||||
const req = ctx.switchToHttp().getRequest();
|
||||
const provided = req.headers['x-service-key'] ?? '';
|
||||
|
||||
if (!provided || provided !== this.serviceKey) return false;
|
||||
|
||||
if (!this.defaultTenantId) {
|
||||
throw new UnauthorizedException('N8N_DEFAULT_TENANT_ID not configured');
|
||||
}
|
||||
|
||||
// Inject synthetic session so @CurrentSession() works
|
||||
req.session = { tenantId: this.defaultTenantId, userId: 'n8n-service', isService: true };
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue