feat(service): P2.3 Socket.io /message gateway + real SessionVerifier
open_thread (create-or-join) / send_message / read / delivered / typing; HS256 token verified on connect (SessionVerifier reuses AppTokenVerifier pattern); OutboxBus bridge propagates REST/ingest messages to rooms (dedup vs inline emit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import type { MessagePrincipal } from '../messaging/message.service';
|
||||
|
||||
/**
|
||||
* The real `session` port for P2: verifies a host app's HS256 token (reuses the
|
||||
* support-service AppTokenVerifier pattern). Per-app secrets come from the
|
||||
* APP_SECRETS env (JSON map keyed by appId). Expected claims: { sub, name?,
|
||||
* appId, orgId?, tenantId? }.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SessionVerifier {
|
||||
private readonly secrets: Record<string, string>;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
this.secrets = JSON.parse(process.env.APP_SECRETS ?? '{}');
|
||||
} catch {
|
||||
this.secrets = {};
|
||||
}
|
||||
}
|
||||
|
||||
verify(token: string): MessagePrincipal {
|
||||
const decoded = jwt.decode(token) as jwt.JwtPayload | null;
|
||||
const appId = decoded?.appId ? String(decoded.appId) : undefined;
|
||||
if (!appId) throw new UnauthorizedException('missing appId claim');
|
||||
const secret = this.secrets[appId];
|
||||
if (!secret) throw new UnauthorizedException(`unknown app: ${appId}`);
|
||||
|
||||
let payload: jwt.JwtPayload;
|
||||
try {
|
||||
payload = jwt.verify(token, secret, { algorithms: ['HS256'] }) as jwt.JwtPayload;
|
||||
} catch {
|
||||
throw new UnauthorizedException('invalid app token');
|
||||
}
|
||||
if (!payload.sub) throw new UnauthorizedException('missing sub claim');
|
||||
|
||||
return {
|
||||
userId: String(payload.sub),
|
||||
appId,
|
||||
orgId: payload.orgId ? String(payload.orgId) : `org_${appId}`,
|
||||
tenantId: payload.tenantId ? String(payload.tenantId) : undefined,
|
||||
displayName: payload.name ? String(payload.name) : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user