feat(guild-auth): enforce unified x-api-key auth for inbound APIs

This commit is contained in:
nav
2026-05-12 11:32:25 +00:00
parent 11aa538793
commit b014767324
4 changed files with 46 additions and 1 deletions

View File

@@ -10,6 +10,9 @@ DB_NAME=fabric_guild
DB_SYNC=true
DB_LOGGING=false
# Unified inbound API auth
FABRIC_API_KEY=change-me-api-key
# Guild identity
GUILD_NODE_ID=guild-node-1
GUILD_NODE_NAME=Guild Node 1

View File

@@ -1,7 +1,9 @@
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { buildTypeOrmConfig } from './database.config';
import { HealthController } from './common/health.controller';
import { ApiKeyGuard } from './common/api-key.guard';
import { GuildsModule } from './guilds/guilds.module';
import { ChannelsModule } from './channels/channels.module';
import { MessagingModule } from './messaging/messaging.module';
@@ -16,5 +18,11 @@ import { EventsModule } from './events/events.module';
MessagingModule,
],
controllers: [HealthController],
providers: [
{
provide: APP_GUARD,
useClass: ApiKeyGuard,
},
],
})
export class AppModule {}

View File

@@ -0,0 +1,34 @@
import {
CanActivate,
ExecutionContext,
Injectable,
ServiceUnavailableException,
UnauthorizedException,
} from '@nestjs/common';
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest<{ path?: string; headers: Record<string, string | string[] | undefined> }>();
const path = req.path ?? '';
// allow health check without auth
if (path.endsWith('/healthz') || path === '/healthz') {
return true;
}
const expected = process.env.FABRIC_API_KEY;
if (!expected || expected.trim() === '') {
throw new ServiceUnavailableException('FABRIC_API_KEY is not configured');
}
const received = req.headers['x-api-key'];
const receivedValue = Array.isArray(received) ? received[0] : received;
if (!receivedValue || receivedValue !== expected) {
throw new UnauthorizedException('invalid api key');
}
return true;
}
}