WebSocket Authorization — Complete Guide to Access Control
In this tutorial, you will learn about Websocket Authorization. We cover key concepts, practical examples, and best practices to help you master this topic.
WebSocket authorization controls which channels and actions an authenticated user can access, using role-based permissions and channel-level access control for real-time applications.
What You'll Learn
- Authorization models for WebSocket connections
- Channel-level permissions and subscription control
- Implementing role-based access for real-time data
Why It Matters
Authentication identifies the user; authorization controls what they can do. Without authorization, any authenticated user can access any real-time data channel.
Real-World Use
Durga Antivirus Pro WebSocket threat feed has three authorization levels: admins receive all threat types, analysts receive threat alerts, and viewers receive only summary notifications. Authorization is checked per message channel.
flowchart LR
A["Authenticated"] --> P["Permission Check"]
P -->|"Admin"| C1["All Channels"]
P -->|"Analyst"| C2["Threat Alerts"]
P -->|"Viewer"| C3["Summary Only"]
P -->|"None"| D["Access Denied"]
style P fill:#dbeafe,stroke:#2563eb
Code Examples
// WebSocket authorization middleware
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
const CHANNEL_PERMISSIONS = {
'threats:all': ['admin'],
'threats:critical': ['admin', 'analyst'],
'threats:summary': ['admin', 'analyst', 'viewer'],
'system:admin': ['admin'],
};
function authorizeChannel(user, channel) {
const allowedRoles = CHANNEL_PERMISSIONS[channel];
if (!allowedRoles) return false;
return allowedRoles.includes(user.role);
}
server.on('connection', (ws, req) => {
// User identity set during authentication (see auth lesson)
const user = ws.user;
ws.on('message', (message) => {
const { action, channel, data } = JSON.parse(message);
if (action === 'subscribe') {
if (!authorizeChannel(user, channel)) {
ws.send(JSON.stringify({
type: 'error',
message: `Not authorized for channel: ${channel}`
}));
return;
}
ws.subscribedChannels = ws.subscribedChannels || [];
ws.subscribedChannels.push(channel);
ws.send(JSON.stringify({ type: 'subscribed', channel }));
}
if (action === 'publish') {
if (!authorizeChannel(user, channel)) {
ws.send(JSON.stringify({
type: 'error',
message: `Not authorized to publish to: ${channel}`
}));
return;
}
broadcast(channel, data);
}
});
});
Expected output: Users can only subscribe to channels their role permits; unauthorized subscriptions receive error.
# Python WebSocket channel authorization
import asyncio
import json
import websockets
CHANNEL_PERMISSIONS = {
'alerts': {'read': ['admin', 'analyst'], 'write': ['admin']},
'metrics': {'read': ['admin', 'analyst', 'viewer'], 'write': ['admin']},
'admin': {'read': ['admin'], 'write': ['admin']},
}
class AuthorizedWebSocket:
def __init__(self, websocket, user):
self.ws = websocket
self.user = user
self.subscriptions = set()
async def handle_subscribe(self, channel):
perms = CHANNEL_PERMISSIONS.get(channel, {})
if self.user['role'] not in perms.get('read', []):
await self.ws.send(json.dumps({
'type': 'error',
'channel': channel,
'message': 'Read access denied'
}))
return False
self.subscriptions.add(channel)
await self.ws.send(json.dumps({'type': 'subscribed', 'channel': channel}))
return True
async def handle_publish(self, channel, data):
perms = CHANNEL_PERMISSIONS.get(channel, {})
if self.user['role'] not in perms.get('write', []):
await self.ws.send(json.dumps({
'type': 'error',
'channel': channel,
'message': 'Write access denied'
}))
return False
await broadcast(channel, data)
return True
Expected output: Channel-based authorization controls read and write access per role.
// Permission-based action authorization
class WebSocketAuthorizer {
constructor() {
this.permissions = {
'send_message': ['user', 'moderator', 'admin'],
'delete_message': ['moderator', 'admin'],
'ban_user': ['admin'],
'view_analytics': ['admin'],
};
}
canPerform(user, action) {
const allowedRoles = this.permissions[action];
if (!allowedRoles) return false;
return allowedRoles.includes(user.role);
}
authorizeAction(user, action, callback) {
if (!this.canPerform(user, action)) {
callback({ error: 'Insufficient permissions', action });
return false;
}
return true;
}
}
const authorizer = new WebSocketAuthorizer();
ws.on('message', (msg) => {
const { action, payload } = JSON.parse(msg);
authorizer.authorizeAction(ws.user, action, (error) => {
ws.send(JSON.stringify(error));
}) && handleAction(action, payload);
});
Expected output: Each action has role-based permissions; unauthorized actions are rejected with error.
Common Mistakes
1. Checking Authorization Only at Connection Time
User roles can change mid-session. Re-check authorization periodically or on sensitive operations.
2. No Channel-Level Granularity
All-or-nothing authorization forces users to choose between too much access or no access at all.
3. Exposing Channel Names Without Authorization
Listing all channels to an unauthenticated user reveals the system topology.
4. Caching Permissions Too Long
If roles change, cached permissions allow unauthorized access. Cache with short TTLs or invalidate on change.
5. Authorization Errors in Production Logs
Logging authorization decisions for debugging is fine but do not expose permission structure in error messages sent to users.
Practice Questions
- What is the difference between WebSocket authentication and authorization?
- Why should authorization be checked per-channel rather than once per connection?
- How do you handle role changes during an active WebSocket session?
- Why should channel names not be exposed to unauthorized users?
- What is the problem with caching permissions too long?
Answers:
- Authentication verifies identity; authorization controls what the identified user can access.
- Users may have different permissions for different channels; per-channel check enables fine-grained control.
- Listen for role change events from the auth service and update permissions mid-session.
- Channel names reveal system capabilities and topology, aiding attack reconnaissance.
- Outdated cached permissions allow unauthorized access after role changes.
Challenge: Implement channel-level authorization for a real-time collaboration app. Three roles (admin, editor, viewer) with read/write permissions per document channel. Handle role changes mid-session.
FAQ
Mini Project
Build a WebSocket authorization system for a real-time dashboard with channels: metrics (all roles), alerts (admin, analyst), admin (admin only). Implement per-action permissions (subscribe, publish) and role change handling mid-session.
What's Next
Learn about WebSocket authentication for verifying identity, or explore WebSocket rate limiting for controlling connection behavior.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro