Skip to main content

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

Upgrade from AccessToken to AccessToken2

This section introduces how to use AccessToken to authenticate your users and how to upgrade from AccessToken to AccessToken2.

If you have never used AccessToken before, you can skip this article and refer to Authenticate Your Users with Tokens.

Authenticate your users with AccessToken

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 Platform. 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 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.


    _128
    package main
    _128
    _128
    import (
    _128
    rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/RtcTokenBuilder"
    _128
    "fmt"
    _128
    "log"
    _128
    "net/http"
    _128
    "time"
    _128
    "encoding/json"
    _128
    "errors"
    _128
    "strconv"
    _128
    )
    _128
    _128
    type rtc_int_token_struct struct{
    _128
    Uid_rtc_int uint32 `json:"uid"`
    _128
    Channel_name string `json:"ChannelName"`
    _128
    Role uint32 `json:"role"`
    _128
    }
    _128
    _128
    var rtc_token string
    _128
    var int_uid uint32
    _128
    var channel_name string
    _128
    _128
    var role_num uint32
    _128
    var role rtctokenbuilder.Role
    _128
    _128
    // Use RtcTokenBuilder to generate an <Vg k="VSDK" /> token.
    _128
    func generateRtcToken(int_uid uint32, channelName string, role rtctokenbuilder.Role){
    _128
    _128
    appID := "<Your App ID>"
    _128
    appCertificate := "<Your App Certificate>"
    _128
    // Number of seconds after which the token expires.
    _128
    // For demonstration purposes the expiry time is set to 40 seconds. This shows you the automatic token renew actions of the client.
    _128
    expireTimeInSeconds := uint32(40)
    _128
    // Get current timestamp.
    _128
    currentTimestamp := uint32(time.Now().UTC().Unix())
    _128
    // Timestamp when the token expires.
    _128
    expireTimestamp := currentTimestamp + expireTimeInSeconds
    _128
    _128
    result, err := rtctokenbuilder.BuildTokenWithUID(appID, appCertificate, channelName, int_uid, role, expireTimestamp)
    _128
    if err != nil {
    _128
    fmt.Println(err)
    _128
    } else {
    _128
    fmt.Printf("Token with uid: %s\n", result)
    _128
    fmt.Printf("uid is %d\n", int_uid )
    _128
    fmt.Printf("ChannelName is %s\n", channelName)
    _128
    fmt.Printf("Role is %d\n", role)
    _128
    }
    _128
    rtc_token = result
    _128
    }
    _128
    _128
    _128
    func rtcTokenHandler(w http.ResponseWriter, r *http.Request){
    _128
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    _128
    w.Header().Set("Access-Control-Allow-Origin", "*")
    _128
    w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS");
    _128
    w.Header().Set("Access-Control-Allow-Headers", "*");
    _128
    _128
    if r.Method == "OPTIONS" {
    _128
    w.WriteHeader(http.StatusOK)
    _128
    return
    _128
    }
    _128
    _128
    if r.Method != "POST" && r.Method != "OPTIONS" {
    _128
    http.Error(w, "Unsupported method. Please check.", http.StatusNotFound)
    _128
    return
    _128
    }
    _128
    _128
    _128
    var t_int rtc_int_token_struct
    _128
    var unmarshalErr *json.UnmarshalTypeError
    _128
    int_decoder := json.NewDecoder(r.Body)
    _128
    int_err := int_decoder.Decode(&t_int)
    _128
    if (int_err == nil) {
    _128
    _128
    int_uid = t_int.Uid_rtc_int
    _128
    channel_name = t_int.Channel_name
    _128
    role_num = t_int.Role
    _128
    switch role_num {
    _128
    case 0:
    _128
    // DEPRECATED. RoleAttendee has the same privileges as RolePublisher.
    _128
    role = rtctokenbuilder.RoleAttendee
    _128
    case 1:
    _128
    role = rtctokenbuilder.RolePublisher
    _128
    case 2:
    _128
    role = rtctokenbuilder.RoleSubscriber
    _128
    case 101:
    _128
    // DEPRECATED. RoleAdmin has the same privileges as RolePublisher.
    _128
    role = rtctokenbuilder.RoleAdmin
    _128
    }
    _128
    }
    _128
    if (int_err != nil) {
    _128
    _128
    if errors.As(int_err, &unmarshalErr){
    _128
    errorResponse(w, "Bad request. Wrong type provided for field " + unmarshalErr.Value + unmarshalErr.Field + unmarshalErr.Struct, http.StatusBadRequest)
    _128
    } else {
    _128
    errorResponse(w, "Bad request.", http.StatusBadRequest)
    _128
    }
    _128
    return
    _128
    }
    _128
    _128
    generateRtcToken(int_uid, channel_name, role)
    _128
    errorResponse(w, rtc_token, http.StatusOK)
    _128
    log.Println(w, r)
    _128
    }
    _128
    _128
    func errorResponse(w http.ResponseWriter, message string, httpStatusCode int){
    _128
    w.Header().Set("Content-Type", "application/json")
    _128
    w.Header().Set("Access-Control-Allow-Origin", "*")
    _128
    w.WriteHeader(httpStatusCode)
    _128
    resp := make(map[string]string)
    _128
    resp["token"] = message
    _128
    resp["code"] = strconv.Itoa(httpStatusCode)
    _128
    jsonResp, _ := json.Marshal(resp)
    _128
    w.Write(jsonResp)
    _128
    _128
    }
    _128
    _128
    func main(){
    _128
    // Handling routes
    _128
    // Video Calling token from Video SDK num uid
    _128
    http.HandleFunc("/fetch_rtc_token", rtcTokenHandler)
    _128
    fmt.Printf("Starting server at port 8082\n")
    _128
    _128
    if err := http.ListenAndServe(":8082", nil); err != nil {
    _128
    log.Fatal(err)
    _128
    }
    _128
    }

  2. 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

  3. Get dependencies by running the following command:


    _1
    $ go get

  4. Start the server by running the following command:


    _1
    $ go run server.go

Use tokens for user authentication

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

In order to show the authentication workflow, this section shows how to build and run a Web client on your local machine.

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 4.x

    _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.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. Then replace <Your App ID> with your App ID. The App ID must match the one in the server. You also need to 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
    // Pass your app ID here.
    _105
    appId: "<Your app ID>",
    _105
    // Set the channel name.
    _105
    channel: "ChannelA",
    _105
    // Set the user role in the channel.
    _105
    role: "host"
    _105
    };
    _105
    _105
    // Fetch 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
    // Fetch 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, fetch 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, fetch 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 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, uid, and channel name. The uid 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 a token 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 a token 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.
  4. 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 tokens.

Token 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

API reference

This section introduces the parameters and descriptions for the method to generate a token. Take C++ as an example:


_7
static std::string buildTokenWithUid(
_7
const std::string& appId,
_7
const std::string& appCertificate,
_7
const std::string& channelName,
_7
uint32_t uid,
_7
UserRole role,
_7
uint32_t privilegeExpiredTs = 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. Supported character scopes are:
  • 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.
  • Role_Publisher(1): (Default) The user has the privilege of a publisher, that is, the user can publish streams in the channel.
  • Role_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 FAQ How do I use co-host authentication.
privilegeExpiredTsThe Unix timestamp (s) when the token expires, represented by the sum of the current timestamp and the valid time of the token. For example, if you set privilegeExpiredTs as the current timestamp plus 600 seconds, the token expires in 10 minutes. A token is valid for 24 hours at most. If you set this parameter as 0 or a period longer than 24 hours, the token is still valid for 24 hours.

Upgrade from AccessToken to AccessToken2

Update the token server

  1. Replace the rtctokenbuilder import statement, and remove the "time" import statement:


    _13
    import (
    _13
    // Replace
    _13
    // rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/RtcTokenBuilder"
    _13
    rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
    _13
    "fmt"
    _13
    "log"
    _13
    "net/http"
    _13
    // Remove
    _13
    // "time"
    _13
    "encoding/json"
    _13
    "errors"
    _13
    "strconv"
    _13
    )

  2. Remove the timestamp generation statements. Add tokenExpireTimeInSeconds and privilegeExpireTimeInSeconds:


    _7
    // expireTimeInSeconds := uint32(40)
    _7
    // Gets current timestamp.
    _7
    // currentTimestamp := uint32(time.Now().UTC().Unix())
    _7
    // Timestamp when the token expires.
    _7
    // expireTimestamp := currentTimestamp + expireTimeInSeconds
    _7
    tokenExpireTimeInSeconds := uint32(40)
    _7
    privilegeExpireTimeInSeconds := uint32(40)

  3. Update the token builder function:


    _6
    // Before
    _6
    // result, err := rtctokenbuilder.BuildTokenWithUID(appID, appCertificate, channelName, int_uid, role, expireTimestamp)
    _6
    // After
    _6
    // Update BuildTokenWithUID with BuildTokenWithUid
    _6
    // Update expireTimestamp with tokenExpireTimeInSeconds and privilegeExpireTimeInSeconds
    _6
    result, err := rtctokenbuilder.BuildTokenWithUid(appID, appCertificate, channelName, int_uid, role, tokenExpireTimeInSeconds, privilegeExpireTimeInSeconds)

  4. Remove unsupported roles:


    _14
    switch role_num {
    _14
    // Remove
    _14
    // case 0:
    _14
    // DEPRECATED. RoleAttendee has the same privileges as RolePublisher.
    _14
    // role = rtctokenbuilder.RoleAttendee
    _14
    case 1:
    _14
    role = rtctokenbuilder.RolePublisher
    _14
    case 2:
    _14
    role = rtctokenbuilder.RoleSubscriber
    _14
    // Remove
    _14
    // case 101:
    _14
    // DEPRECATED. RoleAdmin has the same privileges as RolePublisher.
    _14
    // role = rtctokenbuilder.RoleAdmin
    _14
    }

Update the client

The client does not need any updates as long as the Video Calling Web SDK used in the Web client is v4.8.0 or later. For more information about the SDK versions that support AccessToken2, see SDK compatibility for AccessToken2.

In the index.html file, make the following changes:


_14
<html>
_14
<head>
_14
<title>Token demo</title>
_14
</head>
_14
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
_14
<body>
_14
<h1>Token demo</h1>
_14
<!-- Remove -->
_14
<!-- <script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script>-->
_14
<!-- Upgrade SDK to support AccessToken2.-->
_14
<script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.8.0.js"></script>
_14
<script src="./client.js"></script>
_14
</body>
_14
</html>

Test the AccessToken2 server

If you are also using other Video SDK related products or services such as Cloud Recording and streaming, Agora recommends you contact the Agora technical support team before upgrading to AccessToken2.

To test the AccessToken2 server, open index.html with a supported browser to perform the following actions:

  • Successfully joining a channel.
  • Renewing a token every 10 seconds.

Voice Calling