Skip to main content

Generate a user token

Authentication is the process of validating identities. Agora uses digital tokens to authenticate users and their privileges before they access an Agora service, such as joining an Agora call, or logging in to Chat.

To securely connect to Chat, you use the following token types:

  • User token: User level access to Chat from your app using Chat SDK. User tokens are used to authenticate users when they log in to your Chat application.
  • App token: Admin level access to Chat using the REST API. App tokens grant admin privileges for the app developer to manage the Chat applications, for example, creating and deleting users. For details, see Implement an Agora app token server for Chat.

This page shows you how to create a Chat user token server and a Chat client app. The client app retrieves a user token from the token server. This token enables the current user to access Chat.

Understand the tech

The following figure shows the steps in the authentication flow: Chat user token workflow

A user token is a dynamic key generated on your app server that is valid for a maximum of 24 hours. When your users login from your app, Chat reads the information stored in the token and uses it to authenticate the user. A user token contains the following information:

  • The App ID of your Agora project.
  • The App Certificate of your Agora project.
  • The UUID of the user to be authenticated. The user UUID is a unique internal identification that Chat generates for a user through User Registration REST APIs.
  • The valid duration of the token.

Prerequisites

Ensure that you meet the following requirements before proceeding:

Implement the authentication flow

This section shows you how to supply and consume a token used to authenticate a user with Chat. This token also controls the functionality each user has access to in Chat. The encryption source code used to generate this token is provided by Agora.

Deploy a token server

Token generators create the tokens requested by your client app to enable secure access to Chat. To serve these tokens you deploy a generator in your security infrastructure.

To show the authentication workflow, this section shows how to build and run a token server written in Java on your local machine.

The following figure shows the API call sequence of generating a Chat user token: api sequence of generating user token

