actions/transports_ws_auth_guard.ts

WebSocket auth guard — the audit-event listener that closes live connections when a row reports a revocation.

Why this exists. register_action_ws captures account_id and credential_type at upgrade time and reuses them for every message. perform_action's per-message authorization phase reloads role_grants from the DB, but session and token VALIDITY are not re-queried per message — they are re-read once, at admission (revalidate_resolved_auth) — a trade-off that keeps chatty WS connections fast. The cost: nothing in the dispatch path notices when a session is revoked or a token is rotated, so a revocation reaches an open connection only when something closes it.

Two things do. The revocation handlers close directly, after their transaction commits (queue_connection_close in actions/connection_closer.ts) — that close does not depend on the audit write. This guard repeats it when the audit row is announced, which covers a revocation emitted by code that holds no closer. Closing is idempotent, so the overlap is a repeat, not a conflict. A success row is announced only after its request's transaction commits (auth/audit_emitter.ts), so the guard's close is post-commit too.

Which rows close

The guard dispatches on the RevocationScope each audit event declares in audit_event_revocation_scopes (auth/audit_log_schema.ts), the table the SSE guard (realtime/sse_auth_guard.ts) reads too — one declaration, so the two cannot drift into closing a revoked token's socket while leaving its stream open.

role is the one scope this guard deliberately does not act on: perform_action re-authorizes every message, so a socket whose account just lost the gating role is refused at its next message, and closing it would only change when the caller learns. A stream has no later check, so the SSE guard closes there.

For standard WS endpoints mounted via AppServerOptions.ws_endpoints, create_app_server registers the guard automatically per WsEndpointSpec.auth_guard. For custom wiring, register the handler inside the consumer's audit_factory body (or via audit.add_listener(...) post-assembly).

Twin of the Rust spine's register_socket_revocation_listeners.

view source

Declarations
#

2 declarations

AuditEventHandler
#

actions/transports_ws_auth_guard.ts view source

AuditEventHandler import type {AuditEventHandler} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Audit-event callback shape — the function CreateAuditEmitterOptions.on_audit_event accepts and that create_ws_auth_guard returns.

Exported so consumers composing multiple handlers (typically create_ws_auth_guard + their own pre-existing on_audit_event) can annotate their composed callback without reaching for Parameters<typeof create_ws_auth_guard>[0].

(call)

type (event: AuditLogEvent): void

event

returns void

create_ws_auth_guard
#

actions/transports_ws_auth_guard.ts view source

(closer: ConnectionCloser, log: Logger): AuditEventHandler import {create_ws_auth_guard} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Create an audit event handler that closes connections on a successful revocation row, dispatching on the event's RevocationScope:

  • session → close_sockets_for_session(metadata.session_id)
  • token → close_sockets_for_token(metadata.token_id)
  • account → close_sockets_for_account(target_account_id ?? account_id) — logout, password_change, session_revoke_all, token_revoke_all, account_delete, account_purge
  • role → nothing, deliberately (module doc)
  • none → nothing — every other event, and any event type fuz_app does not define

Ignores outcome === 'failure' events — they carry attacker-controlled identifiers (e.g. a session_revoke that the DB rejected still records the submitted session_id), so reacting to them would let any authenticated user close another user's socket by guessing a session hash or token id.

closer

what to close on — a BackendWebsocketTransport, or a RealtimeCloser to reach every transport from one listener

log

logger for disconnect events (info level on non-zero closures)

type Logger

returns

AuditEventHandler

an on_audit_event callback suitable for create_audit_emitter's on_audit_event slot, or for registering via audit.add_listener post-assembly. The returned callback closes matching connections on closer on every relevant event.

Depends on
#

Imported by
#