6dc9e4ffee
The delegated socket token (aud=IIOS_REALTIME_AUDIENCE) was only narrow in one direction: the gateway accepts ONLY that audience, but REST never checked the actor token's audience — so a leaked browser socket token still drove privileged REST, i.e. a full actor token with extra steps. RealtimeTokenRestGuard closes it, registered GLOBALLY (APP_GUARD) because every REST route is a target — only 2 of 15 controllers sit behind ContextAttestationGuard, so inbox/media/interactions/ ai/... would otherwise stay open. It is a decode-only REJECT filter, never an authenticator: - does not verify signatures (controllers still call SessionVerifier); stripping `aud` to bypass it invalidates the signature downstream, - no bearer -> pass through (health, metrics, HMAC adapter webhooks), - ws context -> pass through (the gateway enforces the mirror rule), - unset IIOS_REALTIME_AUDIENCE -> no-op, the same switch that turns on the socket half. So one env now enables the whole boundary: socket accepts only iios-message, REST refuses it. 8 tests; typecheck + build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
20 lines
705 B
TypeScript
20 lines
705 B
TypeScript
import { Global, Module } from '@nestjs/common';
|
|
import { APP_GUARD } from '@nestjs/core';
|
|
import { PLATFORM_PORTS, LocalDevPorts } from './platform-ports';
|
|
import { RealtimeTokenRestGuard } from './realtime-token.guard';
|
|
|
|
/**
|
|
* Binds the platform ports (P1: the permissive in-service default), and registers the
|
|
* realtime-delegation REST guard globally — a socket-scoped token must not drive REST on ANY
|
|
* route, so it can't be per-controller (see realtime-token.guard).
|
|
*/
|
|
@Global()
|
|
@Module({
|
|
providers: [
|
|
{ provide: PLATFORM_PORTS, useClass: LocalDevPorts },
|
|
{ provide: APP_GUARD, useClass: RealtimeTokenRestGuard },
|
|
],
|
|
exports: [PLATFORM_PORTS],
|
|
})
|
|
export class PlatformModule {}
|