# Use tokens (/en/realtime-media/voice/build/set-up-token-authentication/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 Voice Calling:

**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)

    
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
