keepcalculator

06 Numbers and Powers

WebSocket Auth: Authorize Each Subscription Before Data

Card No. 06·48769 · Updated · Standard formula, shown below

This guide shows you how to enforce per-subscription authorization on a WebSocket server, so a client can only receive data for topics or channels they are allowed to see. You’ll learn a practical pattern where each subscription request is validated before any data is sent, and you’ll get code examples, common pitfalls, and troubleshooting tips.

What You’ll Need

  • A WebSocket server (Node.js with the ws library, Python with websockets, or similar)
  • A way to authenticate the initial connection (e.g., JWT, session cookie, API key)
  • An authorization rule set (e.g., user roles, permissions, or ownership checks)
  • Basic understanding of asynchronous JavaScript or Python

Step-by-Step

  1. Authenticate the WebSocket connection. When a client connects, verify their identity before allowing any subscription. Common methods: pass a token in the query string (?token=...), in the first message, or use a cookie. Reject the connection with a close code (e.g., 4401) if authentication fails.
  2. Define your subscription message format. Decide how the client requests a subscription. A JSON message like {"action": "subscribe", "channel": "orders"} works well. Document this format so clients know what to send.
  3. Parse and validate the subscription request. On receiving a subscription message, parse it and check that it has the required fields. If not, send an error message and ignore the request.
  4. Authorize the subscription. Run your authorization logic: check if the authenticated user has permission to subscribe to the requested channel. This could be a role check, an ownership check (e.g., user can only subscribe to their own order stream), or a combination. This step must happen before you add the subscription to your active subscriptions list.
  5. Store the subscription only if authorized. If authorized, add the subscription to a data structure (e.g., a map of channel to set of WebSocket connections). If not, send a permission denied message (e.g., {"error": "forbidden"}) and do not add it.
  6. Send data only to authorized subscriptions. When you publish data to a channel, iterate over the stored subscriptions for that channel and send the message to each connected client. Because you only stored authorized subscriptions, no unauthorized data leaks.
  7. Handle unsubscription. When a client sends an unsubscribe request or disconnects, remove their subscription from your data structure. This prevents sending data to dead connections.
  8. Re-evaluate authorization on reconnect. If a client disconnects and reconnects, they must go through the same authentication and authorization process. Do not assume a previous connection’s permissions still apply.

Common Mistakes

  • Authorizing only at connection time. A user might be authenticated at connection, but their permissions could change. Always check authorization for each subscription request, not just the initial handshake.
  • Sending data before subscription is confirmed. Some servers start broadcasting as soon as a client connects, without waiting for a subscription message. This can leak data the client never asked for.
  • Storing subscriptions without checking permissions. If you add the subscription to your list before checking authorization, a bug in your permission logic could send data to unauthorized users.
  • Not handling reconnection. If a client reconnects and you automatically re-subscribe them to their old channels without re-checking permissions, you might grant access they no longer have.
  • Using only a client-supplied user ID. Never trust a client to tell you who they are. Derive the user identity from your authentication token or session, not from a field in the subscription message.

Tips

  • Use a central authorization function. Write a function like canSubscribe(user, channel) that returns true/false. This keeps your logic consistent and testable.
  • Log authorization failures. Log when a subscription is denied, including the user ID and channel. This helps with debugging and security auditing.
  • Consider a subscription timeout. If a client doesn’t send a valid subscription within a few seconds, close the connection. This prevents idle connections from consuming resources.
  • Use a library with built-in support. Some WebSocket libraries (e.g., Socket.IO) have rooms or channels. You can still apply the same pattern: check authorization before joining a room.
  • Test with multiple users. Create test users with different permissions and verify that each can only receive data for their allowed channels.

Troubleshooting

Client receives data for a channel they shouldn’t see. Check if you are broadcasting to all connections instead of only those in the subscription map. Also verify that your authorization function is actually being called for each subscription.

Client never receives any data. Ensure the subscription message is being parsed correctly. Check that the client is sending the correct channel name and that the server is matching it exactly (including case sensitivity).

Authorization errors are not sent to the client. Make sure you are sending an error message back on denial. Some clients may not handle it gracefully, so test with a simple client that logs all messages.

Subscriptions are lost after a server restart. That’s expected. Clients must re-subscribe after reconnecting. Implement a reconnection flow that re-authenticates and re-subscribes.

Performance issues with many subscriptions. If you have thousands of channels, consider using a more efficient data structure (e.g., a trie or a map of channel to a Set). Also, avoid copying the entire subscription list on every publish.

Frequently Asked Questions

Can I authorize subscriptions based on the message content?

Yes, you can. For example, if a client wants to subscribe to a chat room, you can check if they are a member of that room before allowing the subscription. The same pattern applies: validate the request, check permissions, then subscribe.

What if a user’s permissions change while they are subscribed?

You should implement a mechanism to revoke subscriptions when permissions change. For example, when a user is banned from a channel, remove their subscription from your map and optionally notify them. This is often done via an admin event that triggers a server-side removal.

Is it safe to pass a token in the query string?

It’s not ideal because tokens can end up in server logs or browser history. Prefer using a cookie or a subprotocol header. If you must use a query string, ensure the connection uses TLS and the token is short-lived.

How do I handle authorization for a wildcard subscription like “all users”?

Treat wildcard subscriptions like any other channel. The authorization function should check if the user is allowed to subscribe to that wildcard pattern. For example, only admins might be allowed to subscribe to “all users”.

Should I authorize on every message or only on subscription?

Only on subscription is usually sufficient, because once subscribed, the client receives all messages for that channel. However, if your data has per-message sensitivity (e.g., a channel with mixed public/private messages), you might need to filter at the message level as well.

Can I use a JWT for both authentication and authorization?

Yes, a JWT can carry claims like roles or permissions. On subscription, decode the JWT and check the claims against the channel’s required permissions. Just remember to validate the JWT’s signature and expiration on every connection.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →