6.4 KiB
6.4 KiB
WebSocket Server Authentication
This document describes the optional authentication features added to the Circle WebSocket server.
Overview
The WebSocket server now supports optional secp256k1 signature-based authentication while maintaining full backward compatibility with existing clients. Authentication is completely opt-in and can be enabled per server instance.
Features
1. Optional Authentication
- Backward Compatible: Existing clients continue to work without any changes
- Opt-in: Authentication can be enabled/disabled per server instance
- Graceful Degradation: Servers can accept both authenticated and unauthenticated connections
2. Nonce-based Security
- Nonce Endpoints: REST API for requesting cryptographic nonces
- Replay Protection: Each nonce can only be used once
- Expiration: Nonces expire after 5 minutes
- Health Monitoring: Health endpoint for monitoring nonce service
3. Signature Verification
- secp256k1: Uses the same cryptographic standard as Ethereum
- Ethereum-style Signing: Compatible with eth_sign message format
- Public Key Recovery: Verifies signatures against provided public keys
API Endpoints
These HTTP API endpoints are served by the WebSocket server instance itself, on the same host and port where the WebSocket service is running.
Nonce Request
GET /auth/nonce?public_key=<optional_public_key>
Response:
{
"nonce": "nonce_1234567890_abcdef",
"expires_at": 1234567890
}
Health Check
GET /auth/health
Response:
{
"status": "healthy",
"active_nonces": 42,
"timestamp": 1234567890
}
WebSocket Authentication
Query Parameters
Clients can authenticate by including these query parameters in the WebSocket URL:
pubkey
: The client's public key in hex format (130 characters, uncompressed)sig
: The signature of the nonce in hex format (130 characters)nonce
: The nonce that was signed (optional)
Example:
ws://localhost:8080/{circle_pk}?pubkey=04abc123...&sig=def456...&nonce=nonce_123_abc
Authentication Flow
- Request Nonce: Client requests a nonce from
/auth/nonce
- Sign Nonce: Client signs the nonce with their private key
- Connect: Client connects to WebSocket with
pubkey
andsig
parameters - Verify: Server verifies the signature and accepts/rejects the connection
Server Configuration
Basic Server (No Authentication)
use circle_ws_lib::{ServerConfig, spawn_circle_server};
let config = ServerConfig::new(
"localhost".to_string(),
8080,
"redis://localhost".to_string(),
);
let (server_task, server_handle) = spawn_circle_server(config)?;
Server with Authentication
use circle_ws_lib::{ServerConfig, spawn_circle_server};
let config = ServerConfig::new(
"localhost".to_string(),
8080,
"redis://localhost".to_string(),
).with_auth();
let (server_task, server_handle) = spawn_circle_server(config)?;
Client Integration
JavaScript/TypeScript Example
// 1. Request nonce (from the WebSocket server's HTTP interface)
const nonceResponse = await fetch('http://localhost:8080/auth/nonce');
const { nonce } = await nonceResponse.json();
// 2. Sign nonce (using your preferred secp256k1 library)
const signature = signMessage(privateKey, nonce);
const publicKey = derivePublicKey(privateKey);
// 3. Connect with authentication (replace {circle_pk} with actual circle public key)
const ws = new WebSocket(
`ws://localhost:8080/${circle_pk}?pubkey=${publicKey}&sig=${signature}&nonce=${nonce}`
);
Rust Client Example
use circle_ws_lib::auth::*;
// Request nonce. NonceClient will derive the HTTP API path from this WebSocket URL.
let nonce_client = NonceClient::from_ws_url("ws://localhost:8080/{circle_pk}")?;
let nonce_response = nonce_client.request_nonce(Some(public_key)).await?;
// Sign nonce
let signature = sign_message(&private_key, &nonce_response.nonce)?;
// Connect with authentication (replace {circle_pk} with actual circle public key)
let ws_url = format!(
"ws://localhost:8080/{}?pubkey={}&sig={}",
circle_pk, public_key, signature
);
Security Considerations
Nonce Management
- Nonces expire after 5 minutes
- Each nonce can only be used once
- Nonces are stored in memory (consider Redis for production)
Signature Security
- Uses secp256k1 elliptic curve cryptography
- Ethereum-style message signing for compatibility
- Public key verification prevents impersonation
Backward Compatibility
- Unauthenticated connections are allowed by default
- No breaking changes to existing APIs
- Optional authentication can be enabled gradually
Error Handling
Authentication Errors
- 401 Unauthorized: Authentication required but not provided
- 403 Forbidden: Authentication provided but invalid
- 400 Bad Request: Malformed authentication parameters
Nonce Errors
- 404 Not Found: Nonce endpoint not available
- 410 Gone: Nonce expired or already used
- 429 Too Many Requests: Rate limiting (if implemented)
Monitoring
Metrics
- Active nonce count via
/auth/health
- Authentication success/failure rates in logs
- Connection counts by authentication status
Logging
INFO Incoming WebSocket connection for circle: 04abc123... (auth_enabled: true)
INFO Authentication successful for pubkey: 04abc123...
WARN Authentication failed: invalid signature
Production Considerations
Scalability
- Consider Redis-backed nonce storage for multiple server instances
- Implement rate limiting for nonce requests
- Monitor memory usage of in-memory nonce storage
Security
- Use HTTPS/WSS in production
- Implement proper key management
- Consider certificate-based authentication for additional security
Monitoring
- Set up alerts for authentication failure rates
- Monitor nonce service health
- Track connection patterns and anomalies
Migration Guide
Existing Deployments
- No Changes Required: Existing clients continue to work
- Gradual Rollout: Enable authentication on new servers first
- Client Updates: Update clients to support authentication when ready
- Full Migration: Eventually require authentication on all servers
Testing
- Test unauthenticated connections still work
- Test authenticated connections with valid signatures
- Test authentication failures are handled gracefully
- Test nonce expiration and replay protection