Authentication is the act of validating the identity of each user before they access your system. Agora uses digital tokens to authenticate users and their privileges before they access an Agora service, such as joining an Agora call, or logging into the real-time messaging system.
To enhance its authentication and security services, Agora provides a new version of token called AccessToken2 as of August 18, 2022.
This document shows you how to create an RTM token server and an RTM client app. The RTM client app retrieves an RTM token from the RTM token server. This token authenticates the current user when the user accesses the Agora RTM service.
The following figure shows the steps in the authentication flow:
An RTM token is a dynamic key generated on your app server that is valid for 24 hours. When your users log in to the RTM system from your app client, the RTM system validates the token and reads the user and project information stored in the token. An RTM token contains the following information:
In order to follow this procedure you must have the following:
SDK | First SDK version to support AccessToken2 |
---|---|
RTM Android SDK | v1.5.0 |
RTM iOS SDK | v1.5.0 |
RTM macOS SDK | v1.5.0 |
RTM Web SDK | v1.5.0 |
RTM Windows SDK | v1.5.0 |
RTM Linux SDK | v1.5.0 |
This section shows you how to supply and consume a token that gives rights to specific functionality to authenticated users using the source code provided by Agora.
This section shows you how to get the security information needed to generate a token, including the App ID and App Certificate of your project.
Agora automatically assigns each project an App ID as a unique identifier.
To copy this App ID, find your project on the Project Management page in Agora Console, and click the icon in the App ID column.
To get an App Certificate, do the following:
Token generators create the tokens requested by your client app to enable secure access to Agora Platform. To serve these tokens you deploy a generator in your security infrastructure.
In order to show the authentication workflow, this section shows how to build and run a token server written in Golang on your local machine.
server.go
, with the following content. Then replace <Your App ID>
and <Your App Certificate>
with your App ID and App Certificate.package main
import (
rtmtokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtmtokenbuilder2"
"fmt"
"log"
"net/http"
"time"
"encoding/json"
"errors"
"strconv"
)
type rtm_token_struct struct{
Uid_rtm string `json:"uid"`
}
var rtm_token string
var rtm_uid string
func generateRtmToken(rtm_uid string){
appID := "Your_App_ID"
appCertificate := "Your_Certificate"
expireTimeInSeconds := uint32(3600)
currentTimestamp := uint32(time.Now().UTC().Unix())
expireTimestamp := currentTimestamp + expireTimeInSeconds
result, err := rtmtokenbuilder.BuildToken(appID, appCertificate, rtm_uid, expireTimestamp)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Rtm Token: %s\n", result)
rtm_token = result
}
}
func rtmTokenHandler(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_rtm_str rtm_token_struct
var unmarshalErr *json.UnmarshalTypeError
str_decoder := json.NewDecoder(r.Body)
rtm_err := str_decoder.Decode(&t_rtm_str)
if (rtm_err == nil) {
rtm_uid = t_rtm_str.Uid_rtm
}
if (rtm_err != nil) {
if errors.As(rtm_err, &unmarshalErr){
errorResponse(w, "Bad request. Please check your params.", http.StatusBadRequest)
} else {
errorResponse(w, "Bad request.", http.StatusBadRequest)
}
return
}
generateRtmToken(rtm_uid)
errorResponse(w, rtm_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(){
// Handling routes
// RTM token from RTM uid
http.HandleFunc("/fetch_rtm_token", rtmTokenHandler)
fmt.Printf("Starting server at port 8082\n")
if err := http.ListenAndServe(":8082", nil); err != nil {
log.Fatal(err)
}
}
A go.mod
file defines this module’s import path and dependency requirements. To create the go.mod
for your token server, run the following command:
$ go mod init sampleServer
Get dependencies by running the following command:
$ go get
Start the server by running the following command:
$ go run server.go
This section uses the Web client as an example to show how to use a token for client-side user authentication.
In order to show the authentication workflow, this section shows how to build and run a Web client on your local machine.
Create the project structure of the Web client with a folder including the following files.
index.html
: User interface
client.js
: App logic with Agora RTM Web SDK
|
|-- index.html
|-- client.js
Download Agora RTM SDK for Web. Save the JS file in libs
to your project directory.
In index.html
, add the following code to include the app logic in the UI:
<path to the JS file>
with the path of the JS file you saved in step 2.<html>
<head>
<title>RTM Token demo</title>
</head>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<body>
<h1>Token demo</h1>
<script src="<path to the JS file>"></script>
<script src="./client.js"></script>
</body>
</html>
Create the app logic by editing client.js
with the following content. Then replace <Your App ID>
with your App ID. The App ID must match the one in the server. You also need to replace <Your Host URL and port>
with the host URL and port of the local Golang server you have just deployed, such as 10.53.3.234:8082
.
// Parameters for the login method
let options = {
token: "",
uid: ""
}
// Whether to stop the token renew loop
let stopped = false
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function fetchToken(uid) {
return new Promise(function (resolve) {
axios.post('http://<Your Host URL and port>/fetch_rtm_token', {
uid: uid,
}, {
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 loginRTM()
{
// Your app ID
const appID = "<Your App ID>"
// Initialize the client
const client = AgoraRTM.createInstance(appID)
// Display connection state changes
client.on('ConnectionStateChanged', function (state, reason) {
console.log("State changed To: " + state + " Reason: " + reason)
})
// Set RTM user ID
options.uid = "1234"
// Get Token
options.token = await fetchToken(options.uid)
// Log in to RTM
await client.login(options)
while (!stopped)
{
// Renew a token every 30 seconds for demonstration purposes.
// Agora recommends that you renew a token regularly, such as every hour, in production.
await sleep(30000)
options.token = await fetchToken(options.uid)
client.renewToken(options.token)
let currentDate = new Date();
let time = currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
console.log("Renew RTM token at " + time)
}
}
loginRTM()
In the code example, you can see that token is related to the following code logic in the client:
login
to log in to the RTM system with token and user ID. You must use the user ID that is used to generate the token.renewToken
to update the token of the SDK at a fixed interval. Agora recommends that you regularly (such as every hour) generate a token from the app server and call renewToken
to update the token of the SDK to ensure that the token is always valid.Open index.html
with a supported browser to perform the following actions:
This section introduces token generator libraries, version requirements, and related documents about tokens.
Agora provides an open-source AgoraDynamicKey repository on GitHub, which enables you to generate tokens on your server with programming languages such as C++, Java, and Go.
Language | Algorithm | Core method | Sample code |
---|---|---|---|
C++ | HMAC-SHA256 | buildToken | RtmTokenBuilder2Sample.cpp |
Go | HMAC-SHA256 | buildToken | sample.go |
Java | HMAC-SHA256 | buildToken | RtmTokenBuilder2Sample.java |
PHP | HMAC-SHA256 | buildToken | RtmTokenBuilder2Sample.php |
Python 2 | HMAC-SHA256 | buildToken | RtmTokenBuilder2Sample.py |
Python 3 | HMAC-SHA256 | buildToken | RtmTokenBuilder2Sample.py |
This section introduces the method to generate an RTM token. Take Golang as an example:
func BuildToken(appId string, appCertificate string, userId string, expire uint32) (string, error) {
token := accesstoken.NewAccessToken(appId, appCertificate, expire)
serviceRtm := accesstoken.NewServiceRtm(userId)
serviceRtm.AddPrivilege(accesstoken.PrivilegeLogin, expire)
token.AddService(serviceRtm)
return token.Build()
}
Parameter | Description |
---|---|
appId |
The App ID of your Agora project. |
appCertificate |
The App Certificate of your Agora project. |
userId |
The user ID of the RTM system. You need specify the user ID yourself. See the userId parameter of the login method for supported character sets. |
expire |
The duration (in seconds) from the generation of AccessToken2 to the expiration of AccessToken2. For example, if you set it as 600, the AccessToken2 expires 10 minutes after generation. An AccessToken2 is valid for a maximum of 24 hours. If you set it to a duration longer than 24 hours, the AccessToken2 still expires after 24 hours. If you set it to 0, the AccessToken2 expires immediately. |
This section introduces how to upgrade from AccessToken to AccessToken2 by example.
rtmtokenbuilder
import statement:// Replace "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/RtmTokenBuilder"
// with "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtmtokenbuilder2".
import (
rtmtokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtmtokenbuilder2"
"fmt"
"log"
"net/http"
"time"
"encoding/json"
"errors"
"strconv"
)
BuildToken
function:// Previously, it is `result, err := rtmtokenbuilder.BuildToken(appID, appCertificate, rtm_uid, rtmtokenbuilder.RoleRtmUser, expireTimestamp)`.
// Now, remove `rtmtokenbuilder.RoleRtmUser`.
result, err := rtmtokenbuilder.BuildToken(appID, appCertificate, rtm_uid, expireTimestamp)
The client does not require any updates; however, the expiration logic changes accordingly.
To use AccessToken2 in server-side, refer to RESTful API Authentication and authenticate with new request headers.
The user ID that you use to generate the RTM token must be the same as the one you use to log in to the RTM system.
To use the RTM token for authentication, you need to enable the App Certificate for your project on Console. Once a project has enabled the App Certificate, you must use RTM tokens to authenticate its users.
AccessToken2 allows you to specify the validity period of an RTM token in seconds based on your business requirements. The validity period can be a maximum of 24 hours.
When a token is due to expire in 30 seconds, the RTM SDK triggers the onTokenPrivilegeWillExpire
callback. Upon receiving this callback, you can generate a new RTM token on your app server and call renewToken
to pass the new RTM token to the SDK.
When an RTM token expires, the subsequent logic varies depending on the connection state of the SDK:
CONNECTION_STATE_CONNECTED
state, users receive the onTokenExpired
callback and the onConnectionStateChanged
callback caused by CONNECTION_CHANGE_REASON_TOKEN_EXPIRED (9)
, notifying them that the connection state of the SDK switches to CONNECTION_STATE_ABORTED
. In this case, users need to log in again via the login
method.CONNECTION_STATE_RECONNECTING
state, users receive the onTokenExpired
callback when the network reconnects. In this case, users need to renew the token via the renewToken
method.onTokenPrivilegeWillExpire
and onTokenExpired
callbacks to handle token expiration conditions, Agora recommends that you regularly renew the token (such as every hour) to keep the token valid.