# 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 [#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 [#prerequisites-7]

    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 [#implement-basic-authentication-7]

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

    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 [#reference-7]

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

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

    
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
