WebSocket Authentication
Unlike traditional HTTP requests, WebSocket connections do not automatically include headers like cookies or authorization tokens with each message. Therefore, authentication must be explicitly handled during the initial handshake or in the first message after connection.
This guide covers common WebSocket authentication patterns and best practices.
π Why Authenticate WebSocket Connections?
Authentication ensures that:
- Only authorized users can establish a connection.
- Access to rooms, channels, or event streams can be restricted.
- Sensitive actions are protected from unauthorized execution.
- Session data can be tied to the user identity.
β Recommended Patterns
1. Token in Query Parameters (Handshake Level)
Append a token (e.g., JWT or session token) in the WebSocket URL:
Server-Side (Python/FastAPI Example):
from fastapi import WebSocket, WebSocketDisconnect
from jose import JWTError, jwt
SECRET_KEY = "your_secret"
async def get_user_from_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload.get("sub")
except JWTError:
return None
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
token = websocket.query_params.get("token")
user = await get_user_from_token(token)
if user is None:
await websocket.close(code=1008)
return
await websocket.accept()
# Proceed with authenticated user
Pros:
- Simple and works with any client.
- Token can expire or be revoked server-side.
Cons:
- Token is exposed in browser history and server logs unless using HTTPS.
2. Token in First Message (Post-Connect Auth)
Send the token as part of the first client message:
socket.onopen = () => {
socket.send(
JSON.stringify({
type: "auth",
token: "eyJhbGciOiJIUz...",
})
);
};
Server-Side Pattern:
async def receive_auth_message(websocket: WebSocket):
data = await websocket.receive_json()
if data.get("type") != "auth":
await websocket.close(code=1008)
return None
return await get_user_from_token(data.get("token"))
Pros:
- More secure than exposing the token in the URL.
- Works well for complex flows (e.g., re-auth, session refresh).
Cons:
- Requires server to track state before authentication is complete.
- Some frameworks donβt support reading messages before
accept()
.
3. Custom Headers (Limited Support)
Most browsers do not support sending custom headers in WebSocket connections due to security restrictions. However, some native clients (e.g., mobile apps or Python scripts) can send headers like:
import websockets
headers = {
"Authorization": "Bearer eyJhbGciOiJIUz..."
}
async with websockets.connect("wss://example.com/ws", extra_headers=headers):
...
Use this only for non-browser clients.
π Handling Reconnection and Expired Tokens
Ensure your backend:
- Rejects expired or invalid tokens with code
1008
(Policy Violation). - Allows clients to re-authenticate by sending a new token if needed.
- Uses short-lived tokens and refresh tokens securely.
π WebSocket Close Codes for Auth Failures
Use meaningful close codes when rejecting clients:
Code | Name | Meaning |
---|---|---|
1008 |
Policy Violation | Invalid/missing token |
4001 |
Custom Code | Token expired |
4003 |
Custom Code | Unauthorized access |
Clients should handle these and trigger appropriate UI responses or retry logic.
π Example Client Implementation (JavaScript)
const token = getAuthToken(); // e.g., from localStorage
const socket = new WebSocket(`wss://api.example.com/ws?token=${token}`);
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle events
};
socket.onclose = (event) => {
if (event.code === 1008) {
alert("Authentication failed. Please log in again.");
}
};
β Best Practices
- Always use HTTPS/WSS to prevent token sniffing.
- Use JWTs for stateless authentication.
- Expire tokens and handle renewal.
- Close unauthorized connections early.
- Log authentication failures for auditing.
π Next Steps
- Message Format: Structure your messages for security and clarity.
- Clients: Implement client-side reconnection and authentication.
- FastAPI Integration: Add full support to your backend framework.