Secure authentication with tokens
Authentication is the process of validating the identity of each user before they access a system. Agora uses digital tokens to authenticate users and their privileges. A token is a string used to verify user privileges when joining a channel. When a user connects to Agora and passes in the token, the server verifies the user's identity and permissions based on the information in the token.
Understand the tech
Agora enables you to implement permissions in two distinct ways according to your scenario:
-
Uniform permissions: Uniformly set permissions for joining channels and concurrent streams. This is applicable to most scenarios. For details, see Implement basic authentication.
-
Finely control permissions for publishing different streams: Manage permissions to publish different types of streams, such as audio, video, and data. If you have higher requirements for business security, use these permissions to finely control the validity period of permissions to publish different streams. For details, see Co-host token authentication.
The information on this page only applies to AccessToken2
. If you are using AccessToken
, refer to the AccessToken upgrade guide to upgrade from AccessToken
to AccessToken2
.
An authentication token is a dynamic key that is valid for a maximum of 24 hours. On request, a token server returns an authentication token that is valid to join a specific channel or wildcard channels.
When users attempt to connect to an Agora channel, your app retrieves a token from the token server in your security infrastructure. Your app then sends this token to Agora SD-RTN™ for authentication. Agora SD-RTN™ validates the token and reads the information stored in the token.
The following figure shows the call flow you implement to create step-up-authentication with Agora Cloud Recording:
Implement basic authentication
This section shows you how to implement basic authentication by generating a token and joining a channel. To ensure the security of your business, Agora recommends that you authenticate every client that joins a channel.
Generate a token
Agora provides an open source token generator code repository on GitHub. The repository provides code in the following languages to generate tokens on your own server, based on the HMAC-SHA256
algorithm:
Refer to the following code samples to generate a token with basic permissions:
- Golang
- Node.js
- PHP
- Python/Python3
- Java
- C++
package mainimport ( "fmt" "os" rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2")func main() { // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console. appId := os.Getenv("AGORA_APP_ID") // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console appCertificate := os.Getenv("AGORA_APP_CERTIFICATE") // Replace channelName with the name of the channel you want to join channelName := "channelName" // Fill in your actual user ID uid := uint32(2882341273) // Token validity time in seconds tokenExpirationInSeconds := uint32(3600) // The validity time of all permissions in seconds privilegeExpirationInSeconds := uint32(3600) fmt.Println("App Id:", appId) fmt.Println("App Certificate:", appCertificate) if appId == "" || appCertificate == "" { fmt.Println("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE") return } // Generate Token result, err := rtctokenbuilder.BuildTokenWithUid(appId, appCertificate, channelName, uid, rtctokenbuilder.RoleSubscriber, tokenExpirationInSeconds, privilegeExpirationInSeconds) if err != nil { fmt.Println(err) } else { fmt.Printf("Token with int uid: %s\n", result) }}
const RtcTokenBuilder = require("../src/RtcTokenBuilder2").RtcTokenBuilder;const RtcRole = require("../src/RtcTokenBuilder2").Role;// Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.const appId = process.env.AGORA_APP_ID;// Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora consoleconst appCertificate = process.env.AGORA_APP_CERTIFICATE;// Replace channelName with the name of the channel you want to joinconst channelName = "channelName";// Fill in your actual user IDconst uid = 2882341273;// Set streaming permissionsconst role = RtcRole.PUBLISHER;// Token validity time in secondsconst tokenExpirationInSecond = 3600;// The validity time of all permissions in secondsconst privilegeExpirationInSecond = 3600;console.log("App Id:", appId);console.log("App Certificate:", appCertificate);if (appId == undefined || appId == "" || appCertificate == undefined || appCertificate == "") { console.log("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE"); process.exit(1);}// Generate Tokenconst tokenWithUid = RtcTokenBuilder.buildTokenWithUid(appId, appCertificate, channelName, uid, role, tokenExpirationInSecond, privilegeExpirationInSecond);console.log("Token with int uid:", tokenWithUid);
<?phpinclude("../src/RtcTokenBuilder2.php");// Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.$appId = getenv("AGORA_APP_ID");// Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console$appCertificate = getenv("AGORA_APP_CERTIFICATE");// Replace channelName with the name of the channel you want to join$channelName = "channelName";// Fill in your actual user ID$uid = 2882341273;// Token validity time in seconds$tokenExpirationInSeconds = 3600;// The validity time of all permissions in seconds$privilegeExpirationInSeconds = 3600;echo "App Id: " . $appId . PHP_EOL;echo "App Certificate: " . $appCertificate . PHP_EOL;if ($appId == "" || $appCertificate == "") { echo "Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE" . PHP_EOL; exit;}// Generate Token$token = RtcTokenBuilder2::buildTokenWithUid($appId, $appCertificate, $channelName, $uid, RtcTokenBuilder2::ROLE_PUBLISHER, $tokenExpirationInSeconds, $privilegeExpirationInSeconds);echo 'Token with int uid: ' . $token . PHP_EOL;
import osimport syssys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))from src.RtcTokenBuilder2 import *def main(): # Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console. app_id = os.environ.get("AGORA_APP_ID") # Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console app_certificate = os.environ.get("AGORA_APP_CERTIFICATE") # Replace channelName with the name of the channel you want to join channel_name = "channel_name" # Fill in your actual user ID uid = 2882341273 # Token validity time in seconds token_expiration_in_seconds = 3600 # The validity time of all permissions in seconds privilege_expiration_in_seconds = 3600 print("App Id: %s" % app_id) print("App Certificate: %s" % app_certificate) if not app_id or not app_certificate: print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE") return # Generate Token token = RtcTokenBuilder.build_token_with_uid(app_id, app_certificate, channel_name, uid, Role_Subscriber, token_expiration_in_seconds, privilege_expiration_in_seconds) print("Token with int uid: {}".format(token))if __name__ == "__main__": main()
package io.agora.sample;import io.agora.media.RtcTokenBuilder2;import io.agora.media.RtcTokenBuilder2.Role;public class RtcTokenBuilder2Sample { // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console. static String appId = System.getenv("AGORA_APP_ID"); // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console static String appCertificate = System.getenv("AGORA_APP_CERTIFICATE"); // Replace channelName with the name of the channel you want to join static String channelName = "channelName"; // Fill in your actual user ID static int uid = 2082341273; // Token validity time in seconds static int tokenExpirationInSeconds = 3600; // The validity time of all permissions in seconds static int privilegeExpirationInSeconds = 3600; public static void main(String[] args) { System.out.printf("App Id: %s\n", appId); System.out.printf("App Certificate: %s\n", appCertificate); if (appId == null || appId.isEmpty() || appCertificate == null || appCertificate.isEmpty()) { System.out.printf("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE\n"); return; } // Generate Token RtcTokenBuilder2 token = new RtcTokenBuilder2(); String result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, Role.ROLE_SUBSCRIBER, tokenExpirationInSeconds, privilegeExpirationInSeconds); System.out.printf("Token with uid: %s\n", result); }}
#include <cstdlib>#include <iostream>#include "../src/RtcTokenBuilder2.h"using namespace agora::tools;int main(int argc, char const *argv[]) { (void)argc; (void)argv; // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console const char *env_app_id = getenv("AGORA_APP_ID"); std::string app_id = env_app_id ? env_app_id : ""; // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console const char *env_app_certificate = getenv("AGORA_APP_CERTIFICATE"); std::string app_certificate = env_app_certificate ? env_app_certificate : ""; // Replace channelName with the name of the channel you want to join std::string channel_name = "channelName"; // Fill in your actual user ID uint32_t uid = 2882341273 // Token validity time in seconds uint32_t token_expiration_in_seconds = 3600; // The validity time of all permissions in seconds uint32_t privilege_expiration_in_seconds = 3600; std::string result; std::cout << "App Id:" << app_id << std::endl; std::cout << "App Certificate:" << app_certificate << std::endl; if (app_id == "" || app_certificate == "") { std::cout << "Need to set environment variable AGORA_APP_ID and " "AGORA_APP_CERTIFICATE" << std::endl; return -1; } // Generate Token result = RtcTokenBuilder2::BuildTokenWithUid( app_id, app_certificate, channel_name, uid, UserRole::kRolePublisher, token_expiration_in_seconds, privilege_expiration_in_seconds); std::cout << "Token With Int Uid:" << result << std::endl; return 0;}
The code samples presented on this page use an integer user ID to generate a token. To use a string user ID, refer to the Token Generator code repository to view the corresponding code sample in the desired language.
Use a token
The client requests a token from your authentication server corresponding to the user ID and the channel name. You use the received token to join a channel.
Token expiration
After you join a channel using a token, the SDK triggers an onTokenPrivilegeWillExpire
callback, 30 seconds before the token is set to expire.
When the token expires, the SDK triggers an onRequestToken
callback. After receiving the callback, you regenerate a new token on the server side, and then update the token in one of the following ways:
Single channel scenario
-
Call
renewToken
to pass in the newly generated Token (Recommended). -
Call
updateChannelMediaOptions
to update the token. -
Call
leaveChannel
[2/2] to leave the current channel, and then pass in a new token when callingjoinChannel
[2/2] to rejoin the channel.
Multi-channel scenario
If you call joinChannelEx
to join multiple channels, call the updateChannelMediaOptionsEx
method to update the token.
The following sample code shows calling renewToken
to update the token when receiving an onTokenPrivilegeWillExpire
callback notification.
Complete sample code
For a complete implementation of token authentication, refer to the following:
-
In
/Gradle Scripts/build.gradle(Module: <projectname>.app)
, add the following dependencies for making HTTP requests to the token server: -
In
MainActivity.java
, replace the content with the following code:Complete sample code for token authentication
package com.example.rtcquickstart; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.pm.PackageManager; import android.view.SurfaceView; import android.widget.FrameLayout; import android.widget.Toast; import io.agora.rtc2.Constants; import io.agora.rtc2.IRtcEngineEventHandler; import io.agora.rtc2.RtcEngine; import io.agora.rtc2.video.VideoCanvas; import io.agora.rtc2.ChannelMediaOptions; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.Call; import okhttp3.Callback; import com.google.gson.Gson; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; public class MainActivity extends AppCompatActivity { // Fill in the App ID from Agora console private String appId = "Your App ID"; // Fill in the channel name private String channelName = "demo"; private String token = ""; private RtcEngine mRtcEngine; private int joined = 1; private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() { @Override // Listen for the remote user to join the channel and obtain the user ID of the remote user public void onUserJoined(int uid, int elapsed) { runOnUiThread(new Runnable() { @Override public void run() { // After obtaining the user ID in the onUserJoined callback, call setupRemoteVideo to set the remote user view setupRemoteVideo(uid); } }); } @Override public void onTokenPrivilegeWillExpire(String token) { fetchToken(1234, channelName, 1); runOnUiThread(new Runnable() { @Override public void run() { Toast toast = Toast.makeText(MainActivity.this, "Token renewed", Toast.LENGTH_SHORT); toast.show(); } }); super.onTokenPrivilegeWillExpire(token); } @Override public void onRequestToken() { joined = 1; fetchToken(1234, channelName, 1); super.onRequestToken(); } }; private static final int PERMISSION_REQ_ID = 22; private static final String[] REQUESTED_PERMISSIONS = { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA }; private boolean checkSelfPermission(String permission, int requestCode) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode); return false; } return true; } // Get RTC Token private void fetchToken(int uid,String channelName,int tokenRole){ OkHttpClient client = new OkHttpClient(); MediaType JSON = MediaType.parse("application/json; charset=utf-8"); JSONObject json = new JSONObject(); try { json.put("uid", uid); json.put("ChannelName", channelName); json.put("role", tokenRole); } catch (JSONException e) { e.printStackTrace(); } RequestBody requestBody = RequestBody.create(JSON, String.valueOf(json)); Request request = new Request.Builder() .url("http://<Your Host URL and port>/fetch_rtc_token") .header("Content-Type", "application/json; charset=UTF-8") .post(requestBody) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if(response.isSuccessful()){ Gson gson = new Gson(); String result = response.body().string(); Map map = gson.fromJson(result, Map.class); new Thread(new Runnable() { @Override public void run() { token = map.get("token").toString(); // If the user has not joined the channel, use token to join the channel ChannelMediaOptions options = new ChannelMediaOptions(); if (joined != 0){joined = mRtcEngine.joinChannel(token, channelName, 1234, options);} // If the user has joined the channel, call renewToken to regenerate the token else {mRtcEngine.renewToken(token);} } }); } } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // If all permissions are granted, initialize the RtcEngine object and join the channel if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) && checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID)) { initializeAndJoinChannel(); } } protected void onDestroy() { super.onDestroy(); mRtcEngine.leaveChannel(); mRtcEngine.destroy(); } private void initializeAndJoinChannel() { try { mRtcEngine = RtcEngine.create(getBaseContext(), appId, mRtcEventHandler); } catch (Exception e) { throw new RuntimeException("Check the error."); } // In interactive live broadcast, set the channel scene to BROADCASTING mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING); // Depending on the actual situation, set the user role to BROADCASTER or AUDIENCE mRtcEngine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER); // SDK turns off video by default. Call enableVideo to enable video mRtcEngine.enableVideo(); FrameLayout container = findViewById(R.id.local_video_view_container); // Call CreateRendererView to create a SurfaceView object and add it as a child to the FrameLayout SurfaceView surfaceView = new SurfaceView (getBaseContext()); container.addView(surfaceView); // Pass the SurfaceView object to Agora to render local video mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0)); // Start local video preview mRtcEngine.startPreview(); // Get a token from the token server fetchToken(1234, channelName, 1); } private void setupRemoteVideo(int uid) { FrameLayout container = findViewById(R.id.remote_video_view_container); SurfaceView surfaceView = new SurfaceView (getBaseContext()); surfaceView.setZOrderMediaOverlay(true); container.addView(surfaceView); mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid)); } }
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Advanced authentication features
This section presents advanced functions related to token authentication.
Generate wildcard tokens
In scenarios where users need to join multiple channels, or frequently switch between channels, they need to request a new token from the token server, each time they join a channel. To solve the problem of frequently requesting tokens when switching channels, Agora enables you to generate wildcard tokens that set the user ID or channel name as a wildcard. Users reuse the same wildcard token to join different channels, which speeds up the process of switching and joining channels. It also helps reduce the pressure on your token server.
To generate wildcard tokens you need to have deployed an AccessToken2
server. If you are still using AccessToken
, refer to the AccessToken upgrade guide to upgrade to AccessToken2
.
The AccessToken2
code provides two BuildTokenWithUid
methods. Choose the method according to your needs. When generating a wildcard token, fill in the following parameters according to your requirements:
Token type | uid | channelName |
---|---|---|
Token for wildcard user ID | Set the user ID to 0 or a wildcard * to match any length of characters for user IDs of int and string types. Agora also supports extended matching, that is, matching multiple combinations of * and strings. For example, 1*a*2 means match user IDs starting with 1, containing the letter a, and ending with 2, such as 1abc2. | A string of up to 64 bytes. |
Token for wildcard channel name | The user ID of the user to be authenticated. User IDs of type int and string are supported. | Set the channel name as a wildcard * or leave it blank. To employ expanded matching, use multiple combinations of * and strings. For example, 1*a*2 matches channel names starting with 1, containing the letter a, and ending with 2, such as 1abc2. |
Precautions
-
If you have generated a token for
uid=0
, consider the following when you use the token:-
If you only need to join a single channel, set the
uid
to0
when callingjoinChannel
. -
If you need to join multiple channels, set
uid
to a specific user ID instead of0
when callingjoinChannelEx
.
-
-
In order to prevent unauthorized users from disrupting the order of the channel if the token is leaked, best practice is that users who use wildcard tokens set their client role as audience, and their token privilege
role
tokRoleSubscriber
.
If viewers using wildcard tokens need to publish streams, Agora recommends the following steps to update the token. The following API uses C++ as an example:
-
Call
setClientRole
to set the user role as Broadcaster. -
When generating a token using
BuildTokenWithUid
, specify the channel name and user ID in the method, and set the token privilegerole
tokRolePublisher
, thereby generating a token with the permission to send streams. -
Call
renewToken
to update the token.
A wildcard token is valid for 24 hours. If the token is about to expire, regenerate a new wildcard token on the server and then call renewToken
to pass in the new wildcard token.
Co-host token authentication
In addition to basic authentication, Agora also provides fine-grained control over the permissions of users in a channel to publish different types of streams. Co-host token authentication helps you ensure that only the streaming users in a channel are authorized to publish streams, thus preventing hackers from stealing tokens or exploiting vulnerabilities to blow up a live broadcast channel.
-
Before using advanced authentication features, ensure that you have enabled Co-host token authentication in Agora Console.
-
To send a stream to a channel, ensure that the client role is set to Broadcaster either at the time of joining the channel, or after joining a channel by calling
setClientRole
.
Agora provides BuildTokenWithUidAndPrivilege
methods to set the expiration time of the following privileges:
- Join a channel
- Publish audio stream in a channel
- Publish video stream in a channel
- Publish data stream in a channel
See the API reference for detailed parameter descriptions.
Generate a token with advanced permissions
Agora provides an open source token generator code repository on Github. The repository supports the use of the following languages to generate tokens on your own server, based on the HMAC-SHA256
algorithm:
Refer to the following code samples to generate a token with advanced permissions:
- Golang
- Node.js
- PHP
- Python/Python3
- Java
- C++
package mainimport ( "fmt" "os" rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2")func main() { // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console appId := os.Getenv("AGORA_APP_ID") // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console appCertificate := os.Getenv("AGORA_APP_CERTIFICATE") // Replace channelName with the name of the channel you want to join channelName := "channelName" // Fill in your actual user ID uid := uint32(2882341273) // Token validity time in seconds tokenExpirationInSeconds := uint32(3600) // Validity time for permission to join a channel (seconds) joinChannelPrivilegeExpireInSeconds := uint32(3600) // Validity in seconds for the permission to publish audio streams in the channel pubAudioPrivilegeExpireInSeconds := uint32(3600) // Validity in seconds for the permission to publish video streams in the channel pubVideoPrivilegeExpireInSeconds := uint32(3600) // Validity in seconds for the permission to publish data streams in the channel pubDataStreamPrivilegeExpireInSeconds := uint32(3600) fmt.Println("App Id:", appId) fmt.Println("App Certificate:", appCertificate) if appId == "" || appCertificate == "" { fmt.Println("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE") return } // Generate Token result, err := rtctokenbuilder.BuildTokenWithUidAndPrivilege( appId, appCertificate, channelName, uid, tokenExpirationInSeconds, joinChannelPrivilegeExpireInSeconds, pubAudioPrivilegeExpireInSeconds, pubVideoPrivilegeExpireInSeconds, pubDataStreamPrivilegeExpireInSeconds, ) if err != nil { fmt.Println(err) } else { fmt.Printf("Token with int uid and privilege: %s\n", result) }}
const RtcTokenBuilder = require("../src/RtcTokenBuilder2").RtcTokenBuilder;// Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora consoleconst appId = process.env.AGORA_APP_ID;// Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora consoleconst appCertificate = process.env.AGORA_APP_CERTIFICATE;// Replace channelName with the name of the channel you want to joinconst channelName = "channelName";// Fill in your actual user IDconst uid = 2082341273;// Token validity time in secondsconst tokenExpirationInSecond = 3600;// Validity time for permission to join a channel (seconds)const joinChannelPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish audio streams in the channelconst pubAudioPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish video streams in the channelconst pubVideoPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish data streams in the channelconst pubDataStreamPrivilegeExpireInSeconds = 3600;console.log("App Id:", appId);console.log("App Certificate:", appCertificate);if (appId == undefined || appId == "" || appCertificate == undefined || appCertificate == "") { console.log("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE"); process.exit(1);}// Generate Tokenconst tokenWithUidAndPrivilege = RtcTokenBuilder.buildTokenWithUidAndPrivilege( appId, appCertificate, channelName, uid, tokenExpirationInSecond, joinChannelPrivilegeExpireInSeconds, pubAudioPrivilegeExpireInSeconds, pubVideoPrivilegeExpireInSeconds, pubDataStreamPrivilegeExpireInSeconds);console.log("Token with int uid and privilege:", tokenWithUidAndPrivilege);
<?phpinclude("../src/RtcTokenBuilder2.php");// Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console$appId = getenv("AGORA_APP_ID");// Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console$appCertificate = getenv("AGORA_APP_CERTIFICATE");// Replace channelName with the name of the channel you want to join$channelName = "channelName";// Fill in your actual user ID$uid = 2882341273;// Token validity time in seconds$tokenExpirationInSeconds = 3600;// Validity time for permission to join a channel (seconds)$joinChannelPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish audio streams in the channel$pubAudioPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish video streams in the channel$pubVideoPrivilegeExpireInSeconds = 3600;// Validity in seconds for the permission to publish data streams in the channel$pubDataStreamPrivilegeExpireInSeconds = 3600;echo "App Id: " . $appId . PHP_EOL;echo "App Certificate: " . $appCertificate . PHP_EOL;if ($appId == "" || $appCertificate == "") { echo "Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE" . PHP_EOL; exit;}// Generate Token$token = RtcTokenBuilder2::buildTokenWithUidAndPrivilege($appId, $appCertificate, $channelName, $uid, $tokenExpirationInSeconds, $joinChannelPrivilegeExpireInSeconds, $pubAudioPrivilegeExpireInSeconds, $pubVideoPrivilegeExpireInSeconds, $pubDataStreamPrivilegeExpireInSeconds);echo 'Token with int uid and privilege: ' . $token . PHP_EOL;
import osimport syssys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))from src.RtcTokenBuilder2 import *def main(): # Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console app_id = os.environ.get("AGORA_APP_ID") # Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console app_certificate = os.environ.get("AGORA_APP_CERTIFICATE") # 将 channel_name 替换为需要加入的频道名 channel_name = "channel_name" # Fill in your actual user ID uid = 2882341273 # Token validity time in seconds token_expiration_in_seconds = 3600 # Validity time for permission to join a channel (seconds) join_channel_privilege_expiration_in_seconds = 3600 # Validity in seconds for the permission to publish audio streams in the channel pub_audio_privilege_expiration_in_seconds = 3600 # Validity in seconds for the permission to publish video streams in the channel pub_video_privilege_expiration_in_seconds = 3600 # Validity in seconds for the permission to publish data streams in the channel pub_data_stream_privilege_expiration_in_seconds = 3600 print("App Id: %s" % app_id) print("App Certificate: %s" % app_certificate) if not app_id or not app_certificate: print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE") return # Generate Token token = RtcTokenBuilder.build_token_with_uid_and_privilege( app_id, app_certificate, channel_name, uid, token_expiration_in_seconds, join_channel_privilege_expiration_in_seconds, pub_audio_privilege_expiration_in_seconds, pub_video_privilege_expiration_in_seconds, pub_data_stream_privilege_expiration_in_seconds) print("Token with int uid and privilege: {}".format(token))if __name__ == "__main__": main()
package io.agora.sample;import io.agora.media.RtcTokenBuilder2;public class RtcTokenBuilder2Sample { // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console static String appId = System.getenv("AGORA_APP_ID"); // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console static String appCertificate = System.getenv("AGORA_APP_CERTIFICATE"); // Replace channelName with the name of the channel you want to join static String channelName = "channelName"; // Fill in your actual user ID static int uid = 2082341273; // Token validity time in seconds static int tokenExpirationInSeconds = 3600; // Validity time for permission to join a channel (seconds) static int joinChannelPrivilegeExpireInSeconds = 3600; // Validity in seconds for the permission to publish audio streams in the channel static int pubAudioPrivilegeExpireInSeconds = 3600; // Validity in seconds for the permission to publish video streams in the channel static int pubVideoPrivilegeExpireInSeconds = 3600; // Validity in seconds for the permission to publish data streams in the channel static int pubDataStreamPrivilegeExpireInSeconds = 3600; public static void main(String[] args) { System.out.printf("App Id: %s\n", appId); System.out.printf("App Certificate: %s\n", appCertificate); if (appId == null || appId.isEmpty() || appCertificate == null || appCertificate.isEmpty()) { System.out.printf("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE\n"); return; } // Generate Token result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, tokenExpirationInSeconds, joinChannelPrivilegeExpireInSeconds, pubAudioPrivilegeExpireInSeconds, pubVideoPrivilegeExpireInSeconds, pubDataStreamPrivilegeExpireInSeconds); System.out.printf("Token with uid and privilege: %s\n", result); }}
#include <cstdlib>#include <iostream>#include "../src/RtcTokenBuilder2.h"using namespace agora::tools;int main(int argc, char const *argv[]) { (void)argc; (void)argv; // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console const char *env_app_id = getenv("AGORA_APP_ID"); std::string app_id = env_app_id ? env_app_id : ""; // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console const char *env_app_certificate = getenv("AGORA_APP_CERTIFICATE"); std::string app_certificate = env_app_certificate ? env_app_certificate : ""; // Replace channelName with the name of the channel you want to join std::string channel_name = "channelName"; // Fill in your actual user ID uint32_t uid = 2882341273; // Token validity period (seconds) uint32_t token_expiration_in_seconds = 3600; // Validity time of permission to join channel (seconds) uint32_t join_channel_privilege_expiration_in_seconds = 3600; // The validity period of the permission to publish audio streams in the channel (seconds) uint32_t pub_audio_privilege_expiration_in_seconds = 3600; // The validity period of the permission to publish video streams in the channel (seconds) uint32_t pub_video_privilege_expiration_in_seconds = 3600; // The validity period of the permission to publish data streams in the channel (seconds) uint32_t pub_data_stream_privilege_expiration_in_seconds = 3600; std::string result; std::cout << "App Id:" << app_id << std::endl; std::cout << "App Certificate:" << app_certificate << std::endl; if (app_id == "" || app_certificate == "") { std::cout << "Need to set environment variable AGORA_APP_ID and " "AGORA_APP_CERTIFICATE" << std::endl; return -1; } // Generate Token result = RtcTokenBuilder2::BuildTokenWithUid( app_id, app_certificate, channel_name, uid, token_expiration_in_seconds, join_channel_privilege_expiration_in_seconds, pub_audio_privilege_expiration_in_seconds, pub_video_privilege_expiration_in_seconds, pub_data_stream_privilege_expiration_in_seconds); std::cout << "Token With Int Uid:" << result << std::endl; return 0;}
Deployment and debugging
This section shows you how to quickly deploy a token generator locally. After successful deployment, you can use the generated tokens to test and debug your project.
Deploy a token generator through Docker
Refer to the following steps to deploy a Docker-based token generator locally:
-
Install Docker.
-
Open a terminal and run the following command to start the token generator. Replace
[YOUR_APP_ID]
and[YOUR_APP_CERTIFICATE]
with the App ID and App certificate you obtained from Agora Console. -
Run the following command to request a token from the token generator you just deployed:
After the request is successful, your terminal displays the generated token.
Manually deploy a token generator locally
This guide uses the Golang language as an example. Before starting, ensure that you have Golang version 1.14 or above.
Refer to the following steps to build and run a token generator locally to generate tokens:
-
Create a new
token-server
folder. Create aserver.go
file inside the folder and paste the following Golang sample code into the file. Replace the values forappID
andappCertificate
with your App ID and App certificate.Sample code for a token server
package main import ( rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2" "fmt" "log" "net/http" "encoding/json" "errors" "strconv" ) type rtc_int_token_struct struct{ Uid_rtc_int uint32 `json:"uid"` Channel_name string `json:"ChannelName"` Role uint32 `json:"role"` } var rtc_token string var int_uid uint32 var channel_name string var role_num uint32 var role rtctokenbuilder.Role // Use RtcTokenBuilder to generate RTC Token func generateRtcToken(int_uid uint32, channelName string, role rtctokenbuilder.Role){ appID := "<Your App ID>" appCertificate := "<Your App Certificate>" // Ensure that the expiration time of the token is later than the permission expiration time. Permission expiration time, unit is seconds tokenExpireTimeInSeconds := uint32(40) // The token-privilege-will-expire callback is triggered 30 seconds before the permission expires. // For demonstration purposes, set the expiration time to 40 seconds. You can see the process of the client automatically updating the Token privilegeExpireTimeInSeconds := uint32(40) result, err := rtctokenbuilder.BuildTokenWithUid(appID, appCertificate, channelName, int_uid, role, tokenExpireTimeInSeconds, privilegeExpireTimeInSeconds) if err != nil { fmt.Println(err) } else { fmt.Printf("Token with uid: %s\n", result) fmt.Printf("uid is %d\n", int_uid ) fmt.Printf("ChannelName is %s\n", channelName) fmt.Printf("Role is %d\n", role) } rtc_token = result } func rtcTokenHandler(w http.ResponseWriter, r *http.Request){ w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS"); w.Header().Set("Access-Control-Allow-Headers", "*"); if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } if r.Method != "POST" && r.Method != "OPTIONS" { http.Error(w, "Unsupported method. Please check.", http.StatusNotFound) return } var t_int rtc_int_token_struct var unmarshalErr *json.UnmarshalTypeError int_decoder := json.NewDecoder(r.Body) int_err := int_decoder.Decode(&t_int) if (int_err == nil) { int_uid = t_int.Uid_rtc_int channel_name = t_int.Channel_name role_num = t_int.Role switch role_num { case 1: role = rtctokenbuilder.RolePublisher case 2: role = rtctokenbuilder.RoleSubscriber } } if (int_err != nil) { if errors.As(int_err, &unmarshalErr){ errorResponse(w, "Bad request. Wrong type provided for field " + unmarshalErr.Value + unmarshalErr.Field + unmarshalErr.Struct, http.StatusBadRequest) } else { errorResponse(w, "Bad request.", http.StatusBadRequest) } return } generateRtcToken(int_uid, channel_name, role) errorResponse(w, rtc_token, http.StatusOK) log.Println(w, r) } func errorResponse(w http.ResponseWriter, message string, httpStatusCode int){ w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.WriteHeader(httpStatusCode) resp := make(map[string]string) resp["token"] = message resp["code"] = strconv.Itoa(httpStatusCode) jsonResp, _ := json.Marshal(resp) w.Write(jsonResp) } func main(){ // Use int type uid to generate RTC Token http.HandleFunc("/fetch_rtc_token", rtcTokenHandler) fmt.Printf("Starting server at port 8082") if err := http.ListenAndServe(":8082", nil); err != nil { log.Fatal(err) } }
-
Open the terminal, go to the
token-server
folder path, and run the following command line to create ago.mod
file for your token generator. This file defines the import path and dependencies: -
Run the following command to install dependencies.
-
Run the following command to start the token generator:
After the token generator is deployed successfully, your terminal returns the token generated based on the App ID and App certificate you filled in.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Compatibility
AccessToken2
is supported on the following versions of Agora SDK (excluding client side bypass push function):
For RTC 4.x SDK:
- Android, iOS, macOS, Windows, Electron, Unity, React Native, or Unreal SDK version must be greater than or equal to v4.0.0
- Flutter SDK version must be greater than or equal to v6.0.0
- Web SDK version must be greater than or equal to v4.8.0
SDKs using AccessToken2
can interoperate with SDKs using AccessToken
. The SDK versions that supports AccessToken2
also supports AccessToken
.
API Reference
This section documents the core methods you use to generate tokens, using the Golang code as an example.
BuildTokenWithUid
Generate a token, set the expiration time, and define all permissions.
Parameters
appId
: The App ID generated when you create a project in Agora Console.appCertificate
: The App certificate of your project.channelName
: Channel name, length should be less than 64 bytes.uid
: The unique user ID of the user to be authenticated. It is a 32-bit signed integer with a value range from -231 to 231 - 1.role
: User permissions, which determine whether the user can send streams in the channel.kRolePublisher
(1): (Default) The user has the permission to send streams in the channel.kRoleSubscriber
(2): The user has receiving permission only. The user can receive streams in the channel, but cannot send streams. This parameter only takes effect after Co-host authentication is enabled.
tokenExpire
: The token validity period (seconds). The maximum validity period is 24 hours.privilegeExpire
: Validity time of all permissions (seconds). If set to 0 (default value), it means that the permission will never expire.
If the token expires but the permissions have not expired, the user remains in the channel and can continue to send streams. Any callbacks related to the token in the SDK are not triggered. Once disconnected from the channel, the user is not able to use the expired token to join the same channel. Best practice is to use consistent settings for the token expiration time and the permission expiration time.
BuildTokenWithUidAndPrivilege
Generate a token with fine control over streaming permissions. Set the validity period of the token and the expiration time of permissions to join channels, publish audio, video, and data streams.
Parameters
appId
: The App ID generated when you create a project in Agora Console.appCertificate
: The App certificate of your project.channelName
: Channel name, length should be less than 64 bytes.uid
: The unique user ID of the user to be authenticated. It is a 32-bit signed integer with a value range from -231 to 231 - 1.tokenExpire
: The token validity period (seconds). The maximum validity period is 24 hours.joinChannelPrivilegeExpire
: Validity in seconds for the permission to join the channel (seconds).pubAudioPrivilegeExpire
: Validity in seconds for the permission to publish audio streams in the channel.pubVideoPrivilegeExpire
: Validity in seconds for the permission to publish video streams in the channel.pubDataStreamPrivilegeExpire
: Validity in seconds for the permission to publish data streams in the channel.
- The expiration time of the different permissions is calculated from the time the token is generated.
- Agora recommends that the token expiration time be consistent with the expiration time of other permissions.
- The permission to publish audio, video and data streams in the channel only takes effect after Co-host token authentication service is enabled. If the permission to join the channel expires earlier than the permission to publish an audio stream, the user is kicked out of the channel after the permission to join the channel expires.