Skip to main content

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:

SDKPurpose
Centrifugo Client SDKWebSocket connection and real-time communication (RPC calls, subscriptions)
Cadenza Client SDKRequest/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

  1. OAuth2 Login - Client authenticates with Supabase using supported methods (credentials, OAuth provider, etc.)
  2. Access Token - Supabase returns a JWT access token upon successful authentication
  3. Connect - Client establishes WebSocket connection to Centrifugo, passing the access token
  4. Validate JWT - Centrifugo validates the token against Supabase's JWKS endpoint
  5. Connection Established - On successful validation, the WebSocket connection is ready
  6. RPC/Subscribe - Client can now make RPC calls and subscribe to channels
  7. 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:

EventDescription
on_connectingConnection attempt in progress
on_connectedSuccessfully connected, receives client ID
on_disconnectedConnection lost, includes reason and disconnect code
on_errorError occurred during connection or communication

Token Expiry Handling

Access tokens have a limited lifetime. Handle expiry gracefully:

  1. Detect expiry - Monitor for disconnect code 3500 or "token expired" errors
  2. Refresh token - Use Supabase's refresh token to obtain a new access token
  3. Reconnect - Establish a new connection with the fresh token

Disconnect Codes:

CodeMeaningAction
3500Token expiredRefresh token and reconnect
109Token invalidRe-authenticate with Supabase
100Invalid token formatVerify token format

Security Best Practices

  1. Use environment variables - Never hardcode credentials or tokens
  2. Always use WSS - Use secure WebSocket (wss://) in production
  3. Handle token refresh - Implement automatic token refresh before expiry
  4. Secure token storage - Store tokens in secure, protected locations
  5. Validate disconnects - Check if disconnection was due to authentication failure