livqenoDocs

ChatAuthentication

Chat Authentication

How chat tokens work, what scopes mean, and why a token can only reduce what a role allows.

A chat token is a short-lived pass that lets one user's app connect to chat. Your backend makes it with the API key and sends it to the app. The app never sees the API key.

Where this fits: your backend creates a conversation and adds members. Then, each time a logged-in user opens the chat, your backend makes them a chat token (this page). The app connects with it. For the bigger picture, see Access tokens.

Make a token

Do this on your backend, in an endpoint your app calls after the user logs in. Take userId from your own login session, never from the request body. Otherwise anyone could chat as anyone.

const token = await livqeno.chat.createToken({
  userId: 'alice',
  conversations: [conversation.publicId],
  expiresIn: 3600, // optional, seconds
});

You get back a grant: the token itself, plus chatUrl and apiUrl (where the app connects), scopes, conversations, tokenId, and expiresAt. Send the whole grant to your app unchanged.

OptionWhat it doesDefault
userIdYour id for the user. Becomes the sender of every message they send.Required
conversationsLimit the token to these conversations.Every conversation the user is a member of
expiresInHow long the token lasts, in seconds.3600 (1 hour). Maximum 6 hours.
scopesNarrow what the user may do (see below).Everything their role allows

A chat token and an RTC token are separate. They use different keys, and neither works for the other. If one leaks, the other is still safe.

Two independent checks

Every chat action passes two checks:

  1. The token says who you are and what you may do. It holds: a project, a user id, an expiry, an optional list of conversations, and a set of scopes.
  2. Membership says which conversations you belong to, and your role in each one: MEMBER, MODERATOR, or ADMIN.

Why two? The token is made once and lives up to hours. Membership can change at any moment. Checking both means that removing someone from a conversation works immediately, even though their token hasn't expired.

Your role decides which scopes you can have. A token can remove scopes from a role, but never add them. So giving out a limited token is always safe.

ScopeLets youRoles that have it
chat:readRead messages, history, presence, and read receiptsAll
chat:sendSend, edit, and delete your own messages; react; typeAll
chat:moderateDelete anyone's messageMODERATOR, ADMIN
chat:manageChange the conversation's settingsADMIN

Example: Bob is a MODERATOR, but you want a read-only "observer" view. Make his token with scopes: ['chat:read']. That token can't send or delete, even though his role could.

Keeping users connected past expiry

A token expires after expiresIn. When it does, the connection closes and the app must connect again with a new token from your backend.

To make this seamless, pass onTokenExpiring when you create the client. The SDK calls it shortly before expiry. Fetch a new token from your backend and return it; the SDK reconnects with it. Without it, the socket just closes at expiry.

Revocation

You can cancel a token before it expires, for example when a user logs out or you suspect it leaked. From your backend:

DELETE /v1/chat/tokens/{tokenId}
  • tokenId comes from the token response.
  • This call needs your project API key, so make it from your backend.
  • It always succeeds. Chat tokens aren't stored, so there's no 404 for an unknown id.
  • The block lasts for CHAT_TOKEN_MAX_TTL_SECONDS (6 hours by default), no matter what expiresIn the token had.

What the server never trusts from a browser

Anything sent from a browser or phone can be changed by the user. So some things only ever come from the signed token or from your backend:

  • The sender always comes from the signed token, never from the request body.
  • system and event messages need a project API key. A user can't fake an official announcement.

See Moderation for how chat:moderate is enforced.

Common errors

ProblemCauseFix
chat:send missingThe user's role doesn't allow it (rare; MEMBER has it), or the token removed it.Check the role in Members and the token's scopes.
Token works for one conversation but not anotherThe token's conversations list didn't include it.Leave out conversations to allow every conversation the user is in, or add it to the list.
ROOM_NOT_FOUND right after connectingThe user isn't a member. The grant shows it under pendingMembership.Call addMember before making the token.
TOKEN_EXPIRED, socket closes with 4440The token's time is up.Fetch a new one from your backend, or use onTokenExpiring.
TOKEN_REVOKEDYou revoked this token.Make a new token if the user should still have access.

Production tips

  • Make a new token for each login session. Don't reuse one long-lived token.
  • Keep expiresIn short (the 1-hour default is fine) and refresh with onTokenExpiring. A short token limits the damage if one leaks.
  • If you have your own role check, pass its result as scopes. Scopes can only reduce access, so this is always safe.

Next steps

API reference

livqeno.chat.createToken() (TypeScript) / livqeno.chat.create_token() (Python) / client.Chat.CreateToken() (Go) / livqeno.chat().createToken() (Java) / $client->chat->createToken() (PHP). See TypeScript SDK, Python SDK, Go SDK, Java SDK and PHP SDK.