A WebSocket begins with an HTTP handshake, then carries application messages over a connection that can outlive the session state checked at upgrade. The protocol does not supply application authentication or record authorization. A browser may send cookies during its handshake, so accepting any origin can let another site open a credentialed connection. An allowed origin is one check, not identity proof: nonbrowser clients can forge that field. After a connection is accepted, every subscription and mutation still needs current permission on its target. The server must define how session expiry, logout, and role changes close or downgrade active channels.
WebSocket Handshake, Session, and Channel Authorization
Working case
Reviewer 47 opens a live case board for organization 6 and subscribes to case 29. A second page from an unrelated origin tries to open the same socket while the reviewer is signed in. The server rejects its browser origin at upgrade. Later an attacker sends a subscribe message for case 62 in organization 9 over a valid connection; the server denies that channel even though the connection was authenticated. An administrator removes reviewer 47 from case 29 while the socket remains open. The service stops delivering future case events and closes that subscription under the stated revocation bound.
Implementation boundary
function mayJoinCase(actor, caseRecord) {
return actor.tenantId === caseRecord.tenantId &&
caseRecord.reviewerIds.includes(actor.userId);
}
console.log(mayJoinCase({ tenantId: 6, userId: 47 }, { tenantId: 9, reviewerIds: [47] }));
// Output: falseRequire a secure transport and an explicit browser-origin allowlist for cookie-authenticated sockets. Validate the session at upgrade and bind the resulting actor to server-owned connection state; do not accept account or tenant fields in later messages as authority. If using a connection ticket, make it short-lived, narrowly scoped, and single-use where appropriate, and keep it out of routine URLs and logs. Parse each message under a versioned schema with length and rate limits. Authorize subscribe, unsubscribe, and mutation actions against current record state. Tie active channel membership to permission changes or periodically recheck it within a documented maximum delay. Close all connections on logout or session invalidation. Preserve normal HTTP routes for durable mutations when they already have a clear retry and audit contract.
Cost and boundaries
A long-lived socket consumes connection slots, memory for subscriptions, and heartbeat traffic even when no case changes. With U users subscribed to C channels, naive membership storage can approach O(U × C); indexed channel sets and bounded subscription counts reduce waste. Rechecking authorization on every event adds reads, while a permission-version cache reduces work but extends revocation delay. Message parsing costs O(B) for B received bytes and must have a size cap. Measure active connections, subscriptions per actor, denied channel attempts, revocation-to-last-event delay, abnormal closes, and memory per connection. Avoid logging full private message payloads.
Failure trace
Connect from an unauthorized browser origin with a valid cookie; reject it. Connect from a nonbrowser client that spoofs an allowed Origin but lacks a valid session; reject it. Subscribe to case 62 across tenant scope and confirm no title or event count leaks. Sign out in another tab and send another message on the old socket; deny and close. Change reviewer assignment while the connection is quiet, then publish a private event and verify the channel has been removed. Send an oversized or malformed frame and close only that client without starving others.
Verification
- Upgrade checks both browser origin and authenticated session.
- Every private channel uses current object permission.
- Logout and revocation stop future private events within a measured bound.
Practice drill
Open 47 simultaneous reviewer connections, each with one authorized case channel. Attempt 63 cross-tenant subscriptions and record denials without logging case notes. Revoke reviewer 47, then measure time until case 29 events stop. Run origin-spoof, missing-session, expired-ticket, and malformed-message cases. Confirm the fallback case page still obtains current state over an authorized HTTP request after a socket closes. Report both connection and per-message policy decisions.
Decision note
Authentication at upgrade starts a connection; current record permission governs every private channel and action afterward.
Common Mistakes
- Assuming an allowed Origin proves user identity.
- Authorizing the connection once and trusting all later channel names.
- Leaving old sockets active after session revocation.
Related lessons
Realtime Connection and Event Delivery; Event Sequence, Replay, and Gap Reconciliation; Socket Fanout, Backpressure, and Slow Consumers; Presence Expiry, Heartbeats, and Connection Drain; Server-Sent Events or WebSocket for Live Views; Permission Revocation and Active Sessions.
Apply and check
Build Project: live permit case board and review Web Development: realtime delivery contracts quiz.
