# Use tokens (/en/realtime-media/video/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 [#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 [#prerequisites]

Before starting, ensure that you have:

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

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

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

## Implement basic authentication [#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 [#use-a-token-to-join-a-channel-9]

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

    1. Open the [Quickstart](/en/realtime-media/video/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.5.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 [#token-expiration-5]

    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 [#complete-sample-code-3]

    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 [#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)

    
  
      
  
      
  
      
  
      
  
      
  
      
  
