# Use tokens (/en/realtime-media/video/build/authenticate-users/authentication-workflow/electron)

> 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-1]

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

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

    2. In `renderer.js` replace the contents with the following:

    **Sample code for basic authentication**

    ```dart
    const {
            createAgoraRtcEngine,
            VideoMirrorModeType,
            VideoSourceType,
            RenderModeType,
            ChannelProfileType,
            ClientRoleType,
        } = require("agora-electron-sdk");

        let rtcEngine;
        let localVideoContainer;
        let remoteVideoContainer;
        let isJoined = false;

        const EventHandles = {
            // Listen to local user joining channel events
            onJoinChannelSuccess: ({ channelId, localUid }, elapsed) => {
                console.log('Successfully joined the channel：' + channelId);
                isJoined = true;
                // After local users join the channel, set the local video window
                rtcEngine.setupLocalVideo({
                    sourceType: VideoSourceType.VideoSourceCameraPrimary,
                    view: localVideoContainer,
                    mirrorMode: VideoMirrorModeType.VideoMirrorModeDisabled,
                    renderMode: RenderModeType.RenderModeFit,
                });
            },

            onLeaveChannel: ({ channelId, localUid }, stats) => {
                console.log('Successfully left the channel:' + channelId);
                isJoined = false;
            },

            // Listen to remote user join event
            onUserJoined: ({ channelId, localUid }, remoteUid, elapsed) => {
                console.log('Remote user ' + remoteUid + ' 已加入');
                // Listen to remote user join event
                rtcEngine.setupRemoteVideoEx(
                    {
                        sourceType: VideoSourceType.VideoSourceRemote,
                        uid: remoteUid,
                        view: remoteVideoContainer,
                        mirrorMode: VideoMirrorModeType.VideoMirrorModeDisabled,
                        renderMode: RenderModeType.RenderModeFit,
                    },
                    { channelId },
                );
            },

            // Listen for the token expiration event
            onTokenPrivilegeWillExpire: ({ channelId, localUid }, token) => {
                console.log('token ' + token + '即将过期');
                // Obtain a new token from the server
                fetchToken(channelId, localUid, ClientRoleType.ClientRoleBroadcaster)
            },

            // Listen for the token expired event
            onRequestToken: ({ channelId, localUid }) => {
                console.log('token expired');
                token = ''
            }
        };

        // Send a token request to the token server
        function fetchToken(channelName, uid, role) {
            //Token server URL example: http://12.123.1.123:8082/fetch_rtc_token
            const url = '<Your host URL and port>/fetch_rtc_token';
            const body = JSON.stringify({
                uid,
                ChannelName: channelName,
                role
            });
            fetch(url, {
                method: 'POST',
                body,
            })
                .then((res) => res.json())
                .then((res) => {
                    console.log('token ' + res.token);
                    if (+res.code === 200) {
                        if (isJoined) {
                            rtcEngine.renewToken(res.token)
                        } else {
                            rtcEngine.joinChannel(res.token, channelName, uid, { clientRoleType: role })
                        }
                    }
                    return res;
                });
        }

        window.onload = () => {
            const os = require("os");
            const path = require("path");

            // Fill in your app ID
            const APPID = "<Your app ID>";
            // Fill in the channel name
            const channel = "Test";

            localVideoContainer = document.getElementById("join-channel-local-video");
            remoteVideoContainer = document.getElementById("join-channel-remote-video");
            const sdkLogPath = path.resolve(os.homedir(), "./test.log");

            // Create an RtcEngine instance
            rtcEngine = createAgoraRtcEngine();

            // Initialize the RtcEngine instance
            rtcEngine.initialize({
                appId: APPID,
                logConfig: { filePath: sdkLogPath }
            });

            // Register event handler
            rtcEngine.registerEventHandler(EventHandles);

            //Set the channel profile to live broadcast
            rtcEngine.setChannelProfile(ChannelProfileType.ChannelProfileLiveBroadcasting);

            // Set the user role, for host use ClientRoleBroadcaster, for audience use ClientRoleAudience
            rtcEngine.setClientRole(ClientRoleType.ClientRoleBroadcaster);

            // Enable the video module
            rtcEngine.enableVideo();

            // Enable camera preview
            rtcEngine.startPreview();

            // Use the token to join a channel
            // You need to specify the user ID yourself and ensure its uniqueness within the channel
            fetchToken(channel, 123456, ClientRoleType.ClientRoleBroadcaster);
        };
    ```

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

    The sample code implements the following logic:

    * Calls `joinChannel` to join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token.

    * The SDK triggers an `onTokenPrivilegeWillExpire` callback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and call `renewToken` to pass the newly generated token to the SDK.

    * If the token expires, the SDK triggers an `onRequestToken` callback. After receiving the callback, obtain a new token from the server and call `joinChannel` with the new token to rejoin the channel.

    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.

      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
    ### API reference [#api-reference-4]

    * [renewToken](https://api-ref.agora.io/en/voice-sdk/electron/4.x/API/class_irtcengine.html#api_irtcengine_renewtoken)
    * [onTokenPrivilegeWillExpire](https://api-ref.agora.io/en/voice-sdk/electron/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_ontokenprivilegewillexpire)
    * [onRequestToken](https://api-ref.agora.io/en/voice-sdk/electron/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onrequesttoken)

    
  
      
  
      
  
      
  
