Client authentication and tokens
Pass an authentication token from the client, refresh it when it expires, and choose how it is sent with the WebSocket connection. The server counterpart is JWT authentication; the SDK works the same way with custom token schemes.
Provide the initial token
The simplest way is to include the token in the connection URL:
import { createCable } from '@anycable/web'
export default createCable(`ws://localhost:8080/cable?jid=${token}`)Or pass it via the auth option:
export default createCable({
auth: { token: 'secret-value' }
})By default, the token is added as the jid query parameter, matching AnyCable JWT authentication. Use param to change the name:
export default createCable({
auth: { token: 'secret-value', param: 'token' }
})With @anycable/web, you can also inject the token into the page instead of into JS:
<meta name="cable-token" content="secret-token">
<!-- optionally, a custom param name -->
<meta name="cable-token-param" content="token">Fetch the token before connecting
When the token can only be obtained via a request, you can still create the cable instance at application start and configure the transport lazily with transportConfigurator. It runs right before the connection is established:
import { createCable } from '@anycable/web'
export default createCable({
transportConfigurator: async (transport, { initial }) => {
// Only fetch on the first connection attempt
if (!initial) return
const response = await fetch('/token.json')
const data = await response.json()
transport.setToken(data.token, 'token')
}
})The configurator does not handle expiration; combine it with tokenRefresher below for long-lived clients.
Refresh expired tokens
The server checks the token only when a connection is established. The typical failure: a client reconnects after a network interruption, and its token has expired in the meantime. The server rejects it with the reason token_expired, and the SDK can then fetch a fresh token and reconnect on its own. Provide a tokenRefresher function:
// cable.js
import { createCable } from '@anycable/web'
export default createCable({
tokenRefresher: async (transport) => {
const response = await fetch('/token.json')
const data = await response.json()
// Update the whole URL...
transport.setURL(`ws://example.com/cable?jid=${data.token}`)
// ...or only the token
transport.setToken(data.token)
}
})The refresher activates only on a token_expired disconnect ({"type":"disconnect","reason":"token_expired","reconnect":false}); network failures go through the regular reconnection flow.
For browser apps that embed the connection URL in HTML meta tags, there is a built-in refresher that reloads the current page in the background and reads the fresh URL and token from it:
import { createCable, fetchTokenFromHTML } from '@anycable/web'
export default createCable({ tokenRefresher: fetchTokenFromHTML() })
// or fetch a dedicated lightweight page
export default createCable({
tokenRefresher: fetchTokenFromHTML({ url: '/custom-token-refresh-endpoint' })
})Keep the refresh response small
fetchTokenFromHTML sends the X-ANYCABLE-OPERATION: token-refresh header with its request. Detect it on the backend and render only the meta tags instead of the full page.
Choose how the token is sent
Browsers cannot set custom headers on WebSocket connections, so query parameters are the default. In other environments you have more options, controlled by websocketAuthStrategy:
import WebSocket from 'ws'
import { createCable } from '@anycable/core'
const cable = createCable(url, {
websocketImplementation: WebSocket,
websocketAuthStrategy: 'header',
auth: { token: 'secret-token' }
})'header'(Node.js, React Native): adds anx-<param>: <token>header to the connection request (x-jidwith the default param name).'sub-protocol': appends ananycable-token.<token>entry to the WebSocket sub-protocols (thesec-websocket-protocolheader). This works in browsers too and is supported by AnyCable server since v1.6.- Query parameter: the default, works everywhere.