WebSocket Authentication — Complete Guide to Secure Connections
In this tutorial, you will learn about WebSocket Authentication. We cover key concepts, practical examples, and best practices to help you master this topic.
WebSocket authentication verifies client identity during the handshake using tokens, cookies, or custom headers before upgrading the HTTP connection to a persistent bidirectional stream.
What You'll Learn
- Authentication strategies for WebSocket connections
- Token-based auth during the HTTP upgrade handshake
- Re-authentication for long-lived connections
Why It Matters
Unauthenticated WebSocket connections expose real-time data to anyone who knows the endpoint URL. Authentication ensures only authorized users establish persistent connections.
Real-World Use
Durga Antivirus Pro WebSocket threat feed requires JWT tokens in the connection URL query string. The server validates the token during the upgrade handshake and drops unauthenticated connections immediately.
flowchart LR
C["Client"] -->|"ws://server/ws?token=JWT"| H["HTTP Upgrade"]
H --> V["Validate Token"]
V -->|"Valid"| U["Upgrade to WebSocket"]
V -->|"Invalid"| D["403 Forbidden"]
U --> M["Authenticated Messages"]
style V fill:#dbeafe,stroke:#2563eb
Code Examples
// Server-side token validation during handshake
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', (ws, req) => {
// Extract token from query string
const url = new URL(req.url, 'http://localhost');
const token = url.searchParams.get('token');
if (!token) {
ws.close(4001, 'Authentication required');
return;
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
ws.userId = decoded.sub;
ws.role = decoded.role;
console.log(`Authenticated user: ${ws.userId}`);
} catch (err) {
ws.close(4001, 'Invalid token');
}
});
// Server: On subsequent messages, user identity is available
server.on('connection', (ws) => {
ws.on('message', (msg) => {
console.log(`Message from user ${ws.userId}: ${msg}`);
});
});
Expected output: Client without valid token receives close code 4001; authenticated clients pass through.
// Client connecting with auth token
const WebSocket = require('ws');
async function connectWithAuth() {
const token = await getAuthToken(); // Get JWT from auth server
const ws = new WebSocket(`ws://api.example.com/ws?token=${token}`);
ws.on('open', () => {
console.log('Authenticated WebSocket connected');
});
ws.on('close', (code, reason) => {
if (code === 4001) {
console.error('Auth failed:', reason.toString());
// Redirect to login or refresh token
}
});
}
connectWithAuth();
Expected output: Client includes token in URL; connection succeeds or fails with auth error code.
# FastAPI WebSocket authentication
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException
from fastapi.security import HTTPBearer
import jwt
app = FastAPI()
security = HTTPBearer()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
token = websocket.query_params.get("token")
if not token:
await websocket.close(code=4001)
return
try:
payload = jwt.decode(token, "secret", algorithms=["HS256"])
user_id = payload["sub"]
except jwt.PyJWTError:
await websocket.close(code=4001)
return
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"User {user_id}: {data}")
except WebSocketDisconnect:
print(f"User {user_id} disconnected")
Expected output: FastAPI WebSocket authenticates via query parameter token before accepting the connection.
Common Mistakes
1. Passing Tokens in URL Query String
Query strings are logged by proxies and servers. Use cookies or Sec-WebSocket-Protocol header for sensitive tokens.
2. No Token Expiry Checking
Long-lived WebSocket connections may outlive token validity. Re-authenticate periodically or use refresh tokens.
3. Authenticating After Upgrade
Validating auth after accepting the connection wastes resources. Validate before calling accept().
4. Sending Sensitive Data in Close Frames
Close reasons are visible to JavaScript. Send generic messages (Invalid auth) instead of details (Token expired).
5. No Per-Connection Rate Limiting
Authenticated users can still flood the server. Apply per-connection rate limiting after authentication.
Practice Questions
- When does WebSocket authentication typically occur?
- Why should token validation happen before accepting the connection?
- What are three ways to pass authentication tokens to a WebSocket?
- How do you handle token expiry during a long-lived WebSocket session?
- Why should you avoid sending sensitive details in close reason codes?
Answers:
- During the HTTP upgrade handshake, before switching to the WebSocket protocol.
- To avoid allocating resources for unauthenticated connections.
- Query string parameter, cookie, or Sec-WebSocket-Protocol header.
- Send a re-authentication request frame, or use short-lived tokens with refresh tokens.
- Close reason codes and data are visible to JavaScript; revealing details helps attackers.
Challenge: Implement WebSocket authentication for a real-time chat application. Use JWT tokens in the query string, validate before upgrade, handle token expiry with a refresh mechanism, and apply per-connection rate limiting.
FAQ
Mini Project
Build a WebSocket server with multiple authentication methods: JWT in query string, cookie-based auth, and Sec-WebSocket-Protocol header. Each method validates before upgrade, logs authentication attempts, and sends close code 4001 on failure.
What's Next
Learn about WebSocket authorization for permission-based access control, or explore WebSocket security best practices for comprehensive protection.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro