feat: enforce realtime delegation audience on the message socket

The socket used to accept any valid token, so a full REST/actor token could open the
live stream. Now it can require a narrowly-scoped delegated token (aud=iios-message),
so a leaked socket token can't drive privileged REST, and vice-versa.

- MessagePrincipal gains `audience`, surfaced from both verify paths (OIDC aud, and the
  app-token `aud` claim).
- message.gateway: when IIOS_REALTIME_AUDIENCE is set, handleConnection accepts only a
  token whose aud matches (opt-in, like IIOS_REQUIRE_ATTESTATION; unset = no change).
- spec: verifier surfaces aud; gateway accepts iios-message, rejects iios-core / no-aud
  when enforcing, passes through when off.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 12:56:19 +05:30
parent e39caa3c80
commit 3298401772
4 changed files with 106 additions and 0 deletions
@@ -8,6 +8,9 @@ export interface MessagePrincipal {
appId: string;
tenantId?: string;
displayName?: string;
/** The token's audience (`aud`). Used to enforce realtime delegation: a socket-scoped
* token (`iios-message`) must not be a full REST/actor token (`iios-core`), and vice-versa. */
audience?: string;
}
/**