This sample server is for demonstration purposes only. Do not use it in a production environment.

  1. Create a Maven project in IntelliJ, set the name of your project, choose the location to save your project, then click Finish.

  2. In <Project name>/pom.xml, add the following dependencies and then reload the Maven project:


    _51
    <properties>
    _51
    <java.version>1.8</java.version>
    _51
    <spring-boot.version>2.4.3</spring-boot.version>
    _51
    </properties>
    _51
    <packaging>jar</packaging>
    _51
    <dependencyManagement>
    _51
    <dependencies>
    _51
    <dependency>
    _51
    <groupId>org.springframework.boot</groupId>
    _51
    <artifactId>spring-boot-dependencies</artifactId>
    _51
    <version>${spring-boot.version}</version>
    _51
    <type>pom</type>
    _51
    <scope>import</scope>
    _51
    </dependency>
    _51
    </dependencies>
    _51
    </dependencyManagement>
    _51
    <dependencies>
    _51
    <dependency>
    _51
    <groupId>org.springframework.boot</groupId>
    _51
    <artifactId>spring-boot-starter-web</artifactId>
    _51
    </dependency>
    _51
    <dependency>
    _51
    <groupId>org.springframework.boot</groupId>
    _51
    <artifactId>spring-boot-starter</artifactId>
    _51
    </dependency>
    _51
    <dependency>
    _51
    <groupId>org.springframework.boot</groupId>
    _51
    <artifactId>spring-boot-configuration-processor</artifactId>
    _51
    </dependency>
    _51
    <dependency>
    _51
    <groupId>commons-codec</groupId>
    _51
    <artifactId>commons-codec</artifactId>
    _51
    <version>1.14</version>
    _51
    </dependency>
    _51
    </dependencies>
    _51
    <build>
    _51
    <plugins>
    _51
    <plugin>
    _51
    <groupId>org.springframework.boot</groupId>
    _51
    <artifactId>spring-boot-maven-plugin</artifactId>
    _51
    <version>2.4.1</version>
    _51
    <executions>
    _51
    <execution>
    _51
    <goals>
    _51
    <goal>repackage</goal>
    _51
    </goals>
    _51
    </execution>
    _51
    </executions>
    _51
    </plugin>
    _51
    </plugins>
    _51
    </build>

  3. Import the token builders provided by Agora to this project.

    1. Download the chat and media packages.

    2. In the token server project, create a com.agora.chat.token.io.agora package under <Project name>/src/main/java.

    3. Copy the chat and media packages and paste under com.agora.chat.token.io.agora. The following figure shows the project structure:

      token server project

    4. Fix the import errors in chat/ChatTokenBuilder2 and media/AccessToken.

      • In ChatTokenBuilder2, the import should be import com.agora.chat.token.io.agora.media.AccessToken2.
      • In AccessToken, the import should be import static com.agora.chat.token.io.agora.media.Utils.crc32.
  4. In <Project name>/src/main/resource, create a application.properties file to hold the information for generating tokens and update it with your project information. Note that the value for appid, appcert, and appkey should be a string without quotation marks.


    _12
    ## Server port
    _12
    server.port=8090
    _12
    ## Fill the App ID of your Agora project
    _12
    appid=
    _12
    ## Fill the app certificate of your Agora project
    _12
    appcert=
    _12
    ## Fill the app key of the Chat service
    _12
    appkey=
    _12
    ## REST API domain for the Chat service
    _12
    domain=
    _12
    ## Set the valid duration (in seconds) for the token
    _12
    expire.second=60

    For details on how to get the app key and REST API domain, see Enable and Configure Chat.

  5. In the com.agora.chat.token package, create a Java class named AgoraChatTokenController, with the following content:


    _120
    package com.agora.chat.token;
    _120
    import com.agora.chat.token.io.agora.chat.ChatTokenBuilder2;
    _120
    import org.springframework.beans.factory.annotation.Value;
    _120
    import org.springframework.util.StringUtils;
    _120
    import org.springframework.web.bind.annotation.CrossOrigin;
    _120
    import org.springframework.web.bind.annotation.GetMapping;
    _120
    import org.springframework.web.bind.annotation.PathVariable;
    _120
    import org.springframework.web.bind.annotation.RestController;
    _120
    import org.springframework.web.client.RestTemplate;
    _120
    import org.springframework.http.*;
    _120
    import org.springframework.web.client.RestClientException;
    _120
    import java.util.*;
    _120
    @RestController
    _120
    @CrossOrigin
    _120
    public class AgoraChatTokenController {
    _120
    @Value("${appid}")
    _120
    private String appid;
    _120
    @Value("${appcert}")
    _120
    private String appcert;
    _120
    @Value("${expire.second}")
    _120
    private int expire;
    _120
    @Value("${appkey}")
    _120
    private String appkey;
    _120
    @Value("${domain}")
    _120
    private String domain;
    _120
    private final RestTemplate restTemplate = new RestTemplate();
    _120
    // Get the Chat user token
    _120
    @GetMapping("/chat/user/{chatUserName}/token")
    _120
    public String getChatToken(@PathVariable String chatUserName) {
    _120
    if (!StringUtils.hasText(appid) || !StringUtils.hasText(appcert)) {
    _120
    return "appid or appcert is not empty";
    _120
    }
    _120
    if (!StringUtils.hasText(appkey) || !StringUtils.hasText(domain)) {
    _120
    return "appkey or domain is not empty";
    _120
    }
    _120
    if (!appkey.contains("#")) {
    _120
    return "appkey is illegal";
    _120
    }
    _120
    if (!StringUtils.hasText(chatUserName)) {
    _120
    return "chatUserName is not empty";
    _120
    }
    _120
    ChatTokenBuilder2 builder = new ChatTokenBuilder2();
    _120
    String chatUserUuid = getChatUserUuid(chatUserName);
    _120
    if (chatUserUuid == null) {
    _120
    chatUserUuid = registerChatUser(chatUserName);
    _120
    }
    _120
    return builder.buildUserToken(appid, appcert, chatUserUuid, expire);
    _120
    }
    _120
    // Get the UUID for a username
    _120
    private String getChatUserUuid(String chatUserName) {
    _120
    String orgName = appkey.split("#")[0];
    _120
    String appName = appkey.split("#")[1];
    _120
    String url = "http://" + domain + "/" + orgName + "/" + appName + "/users/" + chatUserName;
    _120
    HttpHeaders headers = new HttpHeaders();
    _120
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    _120
    headers.setBearerAuth(exchangeToken());
    _120
    HttpEntity<Map<String, String>> entity = new HttpEntity<>(null, headers);
    _120
    ResponseEntity<Map> responseEntity = null;
    _120
    try {
    _120
    responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class);
    _120
    } catch (Exception e) {
    _120
    System.out.println("get chat user error : " + e.getMessage());
    _120
    }
    _120
    if (responseEntity != null) {
    _120
    List<Map<String, Object>> results = (List<Map<String, Object>>) responseEntity.getBody().get("entities");
    _120
    return (String) results.get(0).get("uuid");
    _120
    }
    _120
    return null;
    _120
    }
    _120
    // Create a user with the password "123" and get the UUID
    _120
    private String registerChatUser(String chatUserName) {
    _120
    String orgName = appkey.split("#")[0];
    _120
    String appName = appkey.split("#")[1];
    _120
    String url = "http://" + domain + "/" + orgName + "/" + appName + "/users";
    _120
    HttpHeaders headers = new HttpHeaders();
    _120
    headers.setContentType(MediaType.APPLICATION_JSON);
    _120
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    _120
    headers.setBearerAuth(exchangeToken());
    _120
    Map<String, String> body = new HashMap<>();
    _120
    body.put("username", chatUserName);
    _120
    body.put("password", "123");
    _120
    HttpEntity<Map<String, String>> entity = new HttpEntity<>(body, headers);
    _120
    ResponseEntity<Map> response;
    _120
    try {
    _120
    response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
    _120
    } catch (Exception e) {
    _120
    throw new RestClientException("register chat user error : " + e.getMessage());
    _120
    }
    _120
    List<Map<String, Object>> results = (List<Map<String, Object>>) response.getBody().get("entities");
    _120
    return (String) results.get(0).get("uuid");
    _120
    }
    _120
    // Get the Agora app token
    _120
    private String getAppToken() {
    _120
    if (!StringUtils.hasText(appid) || !StringUtils.hasText(appcert)) {
    _120
    return "appid or appcert is not empty";
    _120
    }
    _120
    ChatTokenBuilder2 builder = new ChatTokenBuilder2();
    _120
    return builder.buildAppToken(appid, appcert, expire);
    _120
    }
    _120
    // Convert the Agora app token to Chat app token
    _120
    private String exchangeToken() {
    _120
    String orgName = appkey.split("#")[0];
    _120
    String appName = appkey.split("#")[1];
    _120
    String url = "http://" + domain + "/" + orgName + "/" + appName + "/token";
    _120
    HttpHeaders headers = new HttpHeaders();
    _120
    headers.setContentType(MediaType.APPLICATION_JSON);
    _120
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    _120
    headers.setBearerAuth(getAppToken());
    _120
    Map<String, String> body = new HashMap<>();
    _120
    body.put("grant_type", "agora");
    _120
    HttpEntity<Map<String, String>> entity = new HttpEntity<>(body, headers);
    _120
    ResponseEntity<Map> response;
    _120
    try {
    _120
    response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
    _120
    } catch (Exception e) {
    _120
    throw new RestClientException("exchange token error : " + e.getMessage());
    _120
    }
    _120
    return (String) Objects.requireNonNull(response.getBody()).get("access_token");
    _120
    }
    _120
    }

  6. In the com.agora.chat.token package, create a Java class named AgoraChatTokenStarter, with the following content:


    _9
    package com.agora.chat.token;
    _9
    import org.springframework.boot.SpringApplication;
    _9
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    _9
    @SpringBootApplication(scanBasePackages = "com.agora")
    _9
    public class AgoraChatTokenStarter {
    _9
    public static void main(String[] args) {
    _9
    SpringApplication.run(AgoraChatTokenStarter.class, args);
    _9
    }
    _9
    }

  7. To start the server, click the green triangle button, and select Debug "AgoraChatTokenStarter...".

    start the server

