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.
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:
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/RtmTokenBuilder"
"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, rtmtokenbuilder.RoleRtmUser, 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 | RtmTokenBuilderSample.cpp |
Go | HMAC-SHA256 | buildToken | sample.go |
Java | HMAC-SHA256 | buildToken | RtmTokenBuilderSample.java |
Node.js | HMAC-SHA256 | buildToken | RtmTokenBuilderSample.js |
PHP | HMAC-SHA256 | buildToken | RtmTokenBuilderSample.php |
Python 2 | HMAC-SHA256 | buildToken | RtmTokenBuilderSample.py |
Python 3 | HMAC-SHA256 | buildToken | RtmTokenBuilderSample.py |
This section introduces the method to generate an RTM token. Take C++ as an example:
static std::string buildToken(const std::string& appId,
const std::string& appCertificate,
const std::string& userAccount,
RtmUserRole userRole,
uint32_t privilegeExpiredTs = 0);
Parameter | Description |
---|---|
appId | The App ID of your Agora project. |
appCertificate | The App Certificate of your Agora project. |
userAccount | 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. |
role | The user role. Agora supports only one user role. Set the value as the default value Rtm_User . |
privilegeExpiredTs | The Unix timestamp (s) when the token expires, represented by the sum of the current timestamp and the valid time of the token. This parameter is currently invalid. You can ignore this parameter. An RTM token is valid for 24 hours. |
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.
An RTM token is valid for 24 hours.
When the RTM SDK is in the CONNECTION_STATE_CONNECTED
state, the user remains online even if the RTM token expires. If a user logs in with an expired RTM token, the RTM SDK returns the LOGIN_ERR_TOKEN_EXPIRED
error.
The RTM SDK triggers the onTokenExpired
callback only when an RTM token expires and the RTM SDK is in the CONNECTION_STATE_RECONNECTING
state. The callback is triggered only once. 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.
onTokenExpired
callback to handle token expiration conditions, Agora recommends that you regularly renew the Token (such as every hour) to keep the token valid.