Guides

Tokens

Connection and subscription JWTs - token vs requestToken, need_token / provideToken, need_sub_token / subRefresh.

Centrifugo authenticates a connection with a JWT and a private channel with a subscription JWT. Your backend signs them; the plugin passes them on and asks the frontend for new ones when needed.

Connection token

Two ways to give the first token:

token: "<jwt>"requestToken: true
First connectUses token at once.Emits need_token and waits for provideToken.
Before the token expiresEmits need_token.Emits need_token.
Neither setAnonymous connection, need_token never comes.—

The need_token handler is installed only when token is set or requestToken: true, so anonymous connections never wait for the frontend.

import { connect, onNeedToken, provideToken } from "tauri-plugin-centrifugo-api";

await onNeedToken(async () => {
  try {
    const token = await fetch("/api/centrifugo/token").then((r) => r.text());
    await provideToken(token);
  } catch {
    await provideToken(""); // see below
  }
});

await connect({ url, channels: [], useProtobuf: false, requestToken: true });

Register the listener before connect(). There is no timeout: the SDK waits for the answer as centrifuge-js does, and disconnect() ends the wait. A newer request replaces an older unanswered one; the old one is retried, not treated as unauthorized.

What to answer

CallEffect
provideToken(token)Answers the pending need_token. Rejects when none is pending.
provideToken("")"No token": an anonymous connect on the first connect; on a refresh the server disconnects the client as unauthorized (disconnected, code 1).
refresh({ token })Answers the pending need_token; when none is pending, sets the token for the next connect.
setToken(token)Replaces the token used from the next connect attempt; does not answer a request.

All of them reject with NotConnected when there is no client (provideToken with "no token request in progress").

Subscription tokens

A subscription created with token refreshes it the same way, per channel:

import { addSubscriptionWithOptions, onNeedSubToken, subRefresh } from "tauri-plugin-centrifugo-api";

await onNeedSubToken(async ({ payload }) => {
  const token = await fetch(`/api/centrifugo/sub-token?channel=${encodeURIComponent(payload.channel)}`)
    .then((r) => r.text());
  await subRefresh({ channel: payload.channel, token });
});

await addSubscriptionWithOptions({ channel: "$private:42", token: firstToken });

need_sub_token carries channel and ts. subRefresh rejects when no request is pending for that channel. An empty token means unauthorized: the subscription ends with unsubscribed. Subscriptions without token (including those from channels) never ask; channels that need a token must be added with addSubscriptionWithOptions.

Expiry and refresh

The SDK reads the ttl the server returns for the token and asks for a new one shortly before it expires; there is nothing to schedule in the app. If the token has already expired on connect, the server answers with "token expired" and the SDK asks again.

Signing tokens is your backend's job, for example with the HMAC secret (token_hmac_secret_key in Centrifugo v5) and the sub claim for the user ID; for subscription tokens also channel.