Skip to main content

You are looking at Voice Calling v3.x Docs. The newest version is  Voice Calling 4.x

Android
iOS
macOS
Windows C++
Windows C#
Unity
Flutter
React Native
Electron
Cocos Creator
Cocos2d-x

Authenticate Your Users with Tokens

Authentication is the act of validating the identity of each user before they access your system. Agora uses digital tokens to authenticate users and their privileges before they access an Agora service, such as joining an Agora call, or logging into the real-time messaging system.

To enhance its authentication and security services, Agora provides a new version of token called AccessToken2 as of August 18, 2022. For how to upgrade from AccessToken to AccessToken2, see Upgrade from AccessToken to AccessToken2.

This page shows you how to create a token server and a client app for AccessToken2. The client app retrieves a token from the token server. This token authenticates the current user when the user accesses the Agora service.

Understand the tech

The following figure shows the steps in the authentication flow:

token authentication flow

A token is a dynamic key generated on your app server that is valid for a maximum of 24 hours. When your users connect to an Agora channel from your app client, Agora Platform validates the token and reads the user and project information stored in the token. A token contains the following information:

  • The App ID of your Agora project
  • The channel name
  • The user ID of the user to be authenticated
  • The privilege of the user, either as a publisher or a subscriber
  • The time after which the token expires

Prerequisites

In order to follow this procedure you must have the following:

Implement the authentication flow

This section shows you how to supply and consume a token that gives rights to specific functionality to authenticated users using the source code provided by Agora.

Get the App ID and App Certificate

This section shows you how to get the security information needed to generate a token, including the App ID and App Certificate of your project.

1. Get the App ID

Agora automatically assigns each project an App ID as a unique identifier.

To copy this App ID, find your project on the Project Management page in Agora Console, and click the copy icon in the App ID column.

2. Get the App Certificate

To get an App Certificate, do the following:

  1. On the Project Management page, click Config for the project you want to use.

    1641971710869

  2. Click the copy icon under Primary Certificate.

    1637660100222

Deploy a token server

Token generators create the tokens requested by your client app to enable secure access to Agora security infrastructure. To serve these tokens you deploy a generator in your security infrastructure.

In order to show the authentication workflow, this section shows how to build and run a token server written in Golang on your local machine.

This sample server uses BuildTokenWithUid[1/2].

This sample server is for demonstration purposes only. Do not use it in a production environment.
  1. Create a file, server.go, with the following content. Then replace <Your App ID> and <Your App Certificate> with your App ID and App Certificate.

