So far, our chat application uses JWT for HTTP authentication and passes the token as a query parameter for WebSockets. While this identifies the user, the actual WebSocket data transfer is currently unencrypted (WS://). For production, all traffic, especially sensitive chat messages, must be encrypted using WSS (WebSocket Secure), which relies on TLS/SSL certificates. This chapter focuses on enabling WSS and reinforcing WebSocket authentication.
Purpose of this Chapter
By the end of this chapter, you will:
- Understand why WSS (TLS/SSL) is crucial for WebSocket security.
- Learn how to generate self-signed SSL certificates for local testing.
- Configure Uvicorn to serve the FastAPI application over HTTPS and WSS.
- Review and secure the WebSocket authentication flow more robustly.
Concepts Explained: WSS and TLS/SSL
TLS (Transport Layer Security), often still referred to by its older name SSL (Secure Sockets Layer), is a cryptographic protocol designed to provide communication security over a computer network.
WSS (WebSocket Secure) is essentially a WebSocket connection layered on top of a TLS/SSL encrypted connection (HTTPS). This means all data exchanged over the WebSocket is encrypted, preventing eavesdropping and tampering.
Why is it crucial?
- Confidentiality: Prevents unauthorized parties from reading chat messages.
- Integrity: Ensures messages haven’t been altered in transit.
- Authentication: Verifies the identity of the server (and optionally the client).
For local development, we’ll use self-signed certificates. These are certificates that you create and sign yourself. Browsers will typically warn you about them (as they are not trusted by a Certificate Authority), but they are sufficient for testing encryption locally. For production, you would obtain certificates from a trusted Certificate Authority (e.g., Let’s Encrypt).
Step-by-Step Tasks
1. Generate Self-Signed SSL Certificates
We’ll use openssl (available on Linux/macOS, or via Git Bash on Windows) to generate a private key and a self-signed certificate.
Open your terminal in the realtime-chat-app directory (or a certs subdirectory) and run:
# Generate a private key
openssl genrsa -out key.pem 2048
# Generate a self-signed certificate (valid for 365 days)
# For Common Name (CN), use "localhost" or "127.0.0.1" for local testing.
openssl req -new -x509 -key key.pem -out cert.pem -days 365
When prompted, fill in the details. Crucially, for “Common Name (e.g., server FQDN or YOUR name)”, enter localhost.
You should now have key.pem (your private key) and cert.pem (your certificate) in your project directory.
2. Configure Uvicorn for HTTPS/WSS
We need to tell Uvicorn to use these certificates.
Modify your uvicorn command. First, ensure your virtual environment is active:
pipenv shell
Now, run Uvicorn with SSL options:
uvicorn app.main:app --reload --ssl-keyfile key.pem --ssl-certfile cert.pem --port 8443
--ssl-keyfile key.pem: Specifies the path to your private key file.--ssl-certfile cert.pem: Specifies the path to your certificate file.--port 8443: We’ll use the standard HTTPS port8443for our secure server.
Uvicorn should start, and you’ll see messages indicating it’s running on https://127.0.0.1:8443.
3. Update Client to Use WSS and Better Auth Flow
The client.html needs to connect to wss:// instead of ws:// and the correct port. Also, for improved security, it’s generally better to connect to WebSockets after login, ensuring the token is available. We already implemented this in the previous chapter, but let’s re-verify the WebSocket connection string.
<!-- client.html (ensure this part is correct for WSS) -->
<script>
// ... (existing code for login, register, etc.) ...
function connectWebSocket(roomName) {
if (ws) {
ws.close();
}
if (!authToken) {
messagesDiv.innerHTML += "<p style='color:red;'>Please login first to get an authentication token.</p>";
return;
}
// IMPORTANT: Use wss:// and the correct port (8443)
ws = new WebSocket(`wss://localhost:8443/ws/${roomName}?token=${authToken}`);
ws.onopen = (event) => {
messagesDiv.innerHTML += `<p>Connected to room '${roomName}' as ${usernameInput.value}!</p>`;
console.log("WebSocket opened:", event);
};
// ... (rest of WebSocket event handlers) ...
}
// ... (rest of the client.html script) ...
</script>
4. Test WSS Connection and Authentication
- Start your FastAPI server with the
uvicorncommand including SSL options (--ssl-keyfile key.pem --ssl-certfile cert.pem --port 8443). - Open
client.htmlin your browser.- Expect browser warnings: Because these are self-signed certificates, your browser will likely show a warning about an untrusted connection. You’ll need to bypass this warning (e.g., “Advanced” -> “Proceed to localhost (unsafe)”). This is normal for self-signed certs.
- Register/Login: Log in with a user (e.g.,
chatuser/chatpassword). - Connect to a room: Select a room and click “Connect/Reconnect”.
- Verify connection: Check your browser’s developer console for WebSocket connection status. It should connect to
wss://without errors (after bypassing the initial browser warning). - Send messages: Send messages and verify they are broadcast to other authenticated clients in the same room.
- Verify connection: Check your browser’s developer console for WebSocket connection status. It should connect to
5. Further Authentication Considerations for WebSockets (Advanced)
While passing the JWT as a query parameter (?token=YOUR_JWT) is a common approach for WebSockets, it has security implications:
- Browser History: The token might be recorded in browser history.
- Logging: Tokens can appear in server access logs.
More secure alternatives (often requiring custom client-side code and server-side middleware):
- Subprotocol negotiation: Pass the token as part of the
Sec-WebSocket-Protocolheader during the handshake. - Custom HTTP Headers: Send custom headers during the initial HTTP upgrade request that establishes the WebSocket connection.
- Short-lived tokens and periodic re-authentication: Issue very short-lived WebSocket-specific tokens that are exchanged periodically over a secure HTTP channel.
For this guide, the query parameter approach with WSS provides a practical balance for understanding the concepts.
Tips/Challenges/Errors
- “ERR_SSL_PROTOCOL_ERROR” or “WebSocket connection to ‘wss://…’ failed”:
- Double-check that your
key.pemandcert.pemfiles are in the correct directory (or paths are correct). - Ensure Uvicorn is running with the
--ssl-keyfileand--ssl-certfileflags. - Make sure the client is trying to connect to
wss://localhost:8443/(or whatever port you configured), notws://. - Bypass the browser’s security warning about self-signed certificates.
- Double-check that your
- Permissions: Ensure the
uvicornprocess has read access tokey.pemandcert.pem. - Production Certificates: Never use self-signed certificates in production. Obtain them from a trusted Certificate Authority (e.g., Let’s Encrypt for free, or commercial CAs).
Summary/Key Takeaways
You’ve successfully secured your WebSocket communication by enabling WSS (TLS/SSL) for your FastAPI application using self-signed certificates. This crucial step encrypts all data transmitted over your chat, protecting user privacy and message integrity. You’ve also reviewed the authentication flow for WebSockets, ensuring only authenticated users can join chat rooms.
With strong security foundations in place, the next step is to prepare our application for deployment, starting with Dockerization.