# Deploy a token server (/en/realtime-media/cloud-recording/build/set-up-authentication/authentication-workflow)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

To ensure the security of your business, best practice is to authenticate every client that joins a channel. Agora uses digital tokens to authenticate users and their privileges. When a user attempts to connect to Agora, they provide a token obtained from a token server. The Agora SDRTN® validates the user's identity and permissions based on the information in the token.

To generate authentication tokens, deploy a token server within your security infrastructure. This guide explains how to set up a token server using Agora's open source code repository.

## Understand the tech [#understand-the-tech]

An authentication token is a dynamic key that is valid for a maximum of 24 hours. On request, your token server returns an authentication token that is valid to join a specific channel or a set of wildcard channels.

The following figure shows the call flow you implement to create step-up-authentication with Agora Cloud Recording:

<Accordions>
  <Accordion title="Token authentication flow">
    ![token authentication flow](https://assets-docs.agora.io/images/video-sdk/token-authentication.svg)
  </Accordion>
</Accordions>

Your app retrieves a token from the token server when a user attempts to connect to an Agora channel. The app then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.

Agora enables you to implement permissions with the following strategies. Select the one that best suits your use-case:

* **Uniform permissions**

  Uniformly set permissions for joining channels and concurrent streams. This is applicable to most use-cases. For details, see [Basic authentication](#basic-authentication).

* **Finely control permissions for publishing different streams**

  Manage permissions to publish different types of streams, such as audio, video, and data. If you have higher requirements for business security, use these permissions to finely control the validity period of permissions to publish different streams. For details, see [Co-host token authentication](#co-host-token-authentication).

<CalloutContainer type="info">
  <CalloutTitle>
    Information
  </CalloutTitle>

  <CalloutDescription>
    The information on this page only applies to `AccessToken2`. If you are still using `AccessToken`, refer to the [AccessToken upgrade guide](./integrate-token-generation#upgrade-to-accesstoken2) to upgrade from `AccessToken` to `AccessToken2`.
  </CalloutDescription>
</CalloutContainer>

## Token generation code [#token-generation-code]

Agora provides an open source token generator code repository on [GitHub](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey). The repository provides code in the following languages to generate tokens on your own server using the `HMAC-SHA256` algorithm:

| Language | Core Method                                                                                                                                              | Sample Code                                                                                                                                                           |
| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Golang   | [BuildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2/rtctokenbuilder.go#L24)              | [sample.go](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/go/sample/rtctokenbuilder2/sample.go)                                             |
| Node.js  | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/nodejs/src/RtcTokenBuilder2.js#L43)                          | [RtcTokenBuilder2Sample.js](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/nodejs/sample/RtcTokenBuilder2Sample.js)                          |
| PHP      | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/php/src/RtcTokenBuilder2.php#L40)                            | [RtcTokenBuilder2Sample.php](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/php/sample/RtcTokenBuilder2Sample.php)                           |
| Python   | [build\_token\_with\_uid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/python/src/RtcTokenBuilder2.py#L21)                    | [RtcTokenBuilder2Sample.py](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/python/sample/RtcTokenBuilder2Sample.py)                          |
| Python3  | [build\_token\_with\_uid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/python3/src/RtcTokenBuilder2.py#L22)                   | [RtcTokenBuilder2Sample.py](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/python3/sample/RtcTokenBuilder2Sample.py)                         |
| Java     | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/java/src/main/java/io/agora/media/RtcTokenBuilder2.java#L47) | [RtcTokenBuilder2Sample.java](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/java/src/main/java/io/agora/sample/RtcTokenBuilder2Sample.java) |
| C++      | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/cpp/src/RtcTokenBuilder2.h#L70)                              | [RtcTokenBuilder2Sample.cpp](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/cpp/sample/RtcTokenBuilder2Sample.cpp)                           |

### Basic authentication [#basic-authentication]

Refer to the following code samples to generate a token with basic permissions:

<Tabs defaultValue="golang">
  <TabsList>
    <TabsTrigger value="golang">
      Golang
    </TabsTrigger>

    <TabsTrigger value="node-js">
      Node.js
    </TabsTrigger>

    <TabsTrigger value="php">
      PHP
    </TabsTrigger>

    <TabsTrigger value="python">
      Python/Python3
    </TabsTrigger>

    <TabsTrigger value="java">
      Java
    </TabsTrigger>

    <TabsTrigger value="cpp">
      C++
    </TabsTrigger>
  </TabsList>

  <TabsContent value="golang">
    ```go
    package main

        "fmt"
        "os"

        rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
    )

    func main() {
        // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.
        appId := os.Getenv("AGORA_APP_ID")
        // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        appCertificate := os.Getenv("AGORA_APP_CERTIFICATE")
        // Replace channelName with the name of the channel you want to join
        channelName := "channelName"
        // Fill in your actual user ID
        uid := uint32(2882341273)
        // Token validity time in seconds
        tokenExpirationInSeconds := uint32(3600)
        // The validity time of all permissions in seconds
        privilegeExpirationInSeconds := uint32(3600)

        fmt.Println("App Id:", appId)
        fmt.Println("App Certificate:", appCertificate)
        if appId == "" || appCertificate == "" {
            fmt.Println("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
            return
        }
        // Generate Token
            result, err := rtctokenbuilder.BuildTokenWithUid(appId, appCertificate, channelName, uid, rtctokenbuilder.RoleSubscriber, tokenExpirationInSeconds, privilegeExpirationInSeconds)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Printf("Token with int uid: %s\\n", result)
        }
    }
    ```
  </TabsContent>

  <TabsContent value="node-js">
    ```js
    const RtcTokenBuilder = require("../src/RtcTokenBuilder2").RtcTokenBuilder;
    const RtcRole = require("../src/RtcTokenBuilder2").Role;

    // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.
    const appId = process.env.AGORA_APP_ID;
    // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
    const appCertificate = process.env.AGORA_APP_CERTIFICATE;
    // Replace channelName with the name of the channel you want to join
    const channelName = "channelName";
    // Fill in your actual user ID
    const uid = 2882341273;
    // Set streaming permissions
    const role = RtcRole.PUBLISHER;
    // Token validity time in seconds
    const tokenExpirationInSecond = 3600;
    // The validity time of all permissions in seconds
    const privilegeExpirationInSecond = 3600;

    console.log("App Id:", appId);
    console.log("App Certificate:", appCertificate);
    if (appId == undefined || appId == "" || appCertificate == undefined || appCertificate == "") {
      console.log("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE");
      process.exit(1);
    }

    // Generate Token
    const tokenWithUid = RtcTokenBuilder.buildTokenWithUid(appId, appCertificate, channelName, uid, role, tokenExpirationInSecond, privilegeExpirationInSecond);
    console.log("Token with int uid:", tokenWithUid);
    ```
  </TabsContent>

  <TabsContent value="php">
    ```js
    <?php
    include("../src/RtcTokenBuilder2.php");

    // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.
    $appId = getenv("AGORA_APP_ID");
    // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
    $appCertificate = getenv("AGORA_APP_CERTIFICATE");
    // Replace channelName with the name of the channel you want to join
    $channelName = "channelName";
    // Fill in your actual user ID
    $uid = 2882341273;
    // Token validity time in seconds
    $tokenExpirationInSeconds = 3600;
    // The validity time of all permissions in seconds
    $privilegeExpirationInSeconds = 3600;

    echo "App Id: " . $appId . PHP_EOL;
    echo "App Certificate: " . $appCertificate . PHP_EOL;
    if ($appId == "" || $appCertificate == "") {
        echo "Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE" . PHP_EOL;
        exit;
    }
    // Generate Token
    $token = RtcTokenBuilder2::buildTokenWithUid($appId, $appCertificate, $channelName, $uid, RtcTokenBuilder2::ROLE_PUBLISHER, $tokenExpirationInSeconds, $privilegeExpirationInSeconds);
    echo 'Token with int uid: ' . $token . PHP_EOL;
    ```
  </TabsContent>

  <TabsContent value="python">
    ```python
    import os

    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

    from src.RtcTokenBuilder2 import *

    def main():
        # Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.
        app_id = os.environ.get("AGORA_APP_ID")
        # Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        app_certificate = os.environ.get("AGORA_APP_CERTIFICATE")
        # Replace channelName with the name of the channel you want to join
        channel_name = "channel_name"
        # Fill in your actual user ID
        uid = 2882341273
        # Token validity time in seconds
        token_expiration_in_seconds = 3600
        # The validity time of all permissions in seconds
        privilege_expiration_in_seconds = 3600

        print("App Id: %s" % app_id)
        print("App Certificate: %s" % app_certificate)
        if not app_id or not app_certificate:
            print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
            return

        # Generate Token
        token = RtcTokenBuilder.build_token_with_uid(app_id, app_certificate, channel_name, uid, Role_Subscriber,
                                                     token_expiration_in_seconds, privilege_expiration_in_seconds)
        print("Token with int uid: {}".format(token))

    if __name__ == "__main__":
        main()
    ```
  </TabsContent>

  <TabsContent value="java">
    ```java
    package io.agora.sample;

    public class RtcTokenBuilder2Sample {
        // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the App ID you obtained from Agora console.
        static String appId = System.getenv("AGORA_APP_ID");
        // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        static String appCertificate = System.getenv("AGORA_APP_CERTIFICATE");
        // Replace channelName with the name of the channel you want to join
        static String channelName = "channelName";
        // Fill in your actual user ID
        static int uid = 2082341273;
        // Token validity time in seconds
        static int tokenExpirationInSeconds = 3600;
        // The validity time of all permissions in seconds
        static int privilegeExpirationInSeconds = 3600;

        public static void main(String[] args) {
            System.out.printf("App Id: %s\\n", appId);
            System.out.printf("App Certificate: %s\\n", appCertificate);
            if (appId == null || appId.isEmpty() || appCertificate == null || appCertificate.isEmpty()) {
                System.out.printf("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE\\n");
                return;
            }
            // Generate Token
            RtcTokenBuilder2 token = new RtcTokenBuilder2();
            String result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, Role.ROLE_SUBSCRIBER,
                    tokenExpirationInSeconds, privilegeExpirationInSeconds);
            System.out.printf("Token with uid: %s\\n", result);
        }
    }
    ```
  </TabsContent>

  <TabsContent value="cpp">
    ```cpp
    #include <cstdlib>
    #include <iostream>

    #include "../src/RtcTokenBuilder2.h"

    using namespace agora::tools;

    int main(int argc, char const *argv[]) {
        (void)argc;
        (void)argv;

        // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
        const char *env_app_id = getenv("AGORA_APP_ID");
        std::string app_id = env_app_id ? env_app_id : "";
        // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        const char *env_app_certificate = getenv("AGORA_APP_CERTIFICATE");
        std::string app_certificate = env_app_certificate ? env_app_certificate : "";
        // Replace channelName with the name of the channel you want to join
        std::string channel_name = "channelName";
        // Fill in your actual user ID
        uint32_t uid = 2882341273
        // Token validity time in seconds
        uint32_t token_expiration_in_seconds = 3600;
        // The validity time of all permissions in seconds
        uint32_t privilege_expiration_in_seconds = 3600;
        std::string result;

        std::cout << "App Id:" << app_id << std::endl;
        std::cout << "App Certificate:" << app_certificate << std::endl;
        if (app_id == "" || app_certificate == "") {
            std::cout << "Need to set environment variable AGORA_APP_ID and "
                        "AGORA_APP_CERTIFICATE"
                    << std::endl;
            return -1;
        }
        // Generate Token
        result = RtcTokenBuilder2::BuildTokenWithUid(
            app_id, app_certificate, channel_name, uid, UserRole::kRolePublisher,
            token_expiration_in_seconds, privilege_expiration_in_seconds);
        std::cout << "Token With Int Uid:" << result << std::endl;

        return 0;
    }
    ```
  </TabsContent>
</Tabs>

<CalloutContainer type="info">
  <CalloutTitle>
    Information
  </CalloutTitle>

  <CalloutDescription>
    The code samples presented on this page use an integer user ID to generate a token. To use a string user ID, refer to the [Token Generator code repository](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey) to view the corresponding code sample in the desired language.
  </CalloutDescription>
</CalloutContainer>

### Advanced authentication features [#advanced-authentication-features]

This section presents advanced functions related to token authentication.

* [Wildcard tokens](#generate-wildcard-tokens): Allow users to join multiple channels or switch channels without frequently requesting new tokens.

* [Co-host token authentication](#co-host-token-authentication): Enable fine-grained control to ensure only authorized users can publish streams.

#### Generate wildcard tokens [#generate-wildcard-tokens]

In use-cases where users need to join multiple channels, or frequently switch between channels, they need to request a new token from the token server, each time they join a channel. To solve the problem of frequently requesting tokens when switching channels, Agora enables you to generate wildcard tokens that set the user ID or channel name as a wildcard. Users reuse the same wildcard token to join different channels, which speeds up the process of switching and joining channels. It also helps reduce the pressure on your token server.

<CalloutContainer type="info">
  <CalloutTitle>
    Information
  </CalloutTitle>

  <CalloutDescription>
    To generate wildcard tokens, deploy an `AccessToken2` server. If you are still using `AccessToken`, see the [AccessToken upgrade guide](./integrate-token-generation#upgrade-to-accesstoken2) to upgrade to `AccessToken2`.
  </CalloutDescription>
</CalloutContainer>

The `AccessToken2` code provides two `BuildTokenWithUid` methods. Choose the method according to your needs. When generating a wildcard token, fill in the following parameters according to your requirements:

| Token type                      | `uid`                                                                                                                                                                                                                                                                                                                                             | `channelName`                                                                                                                                                                                                                                                       |
| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Token for wildcard user ID      | Set the user ID to `0` or a wildcard `*` to match any length of characters for user IDs of int and string types. Agora also supports extended matching, that is, matching multiple combinations of `*` and strings. For example, `1*a*2` means match user IDs starting with *1*, containing the letter *a*, and ending with *2*, such as *1abc2*. | A string of up to 64 bytes.                                                                                                                                                                                                                                         |
| Token for wildcard channel name | The user ID of the user to be authenticated. User IDs of type int and string are supported.                                                                                                                                                                                                                                                       | Set the channel name as a wildcard `*` or leave it blank. To employ expanded matching, use multiple combinations of `*` and strings. For example, `1*a*2` matches channel names starting with *1*, containing the letter *a*, and ending with *2*, such as *1abc2*. |

#### Precautions [#precautions]

* If you have generated a token for `uid=0`, consider the following when you use the token:

  * If you only need to join a single channel, set the `uid` to `0` when calling `joinChannel`.

  * If you need to join multiple channels, set `uid` to a specific user ID instead of `0` when calling `joinChannelEx`.

* In order to prevent unauthorized users from disrupting the order of the channel if the token is leaked, best practice is that users who use wildcard tokens set their client role as audience, and their token privilege `role` to `kRoleSubscriber`.

If viewers using wildcard tokens need to publish streams, Agora recommends the following steps to update the token.
The following API uses C++ as an example:

1. Call `setClientRole` to set the user role as **Broadcaster**.

2. When generating a token using `BuildTokenWithUid`, specify the channel name and user ID in the method, and set the token privilege `role` to `kRolePublisher`, thereby generating a token with the permission to send streams.

3. Call `renewToken` to update the token.

A wildcard token is valid for 24 hours. If the token is about to expire, regenerate a new wildcard token on the server and then call `renewToken` to pass in the new wildcard token.

#### Co-host token authentication [#co-host-token-authentication]

In addition to basic authentication, Agora also provides fine-grained control over the permissions of users in a channel to publish different types of streams. Co-host token authentication helps you ensure that only the streaming users in a channel are authorized to publish streams, thus preventing hackers from stealing tokens or exploiting vulnerabilities to blow up a live broadcast channel.

<CalloutContainer type="warning">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    * Before using advanced authentication features, ensure that you have enabled [Co-host token authentication](/help/integration-issues/token_cohost#enable-co-host-authentication) in Agora Console.

    * To send a stream to a channel, ensure that the client role is set to **Broadcaster** either at the time of joining the channel, or after joining a channel by calling `setClientRole`.
  </CalloutDescription>
</CalloutContainer>

Agora provides `BuildTokenWithUidAndPrivilege` methods to set the expiration time of the following privileges:

* Join a channel
* Publish audio stream in a channel
* Publish video stream in a channel
* Publish data stream in a channel

See the [API reference](#api-reference) for detailed parameter descriptions.

#### Generate a token with advanced permissions [#generate-a-token-with-advanced-permissions]

Use the following methods from the token generation [Github repository](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey) to generate tokens with advanced permissions:

| Language | Core Method                                                                                                                                               | Sample Code                                                                                                                                                           |
| :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Golang   | [BuildTokenWithUidAndPrivilege](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2/rtctokenbuilder.go#L122)  | [sample.go](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/go/sample/rtctokenbuilder2/sample.go)                                             |
| Node.js  | [BuildTokenWithUidAndPrivilege](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/nodejs/src/RtcTokenBuilder2.js#L142)              | [RtcTokenBuilder2Sample.js](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/nodejs/sample/RtcTokenBuilder2Sample.js)                          |
| PHP      | [BuildTokenWithUidAndPrivilege](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/php/src/RtcTokenBuilder2.php#L122)                | [RtcTokenBuilder2Sample.php](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/php/sample/RtcTokenBuilder2Sample.php)                           |
| Python   | [build\_token\_with\_uid\_and\_privilege](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/python/src/RtcTokenBuilder2.py#L76)     | [RtcTokenBuilder2Sample.py](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/python/sample/RtcTokenBuilder2Sample.py)                          |
| Python3  | [build\_token\_with\_uid\_and\_privilege](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/python3/src/RtcTokenBuilder2.py#L75)    | [RtcTokenBuilder2Sample.py](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/python3/sample/RtcTokenBuilder2Sample.py)                         |
| Java     | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/java/src/main/java/io/agora/media/RtcTokenBuilder2.java#L135) | [RtcTokenBuilder2Sample.java](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/java/src/main/java/io/agora/sample/RtcTokenBuilder2Sample.java) |
| C++      | [buildTokenWithUid](https://github.com/AgoraIO/Tools/blob/master/DynamicKey/AgoraDynamicKey/cpp/src/RtcTokenBuilder2.h#L172)                              | [RtcTokenBuilder2Sample.cpp](https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/cpp/sample/RtcTokenBuilder2Sample.cpp)                           |

Refer to the following code samples to generate a token with advanced permissions:

<Tabs defaultValue="golang">
  <TabsList>
    <TabsTrigger value="golang">
      Golang
    </TabsTrigger>

    <TabsTrigger value="node-js">
      Node.js
    </TabsTrigger>

    <TabsTrigger value="php">
      PHP
    </TabsTrigger>

    <TabsTrigger value="python">
      Python/Python3
    </TabsTrigger>

    <TabsTrigger value="java">
      Java
    </TabsTrigger>

    <TabsTrigger value="cpp">
      C++
    </TabsTrigger>
  </TabsList>

  <TabsContent value="golang">
    ```go
    package main

        "fmt"
        "os"
        rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
    )

    func main() {
        // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
        appId := os.Getenv("AGORA_APP_ID")
        // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        appCertificate := os.Getenv("AGORA_APP_CERTIFICATE")
        // Replace channelName with the name of the channel you want to join
        channelName := "channelName"
        // Fill in your actual user ID
        uid := uint32(2882341273)
        // Token validity time in seconds
        tokenExpirationInSeconds := uint32(3600)
        // Validity time for permission to join a channel (seconds)
        joinChannelPrivilegeExpireInSeconds := uint32(3600)
        // Validity in seconds for the permission to publish audio streams in the channel
        pubAudioPrivilegeExpireInSeconds := uint32(3600)
        // Validity in seconds for the permission to publish video streams in the channel
        pubVideoPrivilegeExpireInSeconds := uint32(3600)
        // Validity in seconds for the permission to publish data streams in the channel
        pubDataStreamPrivilegeExpireInSeconds := uint32(3600)

        fmt.Println("App Id:", appId)
        fmt.Println("App Certificate:", appCertificate)

        if appId == "" || appCertificate == "" {
            fmt.Println("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
            return
        }

        // Generate Token
        result, err := rtctokenbuilder.BuildTokenWithUidAndPrivilege(
            appId,
            appCertificate,
            channelName,
            uid,
            tokenExpirationInSeconds,
            joinChannelPrivilegeExpireInSeconds,
            pubAudioPrivilegeExpireInSeconds,
            pubVideoPrivilegeExpireInSeconds,
            pubDataStreamPrivilegeExpireInSeconds,
        )

        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Printf("Token with int uid and privilege: %s\\n", result)
        }
    }
    ```
  </TabsContent>

  <TabsContent value="node-js">
    ```js
    const RtcTokenBuilder = require("../src/RtcTokenBuilder2").RtcTokenBuilder;

    // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
    const appId = process.env.AGORA_APP_ID;
    // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
    const appCertificate = process.env.AGORA_APP_CERTIFICATE;

    // Replace channelName with the name of the channel you want to join
    const channelName = "channelName";
    // Fill in your actual user ID
    const uid = 2082341273;
    // Token validity time in seconds
    const tokenExpirationInSecond = 3600;
    // Validity time for permission to join a channel (seconds)
    const joinChannelPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish audio streams in the channel
    const pubAudioPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish video streams in the channel
    const pubVideoPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish data streams in the channel
    const pubDataStreamPrivilegeExpireInSeconds = 3600;

    console.log("App Id:", appId);
    console.log("App Certificate:", appCertificate);
    if (appId == undefined || appId == "" || appCertificate == undefined || appCertificate == "") {
        console.log("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE");
        process.exit(1);
    }

    // Generate Token
    const tokenWithUidAndPrivilege = RtcTokenBuilder.buildTokenWithUidAndPrivilege(
        appId,
        appCertificate,
        channelName,
        uid,
        tokenExpirationInSecond,
        joinChannelPrivilegeExpireInSeconds,
        pubAudioPrivilegeExpireInSeconds,
        pubVideoPrivilegeExpireInSeconds,
        pubDataStreamPrivilegeExpireInSeconds
    );
    console.log("Token with int uid and privilege:", tokenWithUidAndPrivilege);
    ```
  </TabsContent>

  <TabsContent value="php">
    ```js
    <?php
    include("../src/RtcTokenBuilder2.php");

    // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
    $appId = getenv("AGORA_APP_ID");
    // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
    $appCertificate = getenv("AGORA_APP_CERTIFICATE");

    // Replace channelName with the name of the channel you want to join
    $channelName = "channelName";
    // Fill in your actual user ID
    $uid = 2882341273;
    // Token validity time in seconds
    $tokenExpirationInSeconds = 3600;
    // Validity time for permission to join a channel (seconds)
    $joinChannelPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish audio streams in the channel
    $pubAudioPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish video streams in the channel
    $pubVideoPrivilegeExpireInSeconds = 3600;
    // Validity in seconds for the permission to publish data streams in the channel
    $pubDataStreamPrivilegeExpireInSeconds = 3600;

    echo "App Id: " . $appId . PHP_EOL;
    echo "App Certificate: " . $appCertificate . PHP_EOL;
    if ($appId == "" || $appCertificate == "") {
        echo "Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE" . PHP_EOL;
        exit;
    }

    // Generate Token
    $token = RtcTokenBuilder2::buildTokenWithUidAndPrivilege($appId, $appCertificate, $channelName, $uid, $tokenExpirationInSeconds, $joinChannelPrivilegeExpireInSeconds, $pubAudioPrivilegeExpireInSeconds, $pubVideoPrivilegeExpireInSeconds, $pubDataStreamPrivilegeExpireInSeconds);
    echo 'Token with int uid and privilege: ' . $token . PHP_EOL;
    ```
  </TabsContent>

  <TabsContent value="python">
    ```python
    import os

    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

    from src.RtcTokenBuilder2 import *

    def main():
        # Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
        app_id = os.environ.get("AGORA_APP_ID")
        # Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        app_certificate = os.environ.get("AGORA_APP_CERTIFICATE")

        # Replace channel_name with the name of the channel to join
        channel_name = "channel_name"
        # Fill in your actual user ID
        uid = 2882341273
        # Token validity time in seconds
        token_expiration_in_seconds = 3600
        # Validity time for permission to join a channel (seconds)
        join_channel_privilege_expiration_in_seconds = 3600
        # Validity in seconds for the permission to publish audio streams in the channel
        pub_audio_privilege_expiration_in_seconds = 3600
        # Validity in seconds for the permission to publish video streams in the channel
        pub_video_privilege_expiration_in_seconds = 3600
        # Validity in seconds for the permission to publish data streams in the channel
        pub_data_stream_privilege_expiration_in_seconds = 3600

        print("App Id: %s" % app_id)
        print("App Certificate: %s" % app_certificate)
        if not app_id or not app_certificate:
            print("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE")
            return

        # Generate Token
        token = RtcTokenBuilder.build_token_with_uid_and_privilege(
            app_id, app_certificate, channel_name, uid, token_expiration_in_seconds,
            join_channel_privilege_expiration_in_seconds, pub_audio_privilege_expiration_in_seconds, pub_video_privilege_expiration_in_seconds, pub_data_stream_privilege_expiration_in_seconds)
        print("Token with int uid and privilege: {}".format(token))

    if __name__ == "__main__":
        main()
    ```
  </TabsContent>

  <TabsContent value="java">
    ```java
    package io.agora.sample;

    public class RtcTokenBuilder2Sample {
        // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
        static String appId = System.getenv("AGORA_APP_ID");
        // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
        static String appCertificate = System.getenv("AGORA_APP_CERTIFICATE");

        // Replace channelName with the name of the channel you want to join
        static String channelName = "channelName";
        // Fill in your actual user ID
        static int uid = 2082341273;
        // Token validity time in seconds
        static int tokenExpirationInSeconds = 3600;
        // Validity time for permission to join a channel (seconds)
        static int joinChannelPrivilegeExpireInSeconds = 3600;
        // Validity in seconds for the permission to publish audio streams in the channel
        static int pubAudioPrivilegeExpireInSeconds = 3600;
        // Validity in seconds for the permission to publish video streams in the channel
        static int pubVideoPrivilegeExpireInSeconds = 3600;
        // Validity in seconds for the permission to publish data streams in the channel
        static int pubDataStreamPrivilegeExpireInSeconds = 3600;

        public static void main(String[] args) {
            System.out.printf("App Id: %s\\n", appId);
            System.out.printf("App Certificate: %s\\n", appCertificate);
            if (appId == null || appId.isEmpty() || appCertificate == null || appCertificate.isEmpty()) {
                System.out.printf("Need to set environment variable AGORA_APP_ID and AGORA_APP_CERTIFICATE\\n");
                return;
            }

            // Generate Token
            result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, tokenExpirationInSeconds,
                    joinChannelPrivilegeExpireInSeconds, pubAudioPrivilegeExpireInSeconds,
                    pubVideoPrivilegeExpireInSeconds,
                    pubDataStreamPrivilegeExpireInSeconds);
            System.out.printf("Token with uid and privilege: %s\\n", result);
        }
    }
    ```
  </TabsContent>

  <TabsContent value="cpp">
    ```cpp
    #include <cstdlib>
    #include <iostream>

    #include "../src/RtcTokenBuilder2.h"

    using namespace agora::tools;

    int main(int argc, char const *argv[]) {
      (void)argc;
      (void)argv;

      // Get the value of the environment variable AGORA_APP_ID. Make sure you set this variable to the value you obtained from Agora console
      const char *env_app_id = getenv("AGORA_APP_ID");
      std::string app_id = env_app_id ? env_app_id : "";
      // Get the value of the environment variable AGORA_APP_CERTIFICATE. Make sure you set this variable to the App certificate you obtained from Agora console
      const char *env_app_certificate = getenv("AGORA_APP_CERTIFICATE");
      std::string app_certificate = env_app_certificate ? env_app_certificate : "";

      // Replace channelName with the name of the channel you want to join
      std::string channel_name = "channelName";
      // Fill in your actual user ID
      uint32_t uid = 2882341273;
      // Token validity period (seconds)
      uint32_t token_expiration_in_seconds = 3600;
      // Validity time of permission to join channel (seconds)
      uint32_t join_channel_privilege_expiration_in_seconds = 3600;
      // The validity period of the permission to publish audio streams in the channel (seconds)
      uint32_t pub_audio_privilege_expiration_in_seconds = 3600;
      // The validity period of the permission to publish video streams in the channel (seconds)
      uint32_t pub_video_privilege_expiration_in_seconds = 3600;
      // The validity period of the permission to publish data streams in the channel (seconds)
      uint32_t pub_data_stream_privilege_expiration_in_seconds = 3600;
      std::string result;

      std::cout << "App Id:" << app_id << std::endl;
      std::cout << "App Certificate:" << app_certificate << std::endl;
      if (app_id == "" || app_certificate == "") {
        std::cout << "Need to set environment variable AGORA_APP_ID and "
                     "AGORA_APP_CERTIFICATE"
                  << std::endl;
        return -1;
      }

      // Generate Token
      result = RtcTokenBuilder2::BuildTokenWithUid(
          app_id, app_certificate, channel_name, uid, token_expiration_in_seconds,
          join_channel_privilege_expiration_in_seconds,
          pub_audio_privilege_expiration_in_seconds,
          pub_video_privilege_expiration_in_seconds,
          pub_data_stream_privilege_expiration_in_seconds);
      std::cout << "Token With Int Uid:" << result << std::endl;

      return 0;
    }
    ```
  </TabsContent>
</Tabs>

## Deploy a token server [#deploy-a-token-server]

This section presents multiple methods of deploying a token generator locally. After successful deployment, you can use the generated tokens to test and debug your Cloud Recording project.

### Use the NPM package [#use-the-npm-package]

The [`agora-token`](https://www.npmjs.com/package/agora-token) NPM package provides a library for generating tokens and includes code for a demo server that handles token generation requests. Follow these steps to install the package and run the demo server:

1. Install the NPM package

   ```bash
   npm install agora-token
   ```

2. Add your App ID and App certificate

   Open the file `node_modules/agora-token/server/DemoServer.js` and update the following variables:

   ```js
   // Replace with your Agora credentials
   var appID = "<YOUR APP ID>";
   var appCertificate = "<YOUR APP CERTIFICATE>";
   ```

3. Install the dependencies and run the server

   Navigate to the `server` directory and run the following commands:

   ```bash
   npm i
   node DemoServer.js
   ```

4. Test your server

   Use a `curl` request to generate a sample token:

   ```bash
   curl http://localhost:8080/rtcToken?channelName=test
   ```

   You receive a response like the following:

   ```json
   {
       "key": "YOUR_GENERATED_TOKEN"
   }
   ```

### Deploy with Docker [#deploy-with-docker]

Refer to the following steps to deploy a Docker-based token generator locally:

1. Install [Docker](https://www.docker.com/).

2. Open a terminal and run the following command to start the token generator. Replace `[YOUR_APP_ID]` and `[YOUR_APP_CERTIFICATE]` with the App ID and App certificate you obtained from Agora Console.

   ```bash
   docker run -d -it -p 8080:8080 -e APP_ID=[YOUR_APP_ID] -e APP_CERTIFICATE=[YOUR_APP_CERTIFICATE] --name agora-token-service agoracn/token:0.1.2023053011
   ```

3. Run the following command to request a token from the token generator you just deployed:

   ```bash
   curl --location 'http://localhost:8080/token/generate' \
   --header 'Content-Type: application/json' \
   --data '{
       "channelName": "channel_name_test",
       "uid": "12345678",
       "tokenExpireTs": 3600,
       "privilegeExpireTs": 3600,
       "serviceRtc": {
           "enable": true,
           "role": 1
       }
   }'
   ```

   After the request is successful, your terminal displays the generated token.

### Manual local deployment [#manual-local-deployment]

This section demonstrates how to deploy a token server locally using Golang as an example. Before you begin, ensure that you have [Golang](https://golang.org/dl/) version 1.14 or later installed.

Refer to the following steps to build and run a token generator:

1. Create a new `token-server` folder. Add a `server.go` file inside the folder and paste the following Golang sample code into the file. Replace the values for `appID` and `appCertificate` with your App ID and App certificate.

<Accordions>
  <Accordion title="Sample code for a token server">
    ```go
        package main

            import (
                rtctokenbuilder "github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2"
                "fmt"
                "log"
                "net/http"
                "encoding/json"
                "errors"
                "strconv"
            )

            type rtc_int_token_struct struct{
                Uid_rtc_int uint32 \`json:"uid"\`
                Channel_name string \`json:"ChannelName"\`
                Role uint32 \`json:"role"\`
            }

            var rtc_token string
            var int_uid uint32
            var channel_name string

            var role_num uint32
            var role rtctokenbuilder.Role

            // Use RtcTokenBuilder to generate RTC Token
            func generateRtcToken(int_uid uint32, channelName string, role rtctokenbuilder.Role){
                appID := "<Your App ID>"
                appCertificate := "<Your App Certificate>"
                // Ensure that the expiration time of the token is later than the permission expiration time. Permission expiration time, unit is seconds
                tokenExpireTimeInSeconds := uint32(40)
                // The token-privilege-will-expire callback is triggered 30 seconds before the permission expires.
                // For demonstration purposes, set the expiration time to 40 seconds. You can see the process of the client automatically updating the Token
                privilegeExpireTimeInSeconds := uint32(40)

                result, err := rtctokenbuilder.BuildTokenWithUid(appID, appCertificate, channelName, int_uid, role, tokenExpireTimeInSeconds, privilegeExpireTimeInSeconds)
                if err != nil {
                    fmt.Println(err)
                } else {
                    fmt.Printf("Token with uid: %s\\n", result)
                    fmt.Printf("uid is %d\\n", int_uid )
                    fmt.Printf("ChannelName is %s\\n", channelName)
                    fmt.Printf("Role is %d\\n", role)
                }
                rtc_token = result
            }

            func rtcTokenHandler(w http.ResponseWriter, r *http.Request){
                w.Header().Set("Content-Type", "application/json; charset=UTF-8")
                w.Header().Set("Access-Control-Allow-Origin", "*")
                w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS");
                w.Header().Set("Access-Control-Allow-Headers", "*");

                if r.Method == "OPTIONS" {
                    w.WriteHeader(http.StatusOK)
                    return
                }

                if r.Method != "POST" && r.Method != "OPTIONS" {
                    http.Error(w, "Unsupported method. Please check.", http.StatusNotFound)
                    return
                }

                var t_int rtc_int_token_struct
                var unmarshalErr *json.UnmarshalTypeError
                int_decoder := json.NewDecoder(r.Body)
                int_err := int_decoder.Decode(&t_int)
                if (int_err == nil) {

                            int_uid = t_int.Uid_rtc_int
                            channel_name = t_int.Channel_name
                            role_num = t_int.Role
                            switch role_num {
                                case 1:
                                    role = rtctokenbuilder.RolePublisher
                                case 2:
                                    role = rtctokenbuilder.RoleSubscriber
                            }
                }
                if (int_err != nil) {

                    if errors.As(int_err, &unmarshalErr){
                            errorResponse(w, "Bad request. Wrong type provided for field " + unmarshalErr.Value  + unmarshalErr.Field + unmarshalErr.Struct, http.StatusBadRequest)
                            } else {
                            errorResponse(w, "Bad request.", http.StatusBadRequest)
                        }
                    return
                }

                generateRtcToken(int_uid, channel_name, role)
                errorResponse(w, rtc_token, http.StatusOK)
                log.Println(w, r)
            }

            func errorResponse(w http.ResponseWriter, message string, httpStatusCode int){
                w.Header().Set("Content-Type", "application/json")
                w.Header().Set("Access-Control-Allow-Origin", "*")
                w.WriteHeader(httpStatusCode)
                resp := make(map[string]string)
                resp["token"] = message
                resp["code"] = strconv.Itoa(httpStatusCode)
                jsonResp, _ := json.Marshal(resp)
                w.Write(jsonResp)
            }

            func main(){
                // Use int type uid to generate RTC Token
                http.HandleFunc("/fetch_rtc_token", rtcTokenHandler)
                fmt.Printf("Starting server at port 8082\n")

                if err := http.ListenAndServe(":8082", nil); err != nil {
                    log.Fatal(err)
                }
            }
    ```
  </Accordion>
</Accordions>

2. Open the terminal, go to the `token-server` folder path, and run the following command line to create a `go.mod` file for your token generator. This file defines the import path and dependencies:

   ```go
   go mod init sampleServer
   ```

3. Run the following command to install dependencies.

   ```go
   go get github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src/rtctokenbuilder2
   ```

4. Run the following command to start the token generator:

   ```go
   go run server.go
   ```

After the token generator is deployed successfully, your terminal returns the token generated based on the App ID and App certificate you filled in.

## Reference [#reference]

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

<a name="api-reference" />

### API Reference [#api-reference]

This section documents the core methods you use to generate tokens, using the Golang code as an example.

#### `BuildTokenWithUid` [#buildtokenwithuid]

Generate a token, set the expiration time, and define all permissions.

```go
func BuildTokenWithUid(
    appId string, appCertificate string,
    channelName string, uid uint32,
    role Role,
    tokenExpire uint32, privilegeExpire uint32,
)
```

**Parameters**

* `appId`: The App ID generated when you create a project in Agora Console.
* `appCertificate`: The App certificate of your project.
* `channelName`: Channel name, length should be less than 64 bytes.
* `uid`: The unique user ID of the user to be authenticated. It is a 32-bit signed integer with a value range from -231 to 231 - 1.
* `role`: User permissions, which determine whether the user can send streams in the channel.
  * `kRolePublisher` (1): (Default) The user has the permission to send streams in the channel.
  * `kRoleSubscriber` (2): The user has receiving permission only. The user can receive streams in the channel, but cannot send streams. This parameter only takes effect after Co-host authentication is enabled.
* `tokenExpire`: The token validity period (seconds). The maximum validity period is 24 hours.
* `privilegeExpire`: Validity time of all permissions (seconds). If set to 0 (default value), it means that the permission will never expire.

<CalloutContainer type="warning">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    If the token expires but the permissions have not expired, the user remains in the channel and can continue to send streams. Any callbacks related to the token in the SDK are not triggered. Once disconnected from the channel, the user is not able to use the expired token to join the same channel. Best practice is to use consistent settings for the token expiration time and the permission expiration time.
  </CalloutDescription>
</CalloutContainer>

#### `BuildTokenWithUidAndPrivilege` [#buildtokenwithuidandprivilege]

Generate a token with fine control over streaming permissions. Set the validity period of the token and the expiration time of permissions to join channels, publish audio, video, and data streams.

```go
func BuildTokenWithUidAndPrivilege(
    appId string, appCertificate string,
    channelName string, uid uint32,
    tokenExpire uint32, joinChannelPrivilegeExpire uint32,
    pubAudioPrivilegeExpire uint32, pubVideoPrivilegeExpire uint32, pubDataStreamPrivilegeExpire uint32
)
```

**Parameters**

* `appId`: The App ID generated when you create a project in Agora Console.
* `appCertificate`: The App certificate of your project.
* `channelName`: Channel name, length should be less than 64 bytes.
* `uid`: The unique user ID of the user to be authenticated. It is a 32-bit signed integer with a value range from -231 to 231 - 1.
* `tokenExpire`: The token validity period (seconds). The maximum validity period is 24 hours.
* `joinChannelPrivilegeExpire`: Validity in seconds for the permission to join the channel (seconds).
* `pubAudioPrivilegeExpire`: Validity in seconds for the permission to publish audio streams in the channel.
* `pubVideoPrivilegeExpire`: Validity in seconds for the permission to publish video streams in the channel.
* `pubDataStreamPrivilegeExpire`: Validity in seconds for the permission to publish data streams in the channel.

<CalloutContainer type="warning">
  <CalloutTitle>
    Note
  </CalloutTitle>

  <CalloutDescription>
    * The expiration time of the different permissions is calculated from the time the token is generated.
    * Agora recommends that the token expiration time be consistent with the expiration time of other permissions.
    * The permission to publish audio, video and data streams in the channel only takes effect after Co-host token authentication service is enabled. If the permission to join the channel expires earlier than the permission to publish an audio stream, the user is kicked out of the channel after the permission to join the channel expires.
  </CalloutDescription>
</CalloutContainer>

### Compatibility [#compatibility]

`AccessToken2` is supported on the following versions of Agora SDK (excluding client side bypass push function):

For RTC 4.x SDK:

* Android, iOS, macOS, Windows, Electron, Unity, React Native, or Unreal SDK version must be greater than or equal to v4.0.0
* Flutter SDK version must be greater than or equal to v6.0.0
* Web SDK version must be greater than or equal to v4.8.0

<CalloutContainer type="info">
  <CalloutTitle>
    Information
  </CalloutTitle>

  <CalloutDescription>
    SDKs using `AccessToken2` can interoperate with SDKs using `AccessToken`. The SDK versions that supports `AccessToken2` also supports `AccessToken`.
  </CalloutDescription>
</CalloutContainer>

### Frequently asked questions [#frequently-asked-questions]

* [How do I use co-host token authentication?](/help/integration-issues/token_cohost)
* [How to deal with token-related error codes?](/help/integration-issues/token_related_issues)
