This GoFr example demonstrates how to implement WebSocket connections with authentication middleware. It shows how to:
- Set up Basic Authentication for your GoFr application with a custom validator
- Use WebSockets with authenticated connections
- Handle messages from authenticated clients
- Track active WebSocket connections
- Extract username from authentication credentials
- Authenticated WebSocket Connections: Only authenticated users can establish WebSocket connections
- User Tracking: The example keeps track of connected users and provides an endpoint to list them
- Custom Authentication: Uses a custom validator function to authenticate users
- Chat-like Functionality: Demonstrates a simple chat application where messages include usernames
WebSockets start as HTTP connections that are then upgraded to WebSocket protocol. In GoFr, authentication middleware is applied during the initial HTTP handshake, before the connection is upgraded to a WebSocket.
The authentication flow works as follows:
- Client sends an HTTP request with authentication credentials (e.g., Basic Auth header)
- GoFr's authentication middleware validates the credentials using the custom validator
- If authentication succeeds, the connection is upgraded to WebSocket
- If authentication fails, a 401 Unauthorized response is returned, and the WebSocket connection is not established
This example uses Basic Authentication for simplicity, but the same principle applies to other authentication methods like API Key, OAuth, or custom authentication.
To run the example, use the following command:
go run main.go
You can test the WebSocket connection using tools like websocat or browser-based WebSocket clients.
websocat ws://localhost:8000/ws -H="Authorization: Basic dXNlcjE6cGFzc3dvcmQx"
The Basic Auth header dXNlcjE6cGFzc3dvcmQx
is the base64-encoded string of user1:password1
.
You can use curl to check the list of active users:
curl -u user1:password1 http://localhost:8000/users
This will return a JSON response with the list of currently connected users.
// Function to create a WebSocket with authentication
function createAuthenticatedWebSocket(url, username, password) {
// Create a custom WebSocket object that includes authentication
return new Promise((resolve, reject) => {
// Create the WebSocket connection
const socket = new WebSocket(url);
// Add authentication headers to the connection
// Note: This is a workaround as browsers don't allow setting headers directly
// In a real application, you would use a token-based approach
// Connection opened
socket.addEventListener('open', (event) => {
console.log('Connected to WebSocket server');
resolve(socket);
});
// Connection error
socket.addEventListener('error', (event) => {
console.error('WebSocket connection error:', event);
reject(event);
});
});
}
// Usage example
async function connectToChat() {
try {
// Connect to the WebSocket server
// Note: In a real application, you would need to handle authentication differently
// as browsers don't allow setting custom headers for WebSockets
const socket = await createAuthenticatedWebSocket('ws://localhost:8000/ws', 'user1', 'password1');
// Send a message
socket.send(JSON.stringify({content: 'Hello from browser!'}));
// Listen for messages
socket.addEventListener('message', (event) => {
console.log('Message from server:', event.data);
// Display the message in the UI
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `<div>${event.data}</div>`;
});
// Set up UI for sending messages
document.getElementById('send-button').addEventListener('click', () => {
const messageInput = document.getElementById('message-input');
const message = messageInput.value;
if (message) {
socket.send(JSON.stringify({content: message}));
messageInput.value = '';
}
});
} catch (error) {
console.error('Failed to connect:', error);
}
}
// Start the connection
connectToChat();
Note: Browser WebSocket API doesn't allow setting custom headers directly. In a real application, you would typically use a token-based approach where the token is obtained via a separate authenticated HTTP request and then included in the WebSocket URL or in the first message sent after connection.
In a production environment, consider these security best practices:
- Use HTTPS/WSS instead of HTTP/WS to encrypt the connection
- Implement token-based authentication (JWT) instead of Basic Auth
- Validate user permissions for specific WebSocket actions
- Implement rate limiting to prevent abuse
- Sanitize and validate all incoming WebSocket messages
- Store user credentials securely (e.g., hashed passwords in a database)
- Implement proper session management and token expiration
This example demonstrates several key concepts:
-
Authentication Middleware: GoFr's authentication middleware is applied before the WebSocket connection is established, ensuring only authenticated users can connect.
-
Custom Validator: The example uses a custom validator function to authenticate users, which could be extended to validate against a database.
-
Username Extraction: The example extracts the username from the Basic Auth header to identify the user in the WebSocket connection.
-
Connection Tracking: The example keeps track of active connections and provides an endpoint to list them.
-
Continuous Message Handling: The WebSocket handler uses a loop to continuously process incoming messages until the connection is closed.