Real-time WebSocket service with JWT validation via Traefik API Gateway.
- ✅ Automatic JWT validation via Traefik ForwardAuth
- ✅ User information injection from Identity service
- ✅ User-based connection logging
- ✅ Lightweight, database-free architecture
- ✅ Hot-reload support (Air)
Client → Traefik Gateway → Identity Service (JWT Validate) → WebSocket Service
↓
Injects: User-ID, User-Email, User-Name headers
# Start shared infrastructure (RabbitMQ + Traefik)
cd infra/docker
docker compose up -d
# Start Identity service
cd identity
docker compose --profile dev up -dcd websocket
docker compose --profile dev up// 1. Get token from Identity service
const loginResponse = await fetch('http://localhost:9191/identity/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: '[email protected]',
password: 'password123'
})
});
const { accessToken } = await loginResponse.json();
// 2. Establish WebSocket connection with Authorization header
// NOTE: Standard WebSocket API doesn't support custom headers
// Use query string, subprotocol, or send token in first message
const ws = new WebSocket('ws://localhost:9191/ws');
ws.onopen = () => {
console.log('Connected to WebSocket');
ws.send(JSON.stringify({ type: 'auth', token: accessToken }));
};
ws.onmessage = (event) => {
console.log('Received:', event.data);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected');
};# 1. Get token
TOKEN=$(curl -X POST http://localhost:9191/identity/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"password123"}' \
| jq -r '.accessToken')
# 2. Test WebSocket connection
curl -i -N -H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
-H "Authorization: Bearer $TOKEN" \
http://localhost:9191/ws- New Request → WebSocket Request
- URL:
ws://localhost:9191/ws - Add to Headers tab:
Authorization: Bearer YOUR_TOKEN_HERE
- Click Connect
- Send messages
- ✅ Traefik
identity-jwt-validatemiddleware validates JWT - ✅ Identity service validates token
- ✅ On success, injects
User-ID,User-Email,User-Nameheaders - ✅ WebSocket service rejects connection if
User-IDheader is missing
⚠️ Messages are NOT re-validated⚠️ Connection persists even if token expires- ℹ️ For high security: Implement message-level token validation
Every connection and message is logged with user context:
WebSocket connection established - User: John Doe ([email protected]) - ID: 123
Message from John Doe (123): Hello World
WebSocket connection closed for user John Doe (123)
.env file:
PORT=8080 # WebSocket server port
HOST_PORT=8083 # Docker host port mapping
JWT_SECRET=... # Not used (Identity service validates)# Production build
docker compose --profile prod up -d
# View logs
docker compose logs -f websocket
# Stop service
docker compose down- Is Identity service running? →
docker ps | grep identity - Is token valid? → Decode at
jwt.io - Is token expired? → Check
expclaim
- Is Traefik running? →
curl http://localhost:9191 - Is websocket-router defined in gateway.yaml? →
cat infra/docker/traefik/gateway.yaml | grep websocket - Is WebSocket service running? →
docker ps | grep websocket
- Is WebSocket service on Traefik network? →
docker network inspect auction-traefik-network
# Development mode automatically uses Air
docker compose --profile dev up
# Code changes trigger automatic rebuildPORT=8080 JWT_SECRET=your-secret-key go run main.go| Endpoint | Method | Auth Required | Description |
|---|---|---|---|
/ws |
WS | ✅ Yes (Bearer Token) | WebSocket connection |
- Message-level token validation
- Connection pooling and session management
- Redis pub/sub integration (for multiple instances)
- Rate limiting
- Heartbeat/ping-pong mechanism
- Reconnection logic (client-side)