Use tokens for user authentication

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

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

This sample client is for demonstration purposes only. Do not use it in a production environment.

To implement the Web client, do the following:

  1. Create a project structure for a Chat Web app. In the project root folder, create the following files:

    • index.html: The user interface.
    • index.js: The app logic.
    • webpack.config.js: The webpack configuration.
  2. To configure webpack, copy the following code into webpack.config.js:


    _15
    const path = require('path');
    _15
    _15
    module.exports = {
    _15
    entry: './index.js',
    _15
    mode: 'production',
    _15
    output: {
    _15
    filename: 'bundle.js',
    _15
    path: path.resolve(__dirname, './dist'),
    _15
    },
    _15
    devServer: {
    _15
    compress: true,
    _15
    port: 9000,
    _15
    https: true
    _15
    }
    _15
    };

  3. Set up the npm package for your Web app. In terminal, navigate to the project root directory and run npm init. This creates a package.json file.

  4. Configure the dependencies and scripts for your project. In package.json, add the following code:


    _21
    {
    _21
    "name": "web",
    _21
    "version": "1.0.0",
    _21
    "description": "",
    _21
    "main": "index.js",
    _21
    "scripts": {
    _21
    "build": "webpack --config webpack.config.js",
    _21
    "start:dev": "webpack serve --open --config webpack.config.js"
    _21
    },
    _21
    "keywords": [],
    _21
    "author": "",
    _21
    "license": "ISC",
    _21
    "dependencies": {
    _21
    "agora-chat-sdk": "latest"
    _21
    },
    _21
    "devDependencies": {
    _21
    "webpack": "^5.50.0",
    _21
    "webpack-cli": "^4.8.0",
    _21
    "webpack-dev-server": "^3.11.2"
    _21
    }
    _21
    }

  5. Create the UI for your app. In index.html, add the following code:


    _19
    <!DOCTYPE html>
    _19
    <html lang="en">
    _19
    <head>
    _19
    <title>Chat Token demo</title>
    _19
    </head>
    _19
    _19
    <body>
    _19
    <h1>Token demo</h1>
    _19
    <div class="input-field">
    _19
    <label>Username</label>
    _19
    <input type="text" placeholder="Username" id="username" />
    _19
    </div>
    _19
    <div>
    _19
    <button type="button" id="login">Login</button>
    _19
    </div>
    _19
    <div id="log"></div>
    _19
    <script src="./dist/bundle.js"></script>
    _19
    </body>
    _19
    </html>

  6. Create the app logic. In index.js, add the following code and replace <Your App Key> with your app key.

    In the code example, you can see that token is related to the following code logic in the client:

    • Call open to log in to the Chat system with token and username. You must use the username that is used to register the user and get the UUID.
    • Fetch a new token from the app server and call renewToken to update the token of the SDK when the token is about to expire and when the token expires. 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.

    _65
    import WebIM from "agora-chat-sdk";
    _65
    WebIM.conn = new WebIM.connection({
    _65
    appKey: "<Your App Key>",
    _65
    });
    _65
    // Login to Chat
    _65
    let username;
    _65
    document.getElementById("login").onclick = function () {
    _65
    username = document.getElementById("username").value.toString();
    _65
    // Fetch the Chat user token with username.
    _65
    fetch(`http://localhost:8090/chat/user/${username}/token`)
    _65
    .then((res) => res.text())
    _65
    .then((token) => {
    _65
    // Login to Chat with username and token
    _65
    WebIM.conn.open({
    _65
    user: username,
    _65
    agoraToken: token,
    _65
    });
    _65
    });
    _65
    };
    _65
    // Add an event handler
    _65
    WebIM.conn.addEventHandler("AUTHHANDLER", {
    _65
    // Connected to the server
    _65
    onConnected: () => {
    _65
    document
    _65
    .getElementById("log")
    _65
    .appendChild(document.createElement("div"))
    _65
    .append("Connect success !");
    _65
    },
    _65
    // Receive a text message
    _65
    onTextMessage: (message) => {
    _65
    console.log(message);
    _65
    document
    _65
    .getElementById("log")
    _65
    .appendChild(document.createElement("div"))
    _65
    .append("Message from: " + message.from + " Message: " + message.data);
    _65
    },
    _65
    // Renew the token when the token is about to expire
    _65
    onTokenWillExpire: (params) => {
    _65
    document
    _65
    .getElementById("log")
    _65
    .appendChild(document.createElement("div"))
    _65
    .append("Token is about to expire");
    _65
    refreshToken(username);
    _65
    },
    _65
    // Renew the token when the token has expired
    _65
    onTokenExpired: (params) => {
    _65
    document
    _65
    .getElementById("log")
    _65
    .appendChild(document.createElement("div"))
    _65
    .append("The token has expired");
    _65
    refreshToken(username);
    _65
    },
    _65
    onError: (error) => {
    _65
    console.log("on error", error);
    _65
    },
    _65
    });
    _65
    // Renew token
    _65
    function refreshToken(username) {
    _65
    fetch(`http://localhost:8090/chat/user/${username}/token`)
    _65
    .then((res) => res.text())
    _65
    .then((token) => {
    _65
    WebIM.conn.renewToken(token);
    _65
    }
    _65
    );
    _65
    }

  7. To build and run your project, do the following:

    1. To install the dependencies, run npm install.

    2. To build and run the project using webpack, run the following commands:


      _5
      # Use webpack to package the project
      _5
      npm run build
      _5
      _5
      # Use webpack-dev-server to run the project
      _5
      npm run start:dev

    The index.html page opens in your browser.

    1. Input a username and click the login button. Open the browser console, and you can see the web client performs the following actions:

      • Generates a user token.
      • Connects to the Chat system.
      • Renews a token when it is about to expire.

