VoxaRTC
Documentation menu
Guides

Tokens & auth

Your token server does not change. The gateway verifies real Agora tokens against the app certificate, so whatever library you already mint with keeps working — you only change which credentials it signs.

Supported formats

FormatPrefixTypical minter
AccessToken2007…agora-token (Node), agora_token_builder (Python), Agora's Go builder
AccessToken v1 (legacy)006…agora-access-token and other older libraries

Both are verified with the same HMAC construction Agora uses, so a token that Agora would accept is one VoxaRTC accepts, and one it would reject is rejected with the matching Agora error code.

What happens on join

  1. Your app calls joinChannel(token:, channelId:, uid:).
  2. The SDK presents the token to your VoxaRTC endpoint along with the channel name and uid.
  3. The endpoint reads the App ID out of the token, looks up that project's certificate, and verifies the signature, the channel name, the uid and the expiry.
  4. On success it issues a short-lived credential for that channel and identity.
  5. The SDK streams directly to the media server. The endpoint is not in the media path.

Channels are namespaced per project, so two projects using the channel name lobby never collide.

uid handling

A uid of 0 means "let the server assign", exactly as in Agora. The gateway treats it as the wildcard form, so a token minted for uid 0 is valid for any uid in that channel — which is what Agora's wildcard tokens do, and why you should mint per-user tokens in production.

Roles and publish rights

Agora's publisher privileges in a token are advisory by default: an audience-role client can still call setClientRole(broadcaster) and publish, which is what makes co-host flows work without re-minting a token. VoxaRTC matches that default — a subscriber-role token may publish.

If you want strict enforcement — the token alone decides whether a participant can send media — set enforceCoHostAuth on the project. Then only tokens carrying the publish privileges are allowed to send media.

Renewal

Token lifecycle callbacks work as they do with Agora. The SDK schedules onTokenPrivilegeWillExpire ahead of the Agora token's expiry and onRequestToken at it; you respond with renewToken() as before. The media credential underneath has its own shorter TTL and is refreshed on reconnect exchanges — you never see it.

lib/call_page.dart
_engine.registerEventHandler(RtcEngineEventHandler(
  onTokenPrivilegeWillExpire: (connection, token) async {
    final fresh = await myBackend.fetchRtcToken(
      channel: connection.channelId!,
    );
    await _engine.renewToken(fresh);
  },
  onRequestToken: (connection) async {
    final fresh = await myBackend.fetchRtcToken(
      channel: connection.channelId!,
    );
    await _engine.renewToken(fresh);
  },
));

Minting from your own backend

Nothing VoxaRTC-specific is required — mint an Agora token the way you always have. For example, with Node's agora-token:

token-server.js
const { RtcTokenBuilder, RtcRole } = require('agora-token');

const token = RtcTokenBuilder.buildTokenWithUid(
  process.env.VOXA_APP_ID,          // from the console
  process.env.VOXA_APP_CERTIFICATE, // from the console
  channelName,
  uid,
  RtcRole.PUBLISHER,
  3600,                             // token TTL, seconds
  3600,                             // privilege TTL, seconds
);

The console mints the same thing from its Tokens tab, which is handy before your token server is wired up.

Error codes

Verification failures come back as Agora's numeric codes, so your existing error handling still branches correctly.

CodeMeaningUsual cause
109Token expiredPrivilege TTL elapsed; renew and retry
110Invalid tokenWrong certificate, wrong channel name, or a uid the token was not minted for
2Invalid argumentMissing token or channelName in the join request

If joins fail with "unknown App ID", the token is signed by credentials that are not registered on the gateway at https://gate.zamansheikh.com — create or import the project first.