# Socket Broker A WebSocket broker application that handles topic-based messaging between frontend clients and backend services with API key authentication. ## Features - **WebSocket Server** (Port 3000): Handles client connections and topic subscriptions - **HTTP API** (Port 8080): Allows backend services to publish messages to topics - **API Key Authentication**: Secure access control for publishing messages - **Topic-based messaging**: Clients subscribe to topics and receive messages when published - **Real-time communication**: Instant message delivery to all subscribed clients - **Health monitoring**: Built-in health checks and status endpoints ## Architecture ``` Frontend Clients ←→ WebSocket Server (Port 3000) ↕ Backend Services ←→ HTTP API (Port 8080) [🔐 API Key Required] ``` ## Security **⚠️ IMPORTANT**: The HTTP API endpoints that publish messages require a valid API key for authentication. This ensures that only authorized backend services can send messages to your WebSocket clients. ### API Key Configuration Set the `API_KEY` environment variable to secure your application: ```bash # Set a strong, random API key export API_KEY="your-secure-api-key-here" # Or use in docker-compose API_KEY=your-secure-api-key-here docker-compose up ``` ### Authentication Methods The API accepts the API key in three ways: 1. **Authorization Header** (Recommended): ```bash Authorization: Bearer your-api-key-here ``` 2. **X-API-Key Header**: ```bash X-API-Key: your-api-key-here ``` 3. **Query Parameter**: ```bash POST /publish?api_key=your-api-key-here ``` ## Quick Start ### Using Docker ```bash # Build the image docker build -t socket-broker . # Run the container with API key docker run -d \ --name socket-broker \ -p 3000:3000 \ -p 8080:8080 \ -e API_KEY="your-secure-api-key" \ socket-broker ``` ### Using Docker Compose ```bash # Copy environment file cp env.example .env # Edit .env and set your API key nano .env # Start the service docker-compose up -d ``` ### Using Node.js directly ```bash # Set API key export API_KEY="your-secure-api-key" # Install dependencies npm install # Start the application npm start ``` ## Environment Variables | Variable | Default | Description | | ---------- | --------------------------- | ---------------------------------------- | | `WS_PORT` | `3000` | WebSocket server port | | `API_PORT` | `8080` | HTTP API server port | | `API_KEY` | `default-api-key-change-me` | **REQUIRED**: API key for authentication | ## WebSocket API (Port 3000) ### Connection Connect to `ws://localhost:3000` using Socket.IO client. ### Events #### Subscribe to a topic ```javascript socket.emit("subscribe", "user-notifications"); socket.on("subscribed", (data) => { console.log(`Subscribed to ${data.topic}`); }); ``` #### Unsubscribe from a topic ```javascript socket.emit("unsubscribe", "user-notifications"); socket.on("unsubscribed", (data) => { console.log(`Unsubscribed from ${data.topic}`); }); ``` #### Receive messages ```javascript socket.on("message", (data) => { console.log(`Message from topic ${data.topic}:`, data.message); console.log("Data:", data.data); console.log("Timestamp:", data.timestamp); }); ``` #### Error handling ```javascript socket.on("error", (error) => { console.error("Socket error:", error.message); }); ``` ## HTTP API (Port 8080) ### Public Endpoints (No Authentication Required) #### Health Check ```bash GET /health ``` Response: ```json { "status": "healthy", "connections": 5, "topics": 3, "timestamp": "2024-01-01T12:00:00.000Z", "apiKeyConfigured": true } ``` #### List Topics ```bash GET /topics ``` Response: ```json { "topics": [ { "topic": "user-notifications", "subscribers": 2 }, { "topic": "system-alerts", "subscribers": 1 } ] } ``` #### Get Topic Info ```bash GET /topics/{topic} ``` Response: ```json { "topic": "user-notifications", "subscribers": 2 } ``` ### Protected Endpoints (API Key Required) #### Response Codes - **200 OK**: Message published successfully to subscribers - **204 No Content**: Message published successfully but no subscribers for the topic - **400 Bad Request**: Invalid request (missing topic, message, or data) - **401 Unauthorized**: Invalid or missing API key - **500 Internal Server Error**: Server error #### Publish Message ```bash POST /publish Authorization: Bearer your-api-key-here Content-Type: application/json { "topic": "user-notifications", "message": "New message received", "data": { "userId": 123, "type": "email" } } ``` Alternative endpoint: ```bash POST /publish/{topic} Authorization: Bearer your-api-key-here Content-Type: application/json { "message": "New message received", "data": { "userId": 123, "type": "email" } } ``` Response: ```json { "success": true, "message": "Message published to topic 'user-notifications'", "subscribers": 2, "payload": { "topic": "user-notifications", "message": "New message received", "data": { "userId": 123, "type": "email" }, "timestamp": "2024-01-01T12:00:00.000Z" } } ``` **Note**: If there are no subscribers for the topic, the API returns `204 No Content` instead of an error. This indicates that the message was published successfully, but there were no recipients to receive it. ### Authentication Errors If you don't provide a valid API key, you'll get: ```json { "error": "Unauthorized", "message": "API key is required. Use Authorization header or x-api-key header or api_key query parameter." } ``` ## Frontend Example (JavaScript) ```html Socket Broker Client

