feat: ServiceKeyGuard — n8n automation via X-Service-Key header

This commit is contained in:
admin-valentin 2026-08-26 15:49:16 +00:00
parent a8f26677a5
commit bfe9291cf5

View 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;
}
}