Reference

This section introduces token generator libraries, version requirements, and related documents about tokens.

Token generator libraries

Agora provides an open-source AgoraDynamicKey repository on GitHub, which enables you to generate tokens on your server with programming languages such as C++, Java, and Go.

LanguageAlgorithmCore methodSample code
C++HMAC-SHA256BuildUserTokenChatTokenBuilder2Sample.cpp
JavaHMAC-SHA256buildUserTokenChatTokenBuilder2Sample.java
PHPHMAC-SHA256buildUserTokenChatTokenBuilder2.php
Python 2HMAC-SHA256build_user_tokenChatTokenBuilder2Sample.py
Python 3HMAC-SHA256build_user_tokenChatTokenBuilder2Sample.py

API reference

This section introduces the method to generate a user token. Take Java as an example:


_12
public String buildUserToken(String appId, String appCertificate, String uuid, int expire) {
_12
AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
_12
AccessToken2.Service serviceChat = new AccessToken2.ServiceChat(uuid);
_12
serviceChat.addPrivilegeChat(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER, expire);
_12
accessToken.addService(serviceChat);
_12
try {
_12
return accessToken.build();
_12
} catch (Exception e) {
_12
e.printStackTrace();
_12
return "";
_12
}
_12
}

ParameterDescription
appIdThe App ID of your Agora project.
appCertificateThe App Certificate of your Agora project.
uuidThe unique identifier (UUID) of a user in the Chat system.
expireThe valid duration (in seconds) of the token. The maximum is 86,400, that is, 24 hours.

Token expiration

A user token is valid for a maximum of 24 hours.

When the Chat SDK is in the isConnected(true) state, the user remains online even if the user token expires. If a user logs in with an expired token, Chat returns the TOKEN_EXPIRED error.

The Chat SDK triggers the onTokenExpired callback only when a token expires and the SDK is in the isConnected(true) state. The callback is triggered only once. When your listener receives this event, retrieve a new token from your token server, and pass this token to Chat with a call to renewToken.

Although you can use the onTokenExpired callback to handle token expiration conditions, Agora recommends that you regularly renew the token (for example every hour) to keep the token valid.

Tokens for Agora RTC products

If you use Chat together with the Agora RTC SDK, Agora recommends you upgrade to AccessToken2.

Page Content