# Use tokens (/en/realtime-media/rtc/build/authenticate-users/authentication-workflow/web)

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

To protect your business, it is best practice to authenticate every client that joins a channel. This guide explains how to fetch an authentication token from your token server, use it to join a channel, and renew the token when it expires.

## Understand the tech

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

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

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

## Prerequisites

Before starting, ensure that you have:

* Implemented the [Quickstart](/en/realtime-media/rtc/get-started-sdk) in your project.

* Deployed a token server using either of the following guides:

  * [Deploy a token server](/en/realtime-media/rtc/build/authenticate-users/deploy-token-server)
  * [Deploy a middleware server](/en/realtime-media/rtc/build/authenticate-users/middleware-token-server)

## Implement basic authentication

This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.

      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
### Use a token to join a channel

This section shows you how to integrate token authentication in your app.

1. Open the [Quickstart](/en/realtime-media/rtc/get-started-sdk) project you created earlier.

2. In `index.html`, include an HTTP client library such as `axios` for sending token requests to the authentication server.

   ```html
   <html>
       <head>
           <title>Token demo</title>
       </head>
       <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
       <body>
           <h1>Token demo</h1>
           <script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.24.6.js"></script>
           <script src="../agoraLogic.js"></script>
       </body>
   </html>
   ```

3. Add a `fetchToken` method to retrieve a token from your token server to join a channel.

   ```js
   // Retrieve a token from your token server
   function fetchToken(uid, channelName, tokenRole) {

       return new Promise(function (resolve) {
           axios.post('http://<Your host URL and port>/fetch_rtc_token', {
               uid: uid,
               channelName: channelName,
               role: tokenRole
           }, {
               headers: {
                   'Content-Type': 'application/json; charset=UTF-8'
               }
           })
               .then(function (response) {
                   const token = response.data.token;
                   resolve(token);
               })
               .catch(function (error) {
                   console.log(error);
               });
       })
   }
   ```

4. Use the token to join a channel

   ```js
   // Assign the obtained token to the token parameter in the join method
   let token = await fetchToken(uid, options.channel, 1);
   await client.join(options.appId, options.channel, token, uid);
   ```

### Token expiration

After you join a channel using a token, the SDK triggers the `token-privilege-will-expire` callback, 30 seconds before the token is set to expire. Upon receiving this callback, retrieve a fresh token from the server and call the `renewToken` method to pass the newly generated token to the SDK.

```javascript
client.on("token-privilege-will-expire", async function () {
    // When you receive the token-privilege-will-expire callback, request a fresh token from the server
    let token = await fetchToken(uid, options.channel, 1);
    // Call renewToken to pass the new token to the SDK
    await client.renewToken(token);
});
```

When the token expires, the SDK triggers the `token-privilege-did-expire` callback. In this case, retrieve a fresh token from the server and call the `join` method to rejoin the channel with the new token:

```javascript
// The token expired.
client.on("token-privilege-did-expire", async function () {
console.log("Fetching a new token")
// Request a new token from the server
let token = await fetchToken(uid, options.channel, 1);
console.log("Rejoining the channel with new token")
// Call join to rejoin the channel
await client.join(options.appId, options.channel, token, uid);
});
```

### Complete sample code

For a complete implementation of token authentication, refer to the following code:

**Sample code for basic authentication**

```js
var rtc = {
        // Set local audio track and video track
        localAudioTrack: null,
        localVideoTrack: null,
    };

    var options = {
        // Fill in app ID
        appId: "<Your app ID>",
        // Fill in a channel name
        channel: "test",
        // Set the user as host or audience
        role: "host"
    };

    // Get a token from your token server

    function fetchToken(uid, channelName, tokenRole) {

        return new Promise(function (resolve) {
            axios.post('http://<Your host URL and port>/fetch_rtc_token', {
                uid: uid,
                channelName: channelName,
                role: tokenRole
            }, {
                headers: {
                    'Content-Type': 'application/json; charset=UTF-8'
                }
            })
                .then(function (response) {
                    const token = response.data.token;
                    resolve(token);
                })
                .catch(function (error) {
                    console.log(error);
                });
        })
    }

    async function startBasicCall() {

        const client = AgoraRTC.createClient({ mode: "live", codec: "vp8" });
        client.setClientRole(options.role);
        const uid = 123456;

        // Assign the obtained token to the token parameter of the join method, and join the channel
        let token = await fetchToken(uid, options.channel, 1);

        await client.join(options.appId, options.channel, token, uid);
        rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
        rtc.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
        await client.publish([rtc.localAudioTrack, rtc.localVideoTrack]);
        const localPlayerContainer = document.createElement("div");
        localPlayerContainer.id = uid;
        localPlayerContainer.style.width = "640px";
        localPlayerContainer.style.height = "480px";
        document.body.append(localPlayerContainer);

        rtc.localVideoTrack.play(localPlayerContainer);

        console.log("publish success!");

        client.on("user-published", async (user, mediaType) => {
            await client.subscribe(user, mediaType);
            console.log("subscribe success");

            if (mediaType === "video") {
                const remoteVideoTrack = user.videoTrack;
                const remotePlayerContainer = document.createElement("div");
                remotePlayerContainer.textContent = "Remote user " + user.uid.toString();
                remotePlayerContainer.style.width = "640px";
                remotePlayerContainer.style.height = "480px";
                document.body.append(remotePlayerContainer);
                remoteVideoTrack.play(remotePlayerContainer);

            }

            if (mediaType === "audio") {
                const remoteAudioTrack = user.audioTrack;
                remoteAudioTrack.play();
            }

            client.on("user-unpublished", user => {
                const remotePlayerContainer = document.getElementById(user.uid);
                remotePlayerContainer.remove();
            });

        });

        // When you receive the token-privilege-will-expire callback, request a new token from the server and call renewToken to pass the new token to the SDK
        client.on("token-privilege-will-expire", async function () {
            let token = await fetchToken(uid, options.channel, 1);
            await client.renewToken(token);
        });

        // When you receive the token-privilege-did-expire callback, request a new token from the server and call join to rejoin the channel.
        client.on("token-privilege-did-expire", async function () {
            console.log("Fetching a new token")
            let token = await fetchToken(uid, options.channel, 1);
            console.log("Rejoining the channel with a new token")
            await client.join(options.appId, options.channel, token, uid);
        });

    }

    startBasicCall()
```

Replace `<Your app ID>` with your app ID, which must be consistent with the app ID you specified in the server configuration. Update `<Your Host URL and port>` with the host URL and port of the local Golang server you have deployed. For example `99.9.9.99:8082`.

Build and run the project on the local device, the app performs the following operations:

* Obtains a token from your token server.
* Joins the channel.
* Automatically renews the token when it is about to expire.

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

  <CalloutDescription>
    The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
  </CalloutDescription>
</CalloutContainer>

## Reference

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

      
  
      
  
      
  
      
  
      
* [Reference app](https://github.com/AgoraIO/video-sdk-samples-web/tree/main/Demo/basicVideoCall)

    
  
      
  
      
  
      
  
      
  
      
  
      
  
