feat(guild-auth): enforce unified x-api-key auth for inbound APIs
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
34
Fabric.Backend.Guild/src/common/api-key.guard.ts
Normal file
34
Fabric.Backend.Guild/src/common/api-key.guard.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@
|
||||
- [x] Webhook 事件信封落地(event_id/event_type/occurred_at/data)
|
||||
- [x] HMAC 签名与重放防护
|
||||
- [x] 出站重试队列(指数退避)
|
||||
- [ ] API Key 入站调用鉴权(不区分 Bot/人类)
|
||||
- [x] API Key 入站调用鉴权(不区分 Bot/人类)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user