Use tokens

Updated

Retrieve tokens generated by an authentication token server to securely connect to Agora.

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 Voice Calling:

Token authentication flow

Prerequisites

Before starting, ensure that you have:

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 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>
      <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.

    // 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

    // 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.

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:

// 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

  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.

The user ID and channel name used to join a channel must be consistent with the values used to generate the token.

Reference

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