Socket Broker Client

Connecting...
``` ## Backend Example (Python) ```python import requests import json import os # Get API key from environment variable API_KEY = os.getenv('API_KEY', 'your-api-key-here') def publish_message(topic, message, data=None): url = "http://localhost:8080/publish" headers = { 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } payload = { "topic": topic, "message": message, "data": data } response = requests.post(url, json=payload, headers=headers) return response.json() # Example usage result = publish_message( topic="user-notifications", message="New email received", data={"userId": 123, "emailId": "abc123"} ) print(result) ``` ## Backend Example (Node.js) ```javascript const axios = require("axios"); const API_KEY = process.env.API_KEY || "your-api-key-here"; async function publishMessage(topic, message, data = null) { try { const response = await axios.post( "http://localhost:8080/publish", { topic, message, data, }, { headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", }, } ); return response.data; } catch (error) { console.error( "Error publishing message:", error.response?.data || error.message ); throw error; } } // Example usage publishMessage("user-notifications", "New notification", { userId: 123 }) .then((result) => console.log("Message published:", result)) .catch((error) => console.error("Failed to publish:", error)); ``` ## Docker Compose Example ```yaml version: "3.8" services: socket-broker: build: . ports: - "3000:3000" - "8080:8080" environment: - NODE_ENV=production - WS_PORT=3000 - API_PORT=8080 - API_KEY=${API_KEY:-default-api-key-change-me} restart: unless-stopped healthcheck: test: [ "CMD", "node", "-e", "require('http').get('http://localhost:8080/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })", ] interval: 30s timeout: 10s retries: 3 start_period: 40s env_file: - .env ``` ## Monitoring The application provides several monitoring endpoints: - `/health` - Overall health status and API key configuration status - `/topics` - List of active topics and subscriber counts - `/topics/{topic}` - Specific topic information ## Security Best Practices 1. **Generate Strong API Keys**: Use cryptographically secure random strings ```bash # Generate a secure API key openssl rand -base64 32 ``` 2. **Environment Variables**: Never hardcode API keys in your code ```bash # Good export API_KEY="$(openssl rand -base64 32)" # Bad export API_KEY="my-secret-key-123" ``` 3. **Rotate Keys Regularly**: Change your API keys periodically 4. **Limit Access**: Only share API keys with trusted backend services 5. **HTTPS in Production**: Use HTTPS/WSS in production environments 6. **Network Security**: Consider using internal networks for backend communication ## Troubleshooting ### Common Issues 1. **401 Unauthorized**: Check that you're providing the correct API key 2. **Port conflicts**: Ensure ports 3000 and 8080 are available 3. **CORS errors**: Check that the frontend origin is allowed 4. **Connection refused**: Verify the container is running and ports are exposed ### Testing Authentication Test your API key authentication: ```bash # Test without API key (should fail with 401) curl -X POST http://localhost:8080/publish \ -H "Content-Type: application/json" \ -d '{"topic":"test","message":"hello"}' # Test with correct API key but no subscribers (should succeed with 204) curl -X POST http://localhost:8080/publish \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{"topic":"test","message":"hello"}' # Test with correct API key and subscribers (should succeed with 200) curl -X POST http://localhost:8080/publish \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{"topic":"test","message":"hello"}' ``` ### Logs Check application logs for detailed information: ```bash docker logs socket-broker ``` ### Health Check Verify the application is healthy and API key is configured: ```bash curl http://localhost:8080/health ```