livqenoDocs

RTCAuthentication

RTC Authentication

How to make an RTC token, what its permissions allow, and how to revoke it.

To join a call, a user needs a token: a short-lived pass that says "this user may join this room, with these rights, until this time". Your backend makes the token with your API key. Your app only ever sees the token, never the key.

This page is for the backend half of a call. It answers three questions: who can join, what they can do once inside, and how to stop them.

Before you start: you need an API key on your backend. See API credentials.

How a token moves

1. alice clicks "Join call" in your app
2. Your app calls YOUR backend (e.g. POST /join-room)
3. Your backend checks alice is logged in and allowed in this room
4. Your backend calls livqeno.tokens.create(...)   ← uses the API key
5. Livqeno returns a grant: { token, endpoint, iceServers, ... }
6. Your backend sends the grant to your app
7. Your app calls join() with it

Step 3 is yours. Livqeno doesn't know your users; it trusts your backend to decide who gets a token.

Make a token

On your backend, inside the endpoint your app calls when a user joins:

const token = await livqeno.tokens.create({
  room: 'demo-room',
  identity: req.user.id,        // from YOUR session, never from the client
  permissions: { join: true, publish: true, subscribe: true },
  expiresIn: 3600,               // optional, seconds
});

You get back { token, endpoint, iceServers, expiresAt, ... } (the grant). Send the whole object to your app, unchanged. Every client SDK's join() / Livqeno(...) expects exactly this. iceServers holds the relay (TURN) details that let calls work on strict networks, and they're made for this token only.

Permissions

Permissions decide what the user can do once they're in the room. Anything you don't turn on is off.

PermissionLets the user…
joinEnter the room.
publishSend camera, mic, and screen share.
subscribeSee and hear other people.
publishDataSend data messages (Rooms & Participants).

Real-world examples:

WhoPermissionsWhy
Doctor and patient in a video visitjoin, publish, subscribeBoth talk and see each other.
Student in a lecturejoin, subscribeWatches and listens, can't send video.
Game lobby player who also sends game statejoin, publish, subscribe, publishDataAdds data messages.
  • A viewer-only user gets join + subscribe, with publish: false.
  • You set permissions on the backend when you make the token.
  • The app can't ask for more. Trying to publish without permission fails with PERMISSION_DENIED.

The server always checks

A user could edit your app's code in their browser. That's why the token is signed: the app can read it but can't change it.

The SDK reads the token in the app only to show friendly errors early (like ROOM_NOT_FOUND). It doesn't trust what it reads.

The server checks every permission again when the user connects. A token that was changed or reused fails there.

Expiry

Every token expires. There are no tokens that last forever. By default the expiry only matters for joining: a user who joined with a valid token isn't kicked out when it expires mid-call. It matters again if they reconnect, and a server that enforces expiry ends the session when it runs out.

To keep long calls alive either way, give the SDK a refreshToken callback that returns a new token from your backend (same room and identity). The server warns the SDK a minute before expiry and the SDK swaps the new token into the live session, with no reconnect. See Keep long calls alive.

SettingValue
Set the lifetime withexpiresIn (SDK) or ttlSeconds (REST API)
Default10 minutes (stock configuration)
Maximum6 hours
  • Make a new token for each join. Don't reuse one across sessions.
  • A short lifetime is your best protection if a token leaks.
  • An old token fails with TOKEN_EXPIRED. This is different from INVALID_TOKEN, so your app can tell "get a fresh token" apart from "something is set up wrong".

Reusing a token

An RTC token is a bearer credential. Whoever holds a valid one can use it, as many times as they like, until it expires or you revoke it. There is no per-token join count.

What stops that from multiplying your usage is the identity rule, not the token:

  • One token, one participant. A token is locked to one room and one identity. Joining again with it, or with any token for the same identity in that room, replaces the earlier connection. The older one is closed with code 4002 and leave reason REPLACED, and it stops being metered. So a copied token can't run a crowd of simultaneous participants on your account.
  • The flip side is takeover. Someone holding a leaked token can join as that participant and push the real user out. They can keep doing it until the token expires or is revoked.

So treat a token like a password for one seat in one room:

  • Mint it on your backend, per join, with the shortest expiresIn that covers the call plus a refreshToken for long ones. See Expiry.
  • Never log it, put it in a URL you share, or store it past the join.
  • If one leaks, revoke it (Revocation) and, to put the person out right now, use POST /v1/rooms/{roomId}/participants/{identity}/remove.

There is no concurrency cap beyond this. Expiry and revocation are the controls.

Revocation

You can kill a token before it expires. Use this when you gave a user a token and then changed your mind, for example they were removed from the meeting before they joined.

From your backend, with your API key:

DELETE /v1/rooms/{roomId}/rtc-tokens/{tokenId}
  • tokenId is the id field from the mint response.
  • You can only revoke tokens from your own project and environment. Anything else returns 404.
  • It's safe to call twice, or on a token that already expired.

What revocation does

It blocks the token for anything new. The next connection with it fails with TOKEN_REVOKED. The SDK doesn't retry this error, because the same token will never work again.

Revoking ends sessions running on the token

A user already in the call on that token is put out too: they get room.left with reason TOKEN_REVOKED, an error with TOKEN_REVOKED, and close code 4001. Their reconnect with the same token is refused.

To end a call that's running, close the room from your backend:

DELETE /v1/rooms/{roomId}

This disconnects everyone in it.

Use revocation to stop future joins, for example when you gave a user a token and changed your mind. It helps, but a short token lifetime protects you more.

Each token is locked to one project and one room

These are signed into the token. Nobody can change them.

  • Project and environment. A token only works in the project and environment that made it. A development token can't reach production.
  • Room. A token works for exactly one room. If your app also sends a room id when joining, it must match. A token for room-a can never join room-b.

Permissions are signed too. The server enforces what's in the token, not what the app asks for. See Permissions.

Allowed browser origins are a separate setting per project. See Browser security & CORS.

For the token model shared by RTC and Chat, see Authentication → Tokens.

Common errors

ErrorCauseFix
INVALID_TOKENToken is broken, or from a different project/environment.Use the token exactly as tokens.create() returned it.
TOKEN_EXPIREDPast expiresAt.Make a new one. See Expiry.
ROOM_NOT_FOUNDThe room passed to join() isn't the token's room.Use the same room id/name you passed to tokens.create({ room }).
TOKEN_REVOKEDThe token was revoked with DELETE /v1/rooms/{roomId}/rtc-tokens/{tokenId}.Make a new one. Retrying the same token never works.
ORIGIN_NOT_ALLOWEDThe page's address isn't on the project's allowed list.Add it under Project Settings → Security → Allowed Origins. See Browser security.
USAGE_LIMIT_EXCEEDEDThe account used up its free RTC participant-minutes.Retrying won't help. Calls already running keep going. See Usage.

Checklist for production

  • Make one token per join. Don't share one token across sessions.
  • Set identity from your backend's login session, never from the app.
  • Give only the permissions the user needs. Viewers always get publish: false.

Next steps

Your backend can now make tokens. Next, use them in your app:

Related: