WebSocket Authentication
Overview
The Cadenza WebSocket API uses OAuth2 authentication with Supabase Auth as the Identity Provider (IdP). Clients must obtain a valid access token from Supabase before connecting to the WebSocket API.
Architecture
Identity Provider
Supabase Auth serves as the centralized identity provider, handling:
- User authentication (email/password, OAuth providers, magic links)
- JWT token issuance and validation
- Token refresh management
Required Client SDKs
To use the Cadenza WebSocket API, you need two client SDKs:
| SDK | Purpose |
|---|---|
| Centrifugo Client SDK | WebSocket connection and real-time communication (RPC calls, subscriptions) |
| Cadenza Client SDK | Request/response schema definitions and type-safe message construction |
The Centrifugo SDK handles the transport layer, while the Cadenza SDK provides the application-level protocol and data structures.
Authentication Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │ │ Supabase │ │ Centrifugo │ │ Cadenza │
│ │ │ (IdP) │ │ (Gateway) │ │ (Backend) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ 1. OAuth2 Login │ │ │
│──────────────────▶│ │ │
│ │ │ │
│ 2. Access Token │ │ │
│◀──────────────────│ │ │
│ │ │ │
│ 3. Connect (Bearer Token) │ │
│──────────────────────────────────────▶│ │
│ │ │ │
│ │ 4. Validate JWT │ │
│ │◀─────────────────▶│ │
│ │ (JWKS) │ │
│ │ │ │
│ 5. Connection Established │ │
│◀──────────────────────────────────────│ │
│ │ │ │
│ 6. RPC/Subscribe │ │
│──────────────────────────────────────▶│──────────────────▶│
│ │ │ │
│ 7. Response/Events │ │
│◀──────────────────────────────────────│◀──────────────────│
Flow Steps
- OAuth2 Login - Client authenticates with Supabase using supported methods (credentials, OAuth provider, etc.)
- Access Token - Supabase returns a JWT access token upon successful authentication
- Connect - Client establishes WebSocket connection to Centrifugo, passing the access token
- Validate JWT - Centrifugo validates the token against Supabase's JWKS endpoint
- Connection Established - On successful validation, the WebSocket connection is ready
- RPC/Subscribe - Client can now make RPC calls and subscribe to channels
- Response/Events - Server sends responses and real-time events back to the client
Step 1: Obtain Access Token
TODO: This section is incomplete.
Obtain an OAuth2 access token via WebSocket RPC request.
Step 2: Connect to WebSocket
Use the Centrifugo client SDK to establish a WebSocket connection with the access token:
Python:
from centrifuge import Client
client = Client(WS_URL, token=access_token, events=handler)
await client.connect()
TypeScript:
import { Centrifuge } from 'centrifuge'
const client = new Centrifuge(WS_URL, { token: accessToken })
client.connect()
Step 3: Handle Connection Events
Implement event handlers to manage the connection lifecycle:
| Event | Description |
|---|---|
on_connecting | Connection attempt in progress |
on_connected | Successfully connected, receives client ID |
on_disconnected | Connection lost, includes reason and disconnect code |
on_error | Error occurred during connection or communication |
Token Expiry Handling
Access tokens have a limited lifetime. Handle expiry gracefully:
- Detect expiry - Monitor for disconnect code
3500or "token expired" errors - Refresh token - Use Supabase's refresh token to obtain a new access token
- Reconnect - Establish a new connection with the fresh token
Disconnect Codes:
| Code | Meaning | Action |
|---|---|---|
| 3500 | Token expired | Refresh token and reconnect |
| 109 | Token invalid | Re-authenticate with Supabase |
| 100 | Invalid token format | Verify token format |
Security Best Practices
- Use environment variables - Never hardcode credentials or tokens
- Always use WSS - Use secure WebSocket (
wss://) in production - Handle token refresh - Implement automatic token refresh before expiry
- Secure token storage - Store tokens in secure, protected locations
- Validate disconnects - Check if disconnection was due to authentication failure