_122
package main
_122
_122
import (
_122
rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
_122
"fmt"
_122
"log"
_122
"net/http"
_122
"encoding/json"
_122
"errors"
_122
"strconv"
_122
)
_122
_122
type rtc_int_token_struct struct{
_122
Uid_rtc_int uint32 `json:"uid"`
_122
Channel_name string `json:"ChannelName"`
_122
Role uint32 `json:"role"`
_122
}
_122
_122
var rtc_token string
_122
var int_uid uint32
_122
var channel_name string
_122
_122
var role_num uint32
_122
var role rtctokenbuilder.Role
_122
_122
// Use RtcTokenBuilder to generate an <Vg k="VSDK" /> token.
_122
func generateRtcToken(int_uid uint32, channelName string, role rtctokenbuilder.Role){
_122
_122
appID := "<Your App ID>"
_122
appCertificate := "<Your App Certificate>"
_122
// Number of seconds after which the AccessToken2 expires.
_122
// When the AccessToken2 expires but the privilege does not expire, the user remains in the channel and can continue to publish streams. No callback is triggered from the SDK.
_122
// However, once disconnected from the channel, the user cannot rejoin the channel with that token. Ensure the AccessToken2 does not expire before the privileges.
_122
tokenExpireTimeInSeconds := uint32(40)
_122
// Number of seconds after which the privilege expires.
_122
// The token-privilege-will-expire callback occurs 30 seconds before the privilege expires.
_122
// The token-privilege-did-expire callback occurs when the privilege expires.
_122
// For demonstration purposes the expire time is set to 40 seconds. This shows you the automatic token renew actions of the client.
_122
privilegeExpireTimeInSeconds := uint32(40)
_122
_122
result, err := rtctokenbuilder.BuildTokenWithUid(appID, appCertificate, channelName, int_uid, role, tokenExpireTimeInSeconds, privilegeExpireTimeInSeconds)
_122
if err != nil {
_122
fmt.Println(err)
_122
} else {
_122
fmt.Printf("Token with uid: %s\n", result)
_122
fmt.Printf("uid is %d\n", int_uid )
_122
fmt.Printf("ChannelName is %s\n", channelName)
_122
fmt.Printf("Role is %d\n", role)
_122
}
_122
rtc_token = result
_122
}
_122
_122
_122
func rtcTokenHandler(w http.ResponseWriter, r *http.Request){
_122
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
_122
w.Header().Set("Access-Control-Allow-Origin", "*")
_122
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS");
_122
w.Header().Set("Access-Control-Allow-Headers", "*");
_122
_122
if r.Method == "OPTIONS" {
_122
w.WriteHeader(http.StatusOK)
_122
return
_122
}
_122
_122
if r.Method != "POST" && r.Method != "OPTIONS" {
_122
http.Error(w, "Unsupported method. Please check.", http.StatusNotFound)
_122
return
_122
}
_122
_122
_122
var t_int rtc_int_token_struct
_122
var unmarshalErr *json.UnmarshalTypeError
_122
int_decoder := json.NewDecoder(r.Body)
_122
int_err := int_decoder.Decode(&t_int)
_122
if (int_err == nil) {
_122
_122
int_uid = t_int.Uid_rtc_int
_122
channel_name = t_int.Channel_name
_122
role_num = t_int.Role
_122
switch role_num {
_122
case 1:
_122
role = rtctokenbuilder.RolePublisher
_122
case 2:
_122
role = rtctokenbuilder.RoleSubscriber
_122
}
_122
}
_122
if (int_err != nil) {
_122
_122
if errors.As(int_err, &unmarshalErr){
_122
errorResponse(w, "Bad request. Wrong type provided for field " + unmarshalErr.Value + unmarshalErr.Field + unmarshalErr.Struct, http.StatusBadRequest)
_122
} else {
_122
errorResponse(w, "Bad request.", http.StatusBadRequest)
_122
}
_122
return
_122
}
_122
_122
generateRtcToken(int_uid, channel_name, role)
_122
errorResponse(w, rtc_token, http.StatusOK)
_122
log.Println(w, r)
_122
}
_122
_122
func errorResponse(w http.ResponseWriter, message string, httpStatusCode int){
_122
w.Header().Set("Content-Type", "application/json")
_122
w.Header().Set("Access-Control-Allow-Origin", "*")
_122
w.WriteHeader(httpStatusCode)
_122
resp := make(map[string]string)
_122
resp["token"] = message
_122
resp["code"] = strconv.Itoa(httpStatusCode)
_122
jsonResp, _ := json.Marshal(resp)
_122
w.Write(jsonResp)
_122
_122
}
_122
_122
func main(){
_122
// Video Calling token from Video SDK int uid
_122
http.HandleFunc("/fetch_rtc_token", rtcTokenHandler)
_122
fmt.Printf("Starting server at port 8082\n")
_122
_122
if err := http.ListenAndServe(":8082", nil); err != nil {
_122
log.Fatal(err)
_122
}
_122
}

  1. A go.mod file defines this module’s import path and dependency requirements. To create the go.mod for your token server, run the following command:


    _1
    $ go mod init sampleServer

  2. Get dependencies by running the following command. You can use a Go mirror origin such as https://goproxy.cn/ to speed up the process.


    _1
    $ go get

  3. Start the server by running the following command:


    _1
    $ go run server.go

Use AccessToken2 for client-side user authentication

This section uses the Web client as an example to show how to use a token for client-side user authentication.

This sample client is for demonstration purposes only. Do not use it in a production environment.
  1. Create the project structure of the Web client with a folder including the following files.

    • index.html: User interface
    • client.js: App logic with Agora Video Calling Web SDK v4.x (Must be v4.8.0 or higher)

    _3
    |
    _3
    |-- index.html
    _3
    |-- client.js

  2. In index.html, add the following code to include the app logic in the UI:


    _13
    <html>
    _13
    <head>
    _13
    <title>Token demo</title>
    _13
    </head>
    _13
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    _13
    <body>
    _13
    <h1>Token demo</h1>
    _13
    _13
    <script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.8.0.js""></script>
    _13
    <script src="./client.js"></script>
    _13
    _13
    </body>
    _13
    </html>

  3. Create the app logic by editing client.js with the following content:

  • Replace <Your App ID> with your App ID. The App ID must match the one in the server.
  • Replace <Your Host URL and port> with the host URL and port of the local Golang server you have just deployed, such as 10.53.3.234:8082.

