# Use tokens (/en/realtime-media/interactive-live-streaming/build/authenticate-users/use-tokens/react-native)

> 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 Interactive Live Streaming:

**Token authentication flow**

![token authentication flow](https://assets-docs.agora.io/images/video-sdk/token-authentication.svg)

## Prerequisites

Before starting, ensure that you have:

* Implemented the [Quickstart](../../index.mdx) in your project.

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

  * [Deploy a token server](deploy-token-server.mdx)
  * [Deploy a middleware server](middleware-token-server.md)

## 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 [SDK quickstart](../../index.mdx) project you created earlier.

2. In `ProjectName.tsx` replace the contents with the following:

**Sample code for basic authentication**

```js
  import React, { useCallback, useEffect, useRef, useState } from 'react';
    import {
      PermissionsAndroid,
      Platform,
      SafeAreaView,
      ScrollView,
      StyleSheet,
      Switch,
      Text,
      View,
    } from 'react-native';

    import {
      ChannelProfileType,
      ClientRoleType,
      createAgoraRtcEngine,
      IRtcEngine,
      IRtcEngineEventHandler,
      RtcConnection,
      RtcSurfaceView,
    } from 'react-native-agora';

    // Define basic information
    const appId = '<Your app ID>';
    const channelName = 'test';
    const uid = 123; // Local user Uid

    const App = () => {
    const agoraEngineRef = useRef<IRtcEngine>(); // IRtcEngine instance
    const eventHandlerRef = useRef<IRtcEngineEventHandler>(); // IRtcEngine event handler
    const [token, setToken] = useState<string>(''); // RTC Token
    const [isJoined, setIsJoined] = useState(false); // Whether the local user has joined the channel
    const [isHost, setIsHost] = useState(true); // User role
    const [remoteUid, setRemoteUid] = useState(0); // Uid of remote user
    const [message, setMessage] = useState(''); // Message to the user

    // Send a request to obtain a token to the token server
    const fetchToken = useCallback(() => {
      // 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: isHost
        ? ClientRoleType.ClientRoleBroadcaster
        : ClientRoleType.ClientRoleAudience,
      });
      console.log('fetchToken', url, body);
      return fetch(url, {
        method: 'POST',
        body,
      })
      .then((res) => res.json())
      .then((res) => {
        showMessage('token ' + res.token + 'Already refreshed');
        if (+res.code === 200) {
        setToken(res.token);
        }
        return res;
      });
      }, [isHost]);

    // Use the obtained token to join the channel
    const join = useCallback(async () => {
      if (!token) {
        await fetchToken();
      } else {
      try {
        // After joining the channel, set the channel profile to live broadcast
        agoraEngineRef.current?.setChannelProfile(
        ChannelProfileType.ChannelProfileLiveBroadcasting
        );
        if (isHost) {
        agoraEngineRef.current?.startPreview();
        // Join the channel as a host
        agoraEngineRef.current?.joinChannel(token, channelName, uid, {
          publishCameraTrack: true,
          clientRoleType: ClientRoleType.ClientRoleBroadcaster,
        });
        } else {
        // Join the channel as audience
        agoraEngineRef.current?.joinChannel(token, channelName, uid, {
          clientRoleType: ClientRoleType.ClientRoleAudience,
        });
        }
      } catch (e) {
        console.log(e);
      }
    }
    }, [fetchToken, isHost, token]);

    const leave = () => {
      try {
        agoraEngineRef.current?.leaveChannel();
        setRemoteUid(0);
        setIsJoined(false);
        showMessage('Left the channel');
      } catch (e) {
        console.error(e);
      }
    };

    useEffect(() => {
      const setupVideoSDKEngine = async () => {
      try {
        if (Platform.OS === 'android') {
        await getPermission();
        }
        agoraEngineRef.current = createAgoraRtcEngine();
        eventHandlerRef.current = {
        onJoinChannelSuccess: () => {
          showMessage('Successfully joined the channel: ' + channelName);
          setIsJoined(true);
        },
        onUserJoined: (_connection, Uid) => {
          showMessage('Remote user ' + Uid + ' joined');
          setRemoteUid(Uid);
        },
        onUserOffline: (_connection, Uid) => {
          showMessage('Remote user ' + Uid + ' left the channel');
          setRemoteUid(0);
        },
        onTokenPrivilegeWillExpire(connection: RtcConnection, token: string) {
          showMessage('token ' + token + 'Expires soon');
          fetchToken();
        },
        onRequestToken(connection: RtcConnection) {
          showMessage('token expired');
          setToken('');
        },
        };
        const agoraEngine = agoraEngineRef.current;
        //Initialize engine
        agoraEngine.initialize({
          appId: appId,
          channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
        });
        // Register event handler
        agoraEngine.registerEventHandler(eventHandlerRef.current);
        // Start local video
        agoraEngine.enableVideo();
      } catch (e) {
        console.error(e);
      }
    };

      setupVideoSDKEngine();

      return () => {
        agoraEngineRef.current?.release();
      };
    }, []); // eslint-disable-line react-hooks/exhaustive-deps

    useEffect(() => {
      const checkJoined = () => {
      if (isJoined) {
        console.log('renewToken');
        // agoraEngineRef.current?.renewToken(token);
      } else {
        console.log('joinChannel');
        join();
      }
      };
      checkJoined();
    }, [token]); // eslint-disable-line react-hooks/exhaustive-deps

    // Render the user interface
    return (
      <SafeAreaView style={styles.main}>
      <Text style={styles.head}>Agora SDK Quickstart</Text>
      <View style={styles.btnContainer}>
        <Text onPress={join} style={styles.button}>
        Join channel
        </Text>
        <Text onPress={leave} style={styles.button}>
        Leave Channel
        </Text>
      </View>
      <View style={styles.btnContainer}>
        <Text>Audience</Text>
        <Switch
        onValueChange={(switchValue) => {
          setIsHost(switchValue);
          if (isJoined) {
          leave();
          }
        }}
        value={isHost}
        />
        <Text>Host</Text>
      </View>
      <ScrollView
        style={styles.scroll}
        contentContainerStyle={styles.scrollContainer}
      >
        {isJoined ? (
        <React.Fragment key={0}>
          <RtcSurfaceView canvas={{ uid: 0 }} style={styles.videoView} />
          <Text>Local user uid: {uid}</Text>
        </React.Fragment>
        ) : (
        <Text>Join a channel</Text>
        )}
        {isJoined && !isHost && remoteUid !== 0 ? (
        <React.Fragment key={remoteUid}>
          <RtcSurfaceView
          canvas={{ uid: remoteUid }}
          style={styles.videoView}
          />
          <Text>Remote user uid: {remoteUid}</Text>
        </React.Fragment>
        ) : (
        <Text>{isJoined && !isHost ? 'Wait for a remote user to join' : ''}</Text>
        )}
        <Text style={styles.info}>{message}</Text>
      </ScrollView>
      </SafeAreaView>
    );

    // Show message
    function showMessage(msg: string) {
      console.info(msg);
      setMessage(msg);
    }
    };

    // Define user interface style
    const styles = StyleSheet.create({
      button: {
        paddingHorizontal: 25,
        paddingVertical: 4,
        fontWeight: 'bold',
        color: '#ffffff',
        backgroundColor: '#0055cc',
        margin: 5,
      },
      main: { flex: 1, alignItems: 'center' },
      scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' },
      scrollContainer: { alignItems: 'center' },
      videoView: { width: '90%', height: 200 },
      btnContainer: { flexDirection: 'row', justifyContent: 'center' },
      head: { fontSize: 20 },
      info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' },
    });

    const getPermission = async () => {
      if (Platform.OS === 'android') {
        await PermissionsAndroid.requestMultiple([
        'android.permission.RECORD_AUDIO',
        'android.permission.CAMERA',
        ]);
      }
    };

    export default App;
```

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="info">
  <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.

### API reference

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

    
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
