214 lines
6.4 KiB
Markdown
214 lines
6.4 KiB
Markdown
# 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:**
|
|
```json
|
|
{
|
|
"nonce": "nonce_1234567890_abcdef",
|
|
"expires_at": 1234567890
|
|
}
|
|
```
|
|
|
|
### Health Check
|
|
```
|
|
GET /auth/health
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"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
|
|
1. **Request Nonce**: Client requests a nonce from `/auth/nonce`
|
|
2. **Sign Nonce**: Client signs the nonce with their private key
|
|
3. **Connect**: Client connects to WebSocket with `pubkey` and `sig` parameters
|
|
4. **Verify**: Server verifies the signature and accepts/rejects the connection
|
|
|
|
## Server Configuration
|
|
|
|
### Basic Server (No Authentication)
|
|
```rust
|
|
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
|
|
```rust
|
|
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
|
|
```javascript
|
|
// 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
|
|
```rust
|
|
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
|
|
1. **No Changes Required**: Existing clients continue to work
|
|
2. **Gradual Rollout**: Enable authentication on new servers first
|
|
3. **Client Updates**: Update clients to support authentication when ready
|
|
4. **Full Migration**: Eventually require authentication on all servers
|
|
|
|
### Testing
|
|
1. Test unauthenticated connections still work
|
|
2. Test authenticated connections with valid signatures
|
|
3. Test authentication failures are handled gracefully
|
|
4. Test nonce expiration and replay protection |