_105
var rtc = {
_105
// For the local audio and video tracks.
_105
localAudioTrack: null,
_105
localVideoTrack: null,
_105
};
_105
_105
var options = {
_105
// Passes your app ID here.
_105
appId: "<Your app ID>",
_105
// Sets the channel name.
_105
channel: "ChannelA",
_105
// Sets the user role in the channel.
_105
role: "host"
_105
};
_105
_105
// Fetches a token from the Golang server.
_105
function fetchToken(uid, channelName, tokenRole) {
_105
_105
return new Promise(function (resolve) {
_105
axios.post('http://<Your Host URL and port>/fetch_rtc_token', {
_105
uid: uid,
_105
channelName: channelName,
_105
role: tokenRole
_105
}, {
_105
headers: {
_105
'Content-Type': 'application/json; charset=UTF-8'
_105
}
_105
})
_105
.then(function (response) {
_105
const token = response.data.token;
_105
resolve(token);
_105
})
_105
.catch(function (error) {
_105
console.log(error);
_105
});
_105
})
_105
}
_105
_105
async function startBasicCall() {
_105
_105
const client = AgoraRTC.createClient({ mode: "live", codec: "vp8" });
_105
client.setClientRole(options.role);
_105
const uid = 123456;
_105
_105
// Fetches a token before calling join to join a channel.
_105
let token = await fetchToken(uid, options.channel, 1);
_105
_105
await client.join(options.appId, options.channel, token, uid);
_105
rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
_105
rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
_105
await client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
_105
const localPlayerContainer = document.createElement("div");
_105
localPlayerContainer.id = uid;
_105
localPlayerContainer.style.width = "640px";
_105
localPlayerContainer.style.height = "480px";
_105
document.body.append(localPlayerContainer);
_105
_105
rtc.localVideoTrack.play(localPlayerContainer);
_105
_105
console.log("publish success!");
_105
_105
client.on("user-published", async (user, mediaType) => {
_105
await client.subscribe(user, mediaType);
_105
console.log("subscribe success");
_105
_105
if (mediaType === "video") {
_105
const remoteVideoTrack = user.videoTrack;
_105
const remotePlayerContainer = document.createElement("div");
_105
remotePlayerContainer.textContent = "Remote user " + user.uid.toString();
_105
remotePlayerContainer.style.width = "640px";
_105
remotePlayerContainer.style.height = "480px";
_105
document.body.append(remotePlayerContainer);
_105
remoteVideoTrack.play(remotePlayerContainer);
_105
_105
}
_105
_105
if (mediaType === "audio") {
_105
const remoteAudioTrack = user.audioTrack;
_105
remoteAudioTrack.play();
_105
}
_105
_105
client.on("user-unpublished", user => {
_105
const remotePlayerContainer = document.getElementById(user.uid);
_105
remotePlayerContainer.remove();
_105
});
_105
_105
});
_105
_105
// When token-privilege-will-expire occurs, fetches a new token from the server and call renewToken to renew the token.
_105
client.on("token-privilege-will-expire", async function () {
_105
let token = await fetchToken(uid, options.channel, 1);
_105
await client.renewToken(token);
_105
});
_105
_105
// When token-privilege-did-expire occurs, fetches a new token from the server and call join to rejoin the channel.
_105
client.on("token-privilege-did-expire", async function () {
_105
console.log("Fetching the new Token")
_105
let token = await fetchToken(uid, options.channel, 1);
_105
console.log("Rejoining the channel with new Token")
_105
await rtc.client.join(options.appId, options.channel, token, uid);
_105
});
_105
_105
}
_105
_105
startBasicCall()

In the code example, you can see that token is related to the following code logic in the client:

  • Call join to join the channel with token, user ID, and channel name. The user ID and channel name must be the same as the ones used to generate the token.
  • The token-privilege-will-expire callback occurs 30 seconds before the privilege expires. When the token-privilege-will-expire callback is triggered, the client must fetch the token from the server and call renewToken to pass the new token to the SDK.
  • The token-privilege-did-expire callback occurs when the privilege expires. When the token-privilege-did-expire callback is triggered, the client must fetch the token from the server and call join to use the new token to join the channel.
  1. Open index.html with a supported browser to perform the following actions:
    • Successfully joining a channel.
    • Renewing a token every 10 seconds.

