For AI agents: see the complete documentation index at /llms.txt.
Use tokens
Updated
Retrieve tokens generated by an authentication token server to securely connect to Agora.
To protect your business, it is best practice to authenticate every client that joins a channel. This guide explains how to fetch an authentication token from your token server, use it to join a channel, and renew the token when it expires.
Understand the tech
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
String channelId = "xxx";
// User ID
int uid = 0;
// Request a token from the server corresponding to channelId and uid
String token = getToken(channelId, uid);
// Set channel media options
ChannelMediaOptions option = new ChannelMediaOptions();
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
// Join a channel
engine.joinChannel(token, channelId, 0, option);// Channel name
val channelId = "xxx"
// User ID
val uid = 0
// Request a token from the server corresponding to channelId and uid
val token = getToken(channelId, uid)
// Set channel media options
val option = ChannelMediaOptions().apply {
clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
}
// Join a channel
engine.joinChannel(token, channelId, 0, option)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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelMediaOptionsto 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 use-case
If you call joinChannelEx to join multiple channels, call the updateChannelMediaOptionsEx method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an onTokenPrivilegeWillExpire callback notification.
class RtcEngineEventHandlerImpl extends IRtcEngineEventHandler {
// Callback is triggered when the token is about to expire
@Override
public void onTokenPrivilegeWillExpire(String token) {
super.onTokenPrivilegeWillExpire(token);
// Request to generate a fresh token
String token = getToken();
// Update token
engine.renewToken(token);
}
}class RtcEngineEventHandlerImpl : IRtcEngineEventHandler() {
// Callback triggered when the token is about to expire
override fun onTokenPrivilegeWillExpire(token: String) {
super.onTokenPrivilegeWillExpire(token)
// Request to generate a fresh token
val newToken = getToken()
// Update the token
engine.renewToken(newToken)
}
}Complete sample code
This section presents sample code to implement token authentication in your client.
-
In
/Gradle Scripts/build.gradle(Module: <projectname>.app), add the following dependencies for making HTTP requests to the token server:dependencies { implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.google.code.gson:gson:2.8.4' } -
In
MainActivity, 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
public void onUserJoined(int uid, int elapsed) {
runOnUiThread(() -> setupRemoteVideo(uid));
}
@Override
public void onTokenPrivilegeWillExpire(String token) {
fetchToken(1234, channelName, 1);
runOnUiThread(() -> {
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(() -> {
token = map.get("token").toString();
ChannelMediaOptions options = new ChannelMediaOptions();
if (joined != 0) {
joined = mRtcEngine.joinChannel(token, channelName, 1234, options);
} else {
mRtcEngine.renewToken(token);
}
}).start();
}
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID)) {
initializeAndJoinChannel();
}
}
@Override
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.");
}
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
mRtcEngine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
mRtcEngine.enableVideo();
FrameLayout container = findViewById(R.id.local_video_view_container);
SurfaceView surfaceView = new SurfaceView(getBaseContext());
container.addView(surfaceView);
mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));
mRtcEngine.startPreview();
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));
}
}package com.example.rtcquickstart
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import io.agora.rtc2.*;
import io.agora.rtc2.video.VideoCanvas;
import com.google.gson.Gson;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.*;
class MainActivity : AppCompatActivity() {
// Fill in the App ID from Agora console
private val appId = "Your App ID"
// Fill in the channel name
private val channelName = "demo"
private var token = ""
private var mRtcEngine: RtcEngine? = null
private var joined = 1
private val mRtcEventHandler = object : IRtcEngineEventHandler() {
override fun onUserJoined(uid: Int, elapsed: Int) {
runOnUiThread {
// After obtaining the user ID in the onUserJoined callback, call setupRemoteVideo to set the remote user view
setupRemoteVideo(uid)
}
}
override fun onTokenPrivilegeWillExpire(token: String) {
fetchToken(1234, channelName, 1)
runOnUiThread {
val toast = Toast.makeText(this@MainActivity, "Token renewed", Toast.LENGTH_SHORT)
toast.show()
}
super.onTokenPrivilegeWillExpire(token)
}
override fun onRequestToken() {
joined = 1
fetchToken(1234, channelName, 1)
super.onRequestToken()
}
}
companion object {
private const val PERMISSION_REQ_ID = 22
private val REQUESTED_PERMISSIONS = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
)
}
private fun checkSelfPermission(permission: String, requestCode: Int): Boolean {
return if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode)
false
} else {
true
}
}
// Get RTC Token
private fun fetchToken(uid: Int, channelName: String, tokenRole: Int) {
val client = OkHttpClient()
val JSON = MediaType.parse("application/json; charset=utf-8")
val json = JSONObject()
try {
json.put("uid", uid)
json.put("ChannelName", channelName)
json.put("role", tokenRole)
} catch (e: JSONException) {
e.printStackTrace()
}
val requestBody = RequestBody.create(JSON, json.toString())
val request = Request.Builder()
.url("http://<Your Host URL and port>/fetch_rtc_token")
.header("Content-Type", "application/json; charset=UTF-8")
.post(requestBody)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Handle failure
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val gson = Gson()
val result = response.body?.string()
val map: Map<String, Any> = gson.fromJson(result, Map::class.java)
Thread {
token = map["token"].toString()
// If the user has not joined the channel, use token to join the channel
val options = ChannelMediaOptions()
if (joined != 0) {
joined = mRtcEngine?.joinChannel(token, channelName, 1234, options) ?: -1
} else {
mRtcEngine?.renewToken(token)
}
}.start()
}
}
})
}
override fun onCreate(savedInstanceState: Bundle?) {
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()
}
}
override fun onDestroy() {
super.onDestroy()
mRtcEngine?.leaveChannel()
mRtcEngine?.destroy()
}
private fun initializeAndJoinChannel() {
try {
mRtcEngine = RtcEngine.create(baseContext, appId, mRtcEventHandler)
} catch (e: Exception) {
throw 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()
val container: FrameLayout = findViewById(R.id.local_video_view_container)
// Call CreateRendererView to create a SurfaceView object and add it as a child to the FrameLayout
val surfaceView = SurfaceView(baseContext)
container.addView(surfaceView)
// Pass the SurfaceView object to Agora to render local video
mRtcEngine?.setupLocalVideo(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 fun setupRemoteVideo(uid: Int) {
val container: FrameLayout = findViewById(R.id.remote_video_view_container)
val surfaceView = SurfaceView(baseContext)
surfaceView.setZOrderMediaOverlay(true)
container.addView(surfaceView)
mRtcEngine?.setupRemoteVideo(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.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
let channelId: String = "xxxx"
// User ID
let uid: UInt = 0
// Request the server to generate a token corresponding to channelId and uid
let token = getToken()
// Set channel media options
let mediaOption = AgoraRtcChannelMediaOptions()
// Set the user role as host
mediaOption.clientRoleType = .broadcaster
// Use the token to join a channel
agoraKit.joinChannel(byToken: token, channelId: channelId, uid: uid, mediaOptions: mediaOption)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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelWithMediaOptionsto update the token. -
Call
leaveChannel[2/2] to leave the current channel, and then pass in a new token when callingjoinChannelByToken[2/4] to rejoin the channel.
Multi-channel use-case
If you call joinChannelEx to join multiple channels, call the updateChannelExWithMediaOptions method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an tokenPrivilegeWillExpire callback notification.
extension JoinChannelVideoToken: AgoraRtcEngineDelegate {
// Callback is triggered when the token is about to expire
func rtcEngine(_ engine: AgoraRtcEngineKit, tokenPrivilegeWillExpire token: String) {
// Request to generate a fresh token
let token = getToken()
// Update token
engine.renewToken(token)
}
}Complete sample code
For a complete implementation of token authentication, replace the content in ViewController.swift with the following code. Replace Your App ID with your App ID and <Your Host URL and port> with the host URL and port of the local Golang server you have deployed. For example, 123.1.23.123
Complete sample code for token authentication
import UIKit
import AgoraRtcKit
import Foundation
public enum TokenError: Error{
case noData
case invalidData
}
class ViewController: UIViewController {
var localView: UIView!
var remoteView: UIView!
var agoraKit: AgoraRtcEngineKit!
override func viewDidLoad() {
super.viewDidLoad()
//After loading the view, you can make other settings
initView()
initializeAgoraEngine()
setClientRole()
setupLocalVideo()
fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.joinChannel(token: token)
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
override func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
remoteView.frame = self.view.bounds
localView.frame = CGRect(x: self.view.bounds.width - 90, y: 0, width: 90, height: 160)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
leaveChannel()
destroy()
}
func initView(){
remoteView = UIView()
self.view.addSubview(remoteView)
localView = UIView()
self.view.addSubview(localView)
}
func initializeAgoraEngine(){
let config = AgoraRtcEngineConfig()
config.appId = "Your App ID"
config.channelProfile = .liveBroadcasting
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
if agoraKit != nil{
print("Initialization successful")
}
else{
print("Initialization failed")
}
}
func setClientRole(){
agoraKit.setClientRole(.broadcaster)
}
func setupLocalVideo(){
agoraKit.enableVideo()
agoraKit.startPreview()
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = 0
videoCanvas.renderMode = .hidden
videoCanvas.view = localView
agoraKit.setupLocalVideo(videoCanvas)
}
func joinChannel(token:String){
let option = AgoraRtcChannelMediaOptions()
agoraKit.joinChannel(byToken: token, channelId: "test", uid: 123456, mediaOptions: option)
}
func leaveChannel(){
agoraKit.stopPreview()
agoraKit.leaveChannel(nil)
}
func destroy(){
AgoraRtcEngineKit.destroy()
}
func fetchToken(channelName: String, userId: UInt, role: UInt,
callback: @escaping (Result<String, Error>) -> Void
){
let url = URL(string: "http://<Your Host URL and port>/fetch_rtc_token")
let parameters = ["uid":userId,"channelName": channelName, "role": role] as [String : Any]
print(parameters.self)
var request = URLRequest(
url: url!,
timeoutInterval: 10
)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
}
catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: request){data, _, err in
guard let data = data else {
if let err = err {
callback(.failure(err))
}
else {
callback(.failure(TokenError.noData))
}
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseDict = responseJSON as? [String: Any], let token = responseDict["token"] as? String {
callback(.success(token))
} else {
callback(.failure(TokenError.invalidData))
}
}.resume()
}
}
extension ViewController: AgoraRtcEngineDelegate{
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int){
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
videoCanvas.renderMode = .hidden
videoCanvas.view = remoteView
agoraKit.setupRemoteVideo(videoCanvas)
}
func rtcEngine(_ engine: AgoraRtcEngineKit, tokenPrivilegeWillExpire token: String) {
self.fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.agoraKit.renewToken(token)
print("Renewed the token")
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
func rtcEngine(_ engine: AgoraRtcEngineKit, connectionStateChanged state: AgoraConnectionState, reason: AgoraConnectionChangedReason) {
print("Connection state changed to")
print(state.rawValue)
}
func rtcEngineRequestToken(_ engine: AgoraRtcEngineKit) {
fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.joinChannel(token: token)
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
}The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
let channelId: String = "xxxx"
// User ID
let uid: UInt = 0
// Request the server to generate a token corresponding to channelId and uid
let token = getToken()
// Set channel media options
let mediaOption = AgoraRtcChannelMediaOptions()
// Set the user role as host
mediaOption.clientRoleType = .broadcaster
// Use the token to join a channel
agoraKit.joinChannel(byToken: token, channelId: channelId, uid: uid, mediaOptions: mediaOption)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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelWithMediaOptionsto update the token. -
Call
leaveChannel[2/2] to leave the current channel, and then pass in a new token when callingjoinChannelByToken[2/4] to rejoin the channel.
Multi-channel use-case
If you call joinChannelEx to join multiple channels, call the updateChannelExWithMediaOptions method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an tokenPrivilegeWillExpire callback notification.
extension JoinChannelVideoToken: AgoraRtcEngineDelegate {
// Callback is triggered when the token is about to expire
func rtcEngine(_ engine: AgoraRtcEngineKit, tokenPrivilegeWillExpire token: String) {
// Request to generate a fresh token
let token = getToken()
// Update token
engine.renewToken(token)
}
}Complete sample code
For a complete implementation of token authentication, replace the content in ViewController.swift with the following code. Replace Your App ID with your App ID and <Your Host URL and port> with the host URL and port of the local Golang server you have deployed. For example, 123.1.23.123
Complete sample code for token authentication
import UIKit
import AgoraRtcKit
import Foundation
public enum TokenError: Error{
case noData
case invalidData
}
class ViewController: UIViewController {
var localView: UIView!
var remoteView: UIView!
var agoraKit: AgoraRtcEngineKit!
override func viewDidLoad() {
super.viewDidLoad()
//After loading the view, you can make other settings
initView()
initializeAgoraEngine()
setClientRole()
setupLocalVideo()
fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.joinChannel(token: token)
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
override func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
remoteView.frame = self.view.bounds
localView.frame = CGRect(x: self.view.bounds.width - 90, y: 0, width: 90, height: 160)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
leaveChannel()
destroy()
}
func initView(){
remoteView = UIView()
self.view.addSubview(remoteView)
localView = UIView()
self.view.addSubview(localView)
}
func initializeAgoraEngine(){
let config = AgoraRtcEngineConfig()
config.appId = "Your App ID"
config.channelProfile = .liveBroadcasting
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
if agoraKit != nil{
print("Initialization successful")
}
else{
print("Initialization failed")
}
}
func setClientRole(){
agoraKit.setClientRole(.broadcaster)
}
func setupLocalVideo(){
agoraKit.enableVideo()
agoraKit.startPreview()
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = 0
videoCanvas.renderMode = .hidden
videoCanvas.view = localView
agoraKit.setupLocalVideo(videoCanvas)
}
func joinChannel(token:String){
let option = AgoraRtcChannelMediaOptions()
agoraKit.joinChannel(byToken: token, channelId: "test", uid: 123456, mediaOptions: option)
}
func leaveChannel(){
agoraKit.stopPreview()
agoraKit.leaveChannel(nil)
}
func destroy(){
AgoraRtcEngineKit.destroy()
}
func fetchToken(channelName: String, userId: UInt, role: UInt,
callback: @escaping (Result<String, Error>) -> Void
){
let url = URL(string: "http://<Your Host URL and port>/fetch_rtc_token")
let parameters = ["uid":userId,"channelName": channelName, "role": role] as [String : Any]
print(parameters.self)
var request = URLRequest(
url: url!,
timeoutInterval: 10
)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
}
catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: request){data, _, err in
guard let data = data else {
if let err = err {
callback(.failure(err))
}
else {
callback(.failure(TokenError.noData))
}
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseDict = responseJSON as? [String: Any], let token = responseDict["token"] as? String {
callback(.success(token))
} else {
callback(.failure(TokenError.invalidData))
}
}.resume()
}
}
extension ViewController: AgoraRtcEngineDelegate{
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int){
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
videoCanvas.renderMode = .hidden
videoCanvas.view = remoteView
agoraKit.setupRemoteVideo(videoCanvas)
}
func rtcEngine(_ engine: AgoraRtcEngineKit, tokenPrivilegeWillExpire token: String) {
self.fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.agoraKit.renewToken(token)
print("Renewed the token")
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
func rtcEngine(_ engine: AgoraRtcEngineKit, connectionStateChanged state: AgoraConnectionState, reason: AgoraConnectionChangedReason) {
print("Connection state changed to")
print(state.rawValue)
}
func rtcEngineRequestToken(_ engine: AgoraRtcEngineKit) {
fetchToken(channelName: "test", userId: 1234, role: 1){ result in
switch result {
case .success(let token):
print("token is: \(token)")
self.joinChannel(token: token)
case .failure(let err):
print("Could not fetch token: \(err)")
}
}
}
}The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your app.
-
Open the Quickstart project you created earlier.
-
In
index.html, include an HTTP client library such asaxiosfor sending token requests to the authentication server.<html> <head> <title>Token demo</title> </head> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <body> <h1>Token demo</h1> <script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.24.5.js"></script> <script src="../agoraLogic.js"></script> </body> </html> -
Add a
fetchTokenmethod to retrieve a token from your token server to join a channel.// Retrieve a token from your token server function fetchToken(uid, channelName, tokenRole) { return new Promise(function (resolve) { axios.post('http://<Your host URL and port>/fetch_rtc_token', { uid: uid, channelName: channelName, role: tokenRole }, { headers: { 'Content-Type': 'application/json; charset=UTF-8' } }) .then(function (response) { const token = response.data.token; resolve(token); }) .catch(function (error) { console.log(error); }); }) } -
Use the token to join a channel
// Assign the obtained token to the token parameter in the join method let token = await fetchToken(uid, options.channel, 1); await client.join(options.appId, options.channel, token, uid);
Token expiration
After you join a channel using a token, the SDK triggers the token-privilege-will-expire callback, 30 seconds before the token is set to expire. Upon receiving this callback, retrieve a fresh token from the server and call the renewToken method to pass the newly generated token to the SDK.
client.on("token-privilege-will-expire", async function () {
// When you receive the token-privilege-will-expire callback, request a fresh token from the server
let token = await fetchToken(uid, options.channel, 1);
// Call renewToken to pass the new token to the SDK
await client.renewToken(token);
});When the token expires, the SDK triggers the token-privilege-did-expire callback. In this case, retrieve a fresh token from the server and call the join method to rejoin the channel with the new token:
// The token expired.
client.on("token-privilege-did-expire", async function () {
console.log("Fetching a new token")
// Request a new token from the server
let token = await fetchToken(uid, options.channel, 1);
console.log("Rejoining the channel with new token")
// Call join to rejoin the channel
await client.join(options.appId, options.channel, token, uid);
});Complete sample code
For a complete implementation of token authentication, refer to the following code:
Sample code for basic authentication
var rtc = {
// Set local audio track and video track
localAudioTrack: null,
localVideoTrack: null,
};
var options = {
// Fill in app ID
appId: "<Your app ID>",
// Fill in a channel name
channel: "test",
// Set the user as host or audience
role: "host"
};
// Get a token from your token server
function fetchToken(uid, channelName, tokenRole) {
return new Promise(function (resolve) {
axios.post('http://<Your host URL and port>/fetch_rtc_token', {
uid: uid,
channelName: channelName,
role: tokenRole
}, {
headers: {
'Content-Type': 'application/json; charset=UTF-8'
}
})
.then(function (response) {
const token = response.data.token;
resolve(token);
})
.catch(function (error) {
console.log(error);
});
})
}
async function startBasicCall() {
const client = AgoraRTC.createClient({ mode: "live", codec: "vp8" });
client.setClientRole(options.role);
const uid = 123456;
// Assign the obtained token to the token parameter of the join method, and join the channel
let token = await fetchToken(uid, options.channel, 1);
await client.join(options.appId, options.channel, token, uid);
rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
await client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
const localPlayerContainer = document.createElement("div");
localPlayerContainer.id = uid;
localPlayerContainer.style.width = "640px";
localPlayerContainer.style.height = "480px";
document.body.append(localPlayerContainer);
rtc.localVideoTrack.play(localPlayerContainer);
console.log("publish success!");
client.on("user-published", async (user, mediaType) => {
await client.subscribe(user, mediaType);
console.log("subscribe success");
if (mediaType === "video") {
const remoteVideoTrack = user.videoTrack;
const remotePlayerContainer = document.createElement("div");
remotePlayerContainer.textContent = "Remote user " + user.uid.toString();
remotePlayerContainer.style.width = "640px";
remotePlayerContainer.style.height = "480px";
document.body.append(remotePlayerContainer);
remoteVideoTrack.play(remotePlayerContainer);
}
if (mediaType === "audio") {
const remoteAudioTrack = user.audioTrack;
remoteAudioTrack.play();
}
client.on("user-unpublished", user => {
const remotePlayerContainer = document.getElementById(user.uid);
remotePlayerContainer.remove();
});
});
// When you receive the token-privilege-will-expire callback, request a new token from the server and call renewToken to pass the new token to the SDK
client.on("token-privilege-will-expire", async function () {
let token = await fetchToken(uid, options.channel, 1);
await client.renewToken(token);
});
// When you receive the token-privilege-did-expire callback, request a new token from the server and call join to rejoin the channel.
client.on("token-privilege-did-expire", async function () {
console.log("Fetching a new token")
let token = await fetchToken(uid, options.channel, 1);
console.log("Rejoining the channel with a new token")
await client.join(options.appId, options.channel, token, uid);
});
}
startBasicCall()Replace <Your app ID> with your app ID, which must be consistent with the app ID you specified in the server configuration. Update <Your Host URL and port> with the host URL and port of the local Golang server you have deployed. For example 99.9.9.99:8082.
Build and run the project on the local device, the app performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
const char* channelId;
// User ID
int uid = 0
// Request the server to generate a token corresponding to channelId and uid
const char* token = getToken();
// Set channel media options
ChannelMediaOptions options;
// Set the user role as host
options.clientRoleType = CLIENT_ROLE_TYPE::CLIENT_ROLE_BROADCASTER;
// Use the token to join a channel
m_rtcEngine->joinChannel(token, channelId, uid, options);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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelMediaOptionsto 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 use-case
If you call joinChannelEx to join multiple channels, call the updateChannelMediaOptionsEx method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an onTokenPrivilegeWillExpire callback notification.
class CJoinChannelVideoByTokenRtcEngineEventHandler
: public IRtcEngineEventHandler
{
public:
// Callback is triggered when the token is about to expire
virtual void onTokenPrivilegeWillExpire(const char* token) {
// Request to generate a fresh token
const char* newToken = getToken();
// Update token
m_rtcEngine->renewToken(newToken);
}
};The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API Reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your app.
-
Open the SDK quickstart project you created earlier.
-
In
renderer.jsreplace the contents with the following:Sample code for basic authentication
const { createAgoraRtcEngine, VideoMirrorModeType, VideoSourceType, RenderModeType, ChannelProfileType, ClientRoleType, } = require("agora-electron-sdk"); let rtcEngine; let localVideoContainer; let remoteVideoContainer; let isJoined = false; const EventHandles = { // Listen to local user joining channel events onJoinChannelSuccess: ({ channelId, localUid }, elapsed) => { console.log('Successfully joined the channel:' + channelId); isJoined = true; // After local users join the channel, set the local video window rtcEngine.setupLocalVideo({ sourceType: VideoSourceType.VideoSourceCameraPrimary, view: localVideoContainer, mirrorMode: VideoMirrorModeType.VideoMirrorModeDisabled, renderMode: RenderModeType.RenderModeFit, }); }, onLeaveChannel: ({ channelId, localUid }, stats) => { console.log('Successfully left the channel:' + channelId); isJoined = false; }, // Listen to remote user join event onUserJoined: ({ channelId, localUid }, remoteUid, elapsed) => { console.log('Remote user ' + remoteUid + ' 已加入'); // Listen to remote user join event rtcEngine.setupRemoteVideoEx( { sourceType: VideoSourceType.VideoSourceRemote, uid: remoteUid, view: remoteVideoContainer, mirrorMode: VideoMirrorModeType.VideoMirrorModeDisabled, renderMode: RenderModeType.RenderModeFit, }, { channelId }, ); }, // Listen for the token expiration event onTokenPrivilegeWillExpire: ({ channelId, localUid }, token) => { console.log('token ' + token + '即将过期'); // Obtain a new token from the server fetchToken(channelId, localUid, ClientRoleType.ClientRoleBroadcaster) }, // Listen for the token expired event onRequestToken: ({ channelId, localUid }) => { console.log('token expired'); token = '' } }; // Send a token request to the token server function fetchToken(channelName, uid, role) { //Token server URL example: http://12.123.1.123:8082/fetch_rtc_token const url = '<Your host URL and port>/fetch_rtc_token'; const body = JSON.stringify({ uid, ChannelName: channelName, role }); fetch(url, { method: 'POST', body, }) .then((res) => res.json()) .then((res) => { console.log('token ' + res.token); if (+res.code === 200) { if (isJoined) { rtcEngine.renewToken(res.token) } else { rtcEngine.joinChannel(res.token, channelName, uid, { clientRoleType: role }) } } return res; }); } window.onload = () => { const os = require("os"); const path = require("path"); // Fill in your app ID const APPID = "<Your app ID>"; // Fill in the channel name const channel = "Test"; localVideoContainer = document.getElementById("join-channel-local-video"); remoteVideoContainer = document.getElementById("join-channel-remote-video"); const sdkLogPath = path.resolve(os.homedir(), "./test.log"); // Create an RtcEngine instance rtcEngine = createAgoraRtcEngine(); // Initialize the RtcEngine instance rtcEngine.initialize({ appId: APPID, logConfig: { filePath: sdkLogPath } }); // Register event handler rtcEngine.registerEventHandler(EventHandles); //Set the channel profile to live broadcast rtcEngine.setChannelProfile(ChannelProfileType.ChannelProfileLiveBroadcasting); // Set the user role, for host use ClientRoleBroadcaster, for audience use ClientRoleAudience rtcEngine.setClientRole(ClientRoleType.ClientRoleBroadcaster); // Enable the video module rtcEngine.enableVideo(); // Enable camera preview rtcEngine.startPreview(); // Use the token to join a channel // You need to specify the user ID yourself and ensure its uniqueness within the channel fetchToken(channel, 123456, ClientRoleType.ClientRoleBroadcaster); };Replace
<Your app ID>with your app ID, which must be consistent with the app ID you specified in the server configuration. Update<Your host URL and port>with the host URL and port of the local Golang server you have deployed. For example99.9.9.99:8082.
The sample code implements the following logic:
-
Calls
joinChannelto join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token. -
The SDK triggers an
onTokenPrivilegeWillExpirecallback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and callrenewTokento pass the newly generated token to the SDK. -
If the token expires, the SDK triggers an
onRequestTokencallback. After receiving the callback, obtain a new token from the server and calljoinChannelwith the new token to rejoin the channel.
Build and run the project on the local device, the app performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your app.
-
Add the following to the
pubspec.yamlfile, underdependencies:dependencies: # Agora Flutter SDK, use the latest version of agora_rtc_engine agora_rtc_engine: ^6.3.0 # For making http requests http: ^0.13.5 -
Replace the contents in
/lib/main.dartwith the following code.Sample code for basic authentication
import 'dart:convert'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() => runApp(const MyApp()); /// This widget is the root of your application. class MyApp extends StatefulWidget { /// Construct the [MyApp] const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: const Text('APIExample'), ), body: const JoinChannelVideoToken()), ); } } class JoinChannelVideoToken extends StatefulWidget { const JoinChannelVideoToken({Key? key}) : super(key: key); @override State<StatefulWidget> createState() => _State(); } class _State extends State<JoinChannelVideoToken> { late final RtcEngine _engine; bool _isReadyPreview = false; bool isJoined = false, switchCamera = true, switchRender = true; Set<int> remoteUid = {}; static const String appId = '<Your app ID>'; // Fill in the information from Agora console static const String channelId = '<Your channel name>'; // Fill in the channel name static const String hostUrl = '<Your host URL and port>'; // Fill in the server URL and port @override void initState() { super.initState(); _initEngine(); } @override void dispose() { super.dispose(); _dispose(); } Future<void> _dispose() async { await _engine.leaveChannel(); await _engine.release(); } Future<void> _initEngine() async { _engine = createAgoraRtcEngine(); await _engine.initialize(const RtcEngineContext( appId: appId, )); _engine.registerEventHandler(RtcEngineEventHandler( onJoinChannelSuccess: (RtcConnection connection, int elapsed) { setState(() { isJoined = true; }); }, onUserJoined: (RtcConnection connection, int rUid, int elapsed) { setState(() { remoteUid.add(rUid); }); }, onUserOffline: (RtcConnection connection, int rUid, UserOfflineReasonType reason) { setState(() { remoteUid.removeWhere((element) => element == rUid); }); }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { setState(() { isJoined = false; remoteUid.clear(); }); }, onTokenPrivilegeWillExpire: (RtcConnection connection, String token) { _fetchToken(1234, channelId, 1, false); }, onRequestToken: (RtcConnection connection) { _fetchToken(1234, channelId, 1, true); }, )); await _engine.enableVideo(); await _engine.startPreview(); await _fetchToken(1234, channelId, 1, true); setState(() { _isReadyPreview = true; }); } Future<void> _fetchToken( int uid, String channelName, int toeknRole, bool needJoinChannel, ) async { var client = http.Client(); try { Map<String, String> headers = { 'Content-type': 'application/json', 'Accept': 'application/json', }; var response = await client.post(Uri.parse(hostUrl), headers: headers, body: jsonEncode( {'uid': uid, 'ChannelName': channelName, 'role': toeknRole})); var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map; final token = decodedResponse['token']; if (needJoinChannel) { await _engine.joinChannel( token: token, channelId: channelName, uid: uid, options: const ChannelMediaOptions( channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, clientRoleType: ClientRoleType.clientRoleBroadcaster, ), ); } else { await _engine.renewToken(token); } } finally { client.close(); } } @override Widget build(BuildContext context) { if (!_isReadyPreview) return Container(); return Stack( children: [ AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas(uid: 0), ), ), Align( alignment: Alignment.topLeft, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: List.of(remoteUid.map( (e) => SizedBox( width: 120, height: 120, child: AgoraVideoView( controller: VideoViewController.remote( rtcEngine: _engine, canvas: VideoCanvas(uid: e), connection: const RtcConnection(channelId: channelId), ), ), ), )), ), ), ) ], ); } }Replace
<Your app ID>with your app ID, which must be consistent with the app ID you specified in the server configuration. Update<Your host URL and port>with the host URL and port of the local Golang server you have deployed. For example99.9.9.99:8082.
The sample code implements the following logic:
-
Calls
joinChannelto join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token. -
The SDK triggers an
onTokenPrivilegeWillExpirecallback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and callrenewTokento pass the newly generated token to the SDK. -
If the token expires, the SDK triggers an
onRequestTokencallback. After receiving the callback, obtain a new token from the server and calljoinChannelwith the new token to rejoin the channel.
Build and run the project on the local device, the app performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your app.
-
Open the SDK quickstart project you created earlier.
-
In
ProjectName.tsxreplace the contents with the following:Sample code for basic authentication
import React, { useCallback, useEffect, useRef, useState } from 'react'; import { PermissionsAndroid, Platform, SafeAreaView, ScrollView, StyleSheet, Switch, Text, View, } from 'react-native'; import { ChannelProfileType, ClientRoleType, createAgoraRtcEngine, IRtcEngine, IRtcEngineEventHandler, RtcConnection, RtcSurfaceView, } from 'react-native-agora'; // Define basic information const appId = '<Your app ID>'; const channelName = 'test'; const uid = 123; // Local user Uid const App = () => { const agoraEngineRef = useRef<IRtcEngine>(); // IRtcEngine instance const eventHandlerRef = useRef<IRtcEngineEventHandler>(); // IRtcEngine event handler const [token, setToken] = useState<string>(''); // RTC Token const [isJoined, setIsJoined] = useState(false); // Whether the local user has joined the channel const [isHost, setIsHost] = useState(true); // User role const [remoteUid, setRemoteUid] = useState(0); // Uid of remote user const [message, setMessage] = useState(''); // Message to the user // Send a request to obtain a token to the token server const fetchToken = useCallback(() => { // Token server URL example: http://12.123.1.123:8082/fetch_rtc_token const url = '<Your host URL and port>/fetch_rtc_token'; const body = JSON.stringify({ uid, ChannelName: channelName, role: isHost ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience, }); console.log('fetchToken', url, body); return fetch(url, { method: 'POST', body, }) .then((res) => res.json()) .then((res) => { showMessage('token ' + res.token + 'Already refreshed'); if (+res.code === 200) { setToken(res.token); } return res; }); }, [isHost]); // Use the obtained token to join the channel const join = useCallback(async () => { if (!token) { await fetchToken(); } else { try { // After joining the channel, set the channel profile to live broadcast agoraEngineRef.current?.setChannelProfile( ChannelProfileType.ChannelProfileLiveBroadcasting ); if (isHost) { agoraEngineRef.current?.startPreview(); // Join the channel as a host agoraEngineRef.current?.joinChannel(token, channelName, uid, { publishCameraTrack: true, clientRoleType: ClientRoleType.ClientRoleBroadcaster, }); } else { // Join the channel as audience agoraEngineRef.current?.joinChannel(token, channelName, uid, { clientRoleType: ClientRoleType.ClientRoleAudience, }); } } catch (e) { console.log(e); } } }, [fetchToken, isHost, token]); const leave = () => { try { agoraEngineRef.current?.leaveChannel(); setRemoteUid(0); setIsJoined(false); showMessage('Left the channel'); } catch (e) { console.error(e); } }; useEffect(() => { const setupVideoSDKEngine = async () => { try { if (Platform.OS === 'android') { await getPermission(); } agoraEngineRef.current = createAgoraRtcEngine(); eventHandlerRef.current = { onJoinChannelSuccess: () => { showMessage('Successfully joined the channel: ' + channelName); setIsJoined(true); }, onUserJoined: (_connection, Uid) => { showMessage('Remote user ' + Uid + ' joined'); setRemoteUid(Uid); }, onUserOffline: (_connection, Uid) => { showMessage('Remote user ' + Uid + ' left the channel'); setRemoteUid(0); }, onTokenPrivilegeWillExpire(connection: RtcConnection, token: string) { showMessage('token ' + token + 'Expires soon'); fetchToken(); }, onRequestToken(connection: RtcConnection) { showMessage('token expired'); setToken(''); }, }; const agoraEngine = agoraEngineRef.current; //Initialize engine agoraEngine.initialize({ appId: appId, channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting, }); // Register event handler agoraEngine.registerEventHandler(eventHandlerRef.current); // Start local video agoraEngine.enableVideo(); } catch (e) { console.error(e); } }; setupVideoSDKEngine(); return () => { agoraEngineRef.current?.release(); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { const checkJoined = () => { if (isJoined) { console.log('renewToken'); // agoraEngineRef.current?.renewToken(token); } else { console.log('joinChannel'); join(); } }; checkJoined(); }, [token]); // eslint-disable-line react-hooks/exhaustive-deps // Render the user interface return ( <SafeAreaView style={styles.main}> <Text style={styles.head}>Agora SDK Quickstart</Text> <View style={styles.btnContainer}> <Text onPress={join} style={styles.button}> Join channel </Text> <Text onPress={leave} style={styles.button}> Leave Channel </Text> </View> <View style={styles.btnContainer}> <Text>Audience</Text> <Switch onValueChange={(switchValue) => { setIsHost(switchValue); if (isJoined) { leave(); } }} value={isHost} /> <Text>Host</Text> </View> <ScrollView style={styles.scroll} contentContainerStyle={styles.scrollContainer} > {isJoined ? ( <React.Fragment key={0}> <RtcSurfaceView canvas={{ uid: 0 }} style={styles.videoView} /> <Text>Local user uid: {uid}</Text> </React.Fragment> ) : ( <Text>Join a channel</Text> )} {isJoined && !isHost && remoteUid !== 0 ? ( <React.Fragment key={remoteUid}> <RtcSurfaceView canvas={{ uid: remoteUid }} style={styles.videoView} /> <Text>Remote user uid: {remoteUid}</Text> </React.Fragment> ) : ( <Text>{isJoined && !isHost ? 'Wait for a remote user to join' : ''}</Text> )} <Text style={styles.info}>{message}</Text> </ScrollView> </SafeAreaView> ); // Show message function showMessage(msg: string) { console.info(msg); setMessage(msg); } }; // Define user interface style const styles = StyleSheet.create({ button: { paddingHorizontal: 25, paddingVertical: 4, fontWeight: 'bold', color: '#ffffff', backgroundColor: '#0055cc', margin: 5, }, main: { flex: 1, alignItems: 'center' }, scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' }, scrollContainer: { alignItems: 'center' }, videoView: { width: '90%', height: 200 }, btnContainer: { flexDirection: 'row', justifyContent: 'center' }, head: { fontSize: 20 }, info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' }, }); const getPermission = async () => { if (Platform.OS === 'android') { await PermissionsAndroid.requestMultiple([ 'android.permission.RECORD_AUDIO', 'android.permission.CAMERA', ]); } }; export default App;Replace
<Your app ID>with your app ID, which must be consistent with the app ID you specified in the server configuration. Update<Your host URL and port>with the host URL and port of the local Golang server you have deployed. For example99.9.9.99:8082.
The sample code implements the following logic:
-
Calls
joinChannelto join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token. -
The SDK triggers an
onTokenPrivilegeWillExpirecallback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and callrenewTokento pass the newly generated token to the SDK. -
If the token expires, the SDK triggers an
onRequestTokencallback. After receiving the callback, obtain a new token from the server and calljoinChannelwith the new token to rejoin the channel.
Build and run the project on the local device, the app performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token
-
Import the components and hooks you need to handle the authentication workflow:
import { useClientEvent, useRTCClient } from "agora-rtc-react"; -
Retrieve a token from the authentication server:
async function fetchRTCToken(channelName: string) { if (config.serverUrl !== "") { try { const response = await fetch( `${config.proxyUrl}${config.serverUrl}/rtc/${channelName}/publisher/uid/${config.uid}/?expiry=${config.tokenExpiryTime}` ); const data = await response.json(); console.log("RTC token fetched from server: ", data.rtcToken); return data.rtcToken; } catch (error) { console.error(error); throw error; } } else { return config.rtcToken; } } -
Handle the event triggered by Agora SDRTN® when the token is about to expire:
A token expires after the
tokenExpiryTimespecified in the call to the token server or after 24 hours, if the time is not specified. TheuseTokenWillExpiremethod receives a callback when the current token is about to expire so that a fresh token may be retrieved and used.const useTokenWillExpire = () => { const agoraEngine = useRTCClient(); useClientEvent(agoraEngine, "token-privilege-will-expire", () => { if (config.serverUrl !== "") { fetchRTCToken(config.channelName) .then((token: string) => { console.log("RTC token fetched from server: ", token); return agoraEngine.renewToken(token); }) .catch((error) => { console.error(error); }); } else { console.log("Please make sure you specified the token server URL in the configuration file"); } }); };
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts to connect to an Agora channel, your game retrieves a token from the token server in your security infrastructure. Your game then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your game.
-
Open the SDK quickstart project you created earlier.
-
In
joinChannelVideo.csreplace the contents with the following:Sample code for basic authentication
using UnityEngine; using UnityEngine.UI; using UnityEngine.Serialization; using Agora.Rtc; using Agora.Util; using Logger = Agora.Util.Logger; namespace Agora_RTC_Plugin.API_Example.Examples.Advanced.JoinChannelVideoToken { public class JoinChannelVideoToken : MonoBehaviour { // Fill in your App ID and channel name public string appID = "<Your App ID>"; public string channelName = "test"; public uint localUid = 0; internal static string channelToken = ""; internal IRtcEngine RtcEngine = null; internal CONNECTION_STATE_TYPE _state = CONNECTION_STATE_TYPE.CONNECTION_STATE_DISCONNECTED; private void Start() { if (CheckAppId()) { InitEngine(); JoinChannel(); } } internal void RenewOrJoinToken(TokenObject tokenObject) { if (tokenObject == null || tokenObject.code != 200) { Debug.Log("get token failed"); return; } JoinChannelVideoToken.channelToken = tokenObject.token; if (_state == CONNECTION_STATE_TYPE.CONNECTION_STATE_DISCONNECTED || _state == CONNECTION_STATE_TYPE.CONNECTION_STATE_DISCONNECTED || _state == CONNECTION_STATE_TYPE.CONNECTION_STATE_FAILED ) { // Use the token to join the channel JoinChannel(); } else { // Update token in the channel UpdateToken(); } } private void Update() { PermissionHelper.RequestMicrophonePermission(); PermissionHelper.RequestCameraPermission(); } private void UpdateToken() { RtcEngine.RenewToken(JoinChannelVideoToken.channelToken); Debug.Log("renewToken: " + JoinChannelVideoToken.channelToken); } private bool CheckAppId() { if (appID.Length < 10) { Debug.Log("Please fill in your appId in Inspector"); return false; } return true; } // Initialization private void InitEngine() { RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine(); UserEventHandler handler = new UserEventHandler(this); RtcEngineContext context = new RtcEngineContext(appID, 0, CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING, null, AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT); RtcEngine.Initialize(context); RtcEngine.InitEventHandler(handler); } // Join channel private void JoinChannel() { RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER); RtcEngine.EnableAudio(); RtcEngine.EnableVideo(); if (channelToken.Length == 0) { StartCoroutine(HelperClass.FetchToken(this.localUid, channelName,CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER, this.RenewOrJoinToken)); return; } RtcEngine.JoinChannel(channelToken, channelName, ""); Debug.Log("joinChannel with token:" + channelToken); } internal void StartUpdateTokenEveryTenSecond() { InvokeRepeating("RequestToken", 10, 10); } private void RequestToken() { StartCoroutine(HelperClass.FetchToken(this.localUid, channelName, CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER, this.RenewOrJoinToken)); } private void OnDestroy() { Debug.Log("OnDestroy"); if (RtcEngine == null) return; RtcEngine.InitEventHandler(null); RtcEngine.LeaveChannel(); RtcEngine.Dispose(); } internal string GetChannelName() { return channelName; } #region -- Video Render UI Logic --- internal static void MakeVideoView(uint uid, string channelId = "") { GameObject go = GameObject.Find(uid.ToString()); if (!ReferenceEquals(go, null)) { return; } VideoSurface videoSurface = MakeImageSurface(uid.ToString()); if (!ReferenceEquals(videoSurface, null)) { if (uid == 0) { videoSurface.SetForUser(uid, channelId); } else { videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE); } videoSurface.OnTextureSizeModify += (int width, int height) => { float scale = (float)height / (float)width; videoSurface.transform.localScale = new Vector3(-5, 5 * scale, 1); Debug.Log("OnTextureSizeModify: " + width + " " + height); }; videoSurface.SetEnable(true); } } private static VideoSurface MakePlaneSurface(string goName) { GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane); if (go == null) { return null; } go.name = goName; go.transform.Rotate(-90.0f, 0.0f, 0.0f); go.transform.position = Vector3.zero; go.transform.localScale = new Vector3(0.25f, 0.5f, .5f); var videoSurface = go.AddComponent<VideoSurface>(); return videoSurface; } private static VideoSurface MakeImageSurface(string goName) { GameObject go = new GameObject(); if (go == null) { return null; } go.name = goName; go.AddComponent<RawImage>(); go.AddComponent<UIElementDrag>(); GameObject canvas = GameObject.Find("VideoCanvas"); if (canvas != null) { go.transform.parent = canvas.transform; Debug.Log("add video view"); } else { Debug.Log("Canvas is null video view"); } go.transform.Rotate(0f, 0.0f, 180.0f); go.transform.localPosition = Vector3.zero; go.transform.localScale = new Vector3(3f, 4f, 1f); var videoSurface = go.AddComponent<VideoSurface>(); return videoSurface; } internal static void DestroyVideoView(uint uid) { GameObject go = GameObject.Find(uid.ToString()); if (!ReferenceEquals(go, null)) { Object.Destroy(go); } } #endregion } internal class UserEventHandler : IRtcEngineEventHandler { private readonly JoinChannelVideoToken _helloVideoTokenAgora; internal UserEventHandler(JoinChannelVideoToken helloVideoTokenAgora) { _helloVideoTokenAgora= helloVideoTokenAgora; } public override void OnError(int err, string msg) { Debug.Log(string.Format("OnError err: {0}, msg: {1}", err, msg)); } // Joined the channel successfully public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed) { int build = 0; Debug.Log(string.Format("sdk version: ${0}", _helloVideoTokenAgora.RtcEngine.GetVersion(ref build))); Debug.Log( string.Format("OnJoinChannelSuccess channelName: {0}, uid: {1}, elapsed: {2}", connection.channelId, connection.localUid, elapsed)); Debug.Log(string.Format("New Token: {0}", JoinChannelVideoToken.channelToken)); JoinChannelVideoToken.MakeVideoView(0); _helloVideoTokenAgora.localUid = connection.localUid; _helloVideoTokenAgora.StartUpdateTokenEveryTenSecond(); } public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed) { Debug.Log(string.Format("OnUserJoined uid: ${0} elapsed: ${1}", uid, elapsed)); JoinChannelVideoToken.MakeVideoView(uid, _helloVideoTokenAgora.GetChannelName()); } public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason) { Debug.Log(string.Format("OnUserOffLine uid: ${0}, reason: ${1}", uid, (int)reason)); JoinChannelVideoToken.DestroyVideoView(uid); } //The token is about to expire public override void OnTokenPrivilegeWillExpire(RtcConnection connection, string token) { _helloVideoTokenAgora.StartCoroutine(HelperClass.FetchToken(_helloVideoTokenAgora.localUid, _helloVideoTokenAgora.GetChannelName(), CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER, _helloVideoTokenAgora.RenewOrJoinToken)); } public override void OnConnectionStateChanged(RtcConnection connection, CONNECTION_STATE_TYPE state, CONNECTION_CHANGED_REASON_TYPE reason) { _helloVideoTokenAgora._state = state; } public override void OnConnectionLost(RtcConnection connection) { Debug.Log(string.Format("OnConnectionLost ")); } } #endregion } namespace Agora.Util { public static class HelperClass { public static IEnumerator FetchToken(uint uid, string channelName, CLIENT_ROLE_TYPE role, Action<TokenObject> callback = null) { Dictionary<string, object> postParams = new Dictionary<string, object>(); postParams.Add("uid", uid); postParams.Add("ChannelName", channelName); postParams.Add("role", role); string json = AgoraJson.ToJson<Dictionary<string, object>>(postParams); var url = "http://127.0.0.1/fetch_rtc_token"; byte[] postBytes = System.Text.Encoding.Default.GetBytes(json); UnityWebRequest request = new UnityWebRequest(url, "POST"); request.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes); request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); yield return request.SendWebRequest(); if (request.isNetworkError || request.isHttpError) { Debug.Log(request.error); callback(null); yield break; } Debug.Log("text: " + request.downloadHandler.text); TokenObject tokenInfo = JsonUtility.FromJson<TokenObject>(request.downloadHandler.text); callback(tokenInfo); } } }Replace
<Your app ID>with your app ID, which must be consistent with the app ID you specified in the server configuration. Updateurlwith the host URL and port of the local Golang server you have deployed.
The sample code implements the following logic:
-
Calls
JoinChannelto join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token. -
The SDK triggers an
OnTokenPrivilegeWillExpirecallback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and callRenewTokento pass the newly generated token to the SDK.
Build and run the project on the local device, the game performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts to connect to an Agora channel, your game retrieves a token from the token server in your security infrastructure. Your game then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
const char* channelId;
// User ID
int uid = 0
// Request a token from the server corresponding to channelId and uid
const char* token = getToken();
// Set channel media options
ChannelMediaOptions options;
// Set the user role as host
options.clientRoleType = CLIENT_ROLE_TYPE::CLIENT_ROLE_BROADCASTER;
// Join the channel
m_rtcEngine->joinChannel(token, channelId, uid, options);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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelMediaOptionsto 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 use-case
If you call joinChannelEx to join multiple channels, call the updateChannelMediaOptionsEx method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an onTokenPrivilegeWillExpire callback notification.
class CJoinChannelVideoByTokenRtcEngineEventHandler
: public IRtcEngineEventHandler
{
public:
// Triggered when a token is about to expire
virtual void onTokenPrivilegeWillExpire(const char* token) {
// Request to generate a fresh token
const char* token = getToken();
// Renew token
m_rtcEngine->renewToken(token);
}
}The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
When a user attempts to connect to an Agora channel, your game retrieves a token from the token server in your security infrastructure. Your game then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
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.
// Channel name
const char* channelId;
// User ID
int uid = 0
// Request a token from the server corresponding to channelId and uid
const char* token = getToken();
// Set channel media options
ChannelMediaOptions options;
// Set the user role as host
options.clientRoleType = CLIENT_ROLE_TYPE::CLIENT_ROLE_BROADCASTER;
// Join the channel
m_rtcEngine->joinChannel(token, channelId, uid, options);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 use-case
-
Call
renewTokento pass in the newly generated Token (Recommended). -
Call
updateChannelMediaOptionsto 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 use-case
If you call joinChannelEx to join multiple channels, call the updateChannelMediaOptionsEx method to update the token.
The following sample code demonstrates how to call renewToken to update the token upon receiving an onTokenPrivilegeWillExpire callback notification.
class CJoinChannelVideoByTokenRtcEngineEventHandler
: public IRtcEngineEventHandler
{
public:
// Triggered when a token is about to expire
virtual void onTokenPrivilegeWillExpire(const char* token) {
// Request to generate a fresh token
const char* token = getToken();
// Renew token
m_rtcEngine->renewToken(token);
}
}The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
When a user attempts 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 SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Voice Calling:
Token authentication flow
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