Reference

This section introduces token generator libraries, version requirements, and related documents about AccessToken2.

SDK compatibility for AccessToken2

AccessToken2 supports the following versions of the Agora Video SDK (excluding the client-side Media Push feature):

SDKSDK Version to Support AccessToken2
Video Calling Native SDK>= 3.6.0
Video Calling Electron SDK>= 3.6.0
Video Calling Unity SDK>= 3.6.0
Video Calling React Native SDK>= 3.6.0
Video Calling Flutter SDK>= 5.10
Video Calling Web SDK>= 4.8.0

Video Calling SDKs that use AccessToken2 can interoperate with Video Calling SDKs that use AccessToken. Video Calling SDKs that support AccessToken2 also support AccessToken.

AccessToken2 generator libraries

Agora provides an open-source AgoraDynamicKey repository on GitHub, which enables you to generate tokens on your server with programming languages such as C++, Java, and Go.

LanguageAlgorithmCore methodSample code
C++HMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.cpp
GoHMAC-SHA256buildTokenWithUidsample.go
JavaHMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.java
Node.jsHMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.js
PHPHMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.php
PythonHMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.py
Python3HMAC-SHA256buildTokenWithUidRtcTokenBuilderSample.py

BuildTokenWithUid API Reference

This section introduces the core method for generating AccessToken2: BuildTokenWithUid. The AccessToken2 generator libraries provide two BuildTokenWithUid methods:

  • BuildTokenWithUid [1/2]: Generates an AccessToken2, and sets the expiration for AccessToken2 and the expiration for all privileges.
  • BuildTokenWithUid [2/2]: Generates an AccessToken2, and sets the expiration for the following:
  • AccessToken2
  • The privilege of publishing audio streams in a channel
  • The privilege of publishing video streams in a channel
  • The privilege of publishing data streams in a channel

BuildTokenWithUid [1/2]

This method uses a token_expire parameter to set the expiration for AccessToken2 and a privilege_expire parameter to set the expiration for all privileges.


_7
// Take C++ as an example
_7
static std::string BuildTokenWithUid(const std::string& app_id,
_7
const std::string& app_certificate,
_7
const std::string& channel_name,
_7
uint32_t uid, UserRole role,
_7
uint32_t token_expire,
_7
uint32_t privilege_expire = 0);

ParameterDescription
appIdThe App ID of your Agora project.
appCertificateThe App Certificate of your Agora project.
channelNameThe channel name. The string length must be less than 64 bytes. The following character sets are supported:
  • All lowercase English letters: a to z.
  • All upper English letters: A to Z.
  • All numeric characters: 0 to 9.
  • The space character.
  • Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " , ", "|", "~", ",".
uidThe user ID of the user to be authenticated. A 32-bit unsigned integer with a value range from 1 to (2³² - 1). It must be unique. Set uid as 0, if you do not want to authenticate the user ID, that is, any uid from the app client can join the channel.
roleThe privilege of the user, either as a publisher or a subscriber. This parameter determines whether a user can publish streams in the channel.
  • kRole_Publisher(1): (Default) The user has the privilege of a publisher, that is, the user can publish streams in the channel.
  • kRole_Subscriber(2): The user has the privilege of a subscriber, that is, the user can only subscribe to streams, not publish them, in the channel.
This value takes effect only if you have enabled co-host authentication. For details, see Enable co-host authentication.
token_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of that AccessToken2. For example, if you set it as 600, the AccessToken2 expires 10 minutes after generation. The maximum duration of an AccessToken2 is 24 hours. If you set it to a duration longer than 24 hours, the AccessToken2 still expires after 24 hours. If you set it to 0, the AccessToken2 expires immediately.
privilege_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of all privileges. For example, if you set it to 600, the privilege expires 10 minutes after generation. If you set it to 0 (default), the privilege never expires.
When the AccessToken2 expires but the privilege does not expire, the user remains in the channel and can continue to publish streams. No token-related callback is triggered in the SDK. However, once disconnected from the channel, the user cannot rejoin the channel with that token. Ensure the AccessToken2 does not expire before privileges.

BuildTokenWithUid [2/2]

To facilitate privilege-level configuration in a channel, Agora provides an overloaded method, BuildTokenWithUid [2/2], to support configuring the expiration of the AccessToken2 and related privileges:

  • Join a channel
  • Publish audio streamsin a channel
  • Publish video streams in a channel
  • Publish data streams in a channel

_11
// Take C++ as an example
_11
static std::string BuildTokenWithUid(
_11
const std::string& app_id,
_11
const std::string& app_certificate,
_11
const std::string& channel_name,
_11
uint32_t uid,
_11
uint32_t token_expire,
_11
uint32_t join_channel_privilege_expire = 0,
_11
uint32_t pub_audio_privilege_expire = 0,
_11
uint32_t pub_video_privilege_expire = 0,
_11
uint32_t pub_data_stream_privilege_expire = 0);

This method generates an Video SDK AccessToken2 and supports configuring the expiration time of the token and the following privileges:

  • Joining an Video SDK channel
  • Publishing audio streams in an Video SDK channel
  • Publishing video streams in an Video SDK channel
  • Publishing data streams in an Video SDK channel

The privileges of publishing audio streams in an Video SDK channel, publishing video streams in an Video SDK channel, and publishing data streams in an Video SDK channel only take effect after enabling co-host authentication.

You can assign multiple privileges to a user. When a privilege is about to expire or has expired, the Video SDK triggers the onTokenPriviegeWillExpire callback or the onRequestToken callback. You need to take the following actions in your own app logic:

  1. Tag the type of privilege that is about to expire or has expired in your app logic.
  2. The app fetches a new AccessToken2 from the token server.
  3. The SDK calls renewToken to renew the AccessToken2.

You need to set an appropriate expiration timestamp. For example, if the expiration time of joining a channel is earlier than that of publishing audio in the channel, when the privilege of joining a channel expires, the user is kicked out of the Video SDK channel. Even if the privilege of publishing audio is still valid, user cannot exercise that privilege.

When the AccessToken2 expires but the privilege of joining a channel does not expire, the user remains in the channel and can continue to publish streams if the corresponding privilege does not expire. No token-related callback is triggered in the SDK. However, once disconnected from the channel, the user cannot rejoin the channel with that token. Ensure the AccessToken2 does not expire before other privileges.
ParameterDescription
token_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of that AccessToken2. For example, if you set it as 600, the AccessToken2 expires 10 minutes after generation. The maximum duration of an AccessToken2 is 24 hours. If you set it to a duration longer than 24 hours, the AccessToken2 still expires after 24 hours. If you set it to 0, the AccessToken2 expires immediately.
join_channel_privilege_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of the privilege of joining a channel. For example, if you set it to 600, the privilege expires 10 minutes after generation. If you set it to 0 (default), the privilege never expires.
pub_audio_privilege_expire The duration (in seconds) from the generation of an AccessToken2 to the expiration of the privilege of publishing audio streams in a channel. For example, if you set it to 600, the privilege expires 10 minutes after generation. If you set it to 0 (default), the privilege never expires.
pub_video_privilege_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of the privilege of publishing video streams in a channel. For example, if you set it to 600, the privilege expires 10 minutes after generation. If you set it to 0 (default), the privilege never expires.
pub_data_stream_privilege_expireThe duration (in seconds) from the generation of an AccessToken2 to the expiration of the privilege of publishing data streams in a channel. For example, if you set it to 600, the privilege expires 10 minutes after generation. If you set it to 0 (default), the privilege never expires.

Enable co-host authentication

Refer to the following steps to enable this function in Agora Console:

  1. Log in to Agora Console. Under Projects, choose a project for which you want to enable co-host authentication, click the Config icon, and enter the Edit Project page.
  2. In the Features area, click Enable authentication.
  3. Follow the on-screen instructions to learn more about this function, check the box, and click Enable.

Co-host authentication takes effect in 5 minutes.

Once you have enabled co-host authentication, a user using your app must meet both of the following requirements to publish streams in a channel:

  • The user role in setClientRole is set as host.
  • The user joins the channel with a token that has the privilege of a publisher (by setting the role parameter in the buildToken method as kRolePublisher).

Upgrade from AccessToken to AccessToken2

For how to use AccessToken to authenticate your users and how to upgrade to AccessToken2, see Upgrade from AccessToken to AccessToken2.

Voice Calling