1. Preparing the React Native(Expo) Development Environment

Please follow the Expo official documentation to set up your React Native(Expo) development environment.

If you already have the React Native(Expo) development environment set up, you can proceed to the next step.

Download the Git Repository

If you want to download and use the sample project, use the following command to download the git repository.

Download the Furo Expo Sample project.

git clone https://github.com/furo-official/furo-sample-expo

2. Installing and Using Libraries

Use the following command to install the required libraries.

npm install

In this guide, we will additionally use the following libraries.

  • @react-navigation/native, @react-navigation/native-stack

    Used to handle deep linking after login.

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

<NavigationContainer linking={linking}>
  <Stack.Navigator>
    <Stack.Screen name="Home" component={HomeScreen} />
  </Stack.Navigator>
</NavigationContainer>;
  • react-native-inappbrowser-reborn

    Used to open the Furo login page through the InApp browser.

    import { InAppBrowser } from 'react-native-inappbrowser-reborn';
    
    const response = await InAppBrowser.openAuth(url, 'exp://', options);
  • axios

    Used to call Furo’s authentication API.

    import axios from 'axios';
    
    const { data } = await axios.post(
      'https://api.furo.one/sessions/code/authenticate',
      { code },
    );

3. Integrating Furo Login

  1. Add the Client ID value copied from the Furo console to the .env file. If the .env file doesn’t exist, create it and set the environment variable as follows:
EXPO_FURO_CLIENT_ID="{{YOUR_CLIENT_ID}}"
  1. Enter exp:// in the Default Callback URI input field of the Furo console.

4. Full Sample Code

import React, { useCallback, useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button, Linking } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
import axios from 'axios';

const CLIENT_ID = process.env.EXPO_FURO_CLIENT_ID;
const LOGIN_URL = `https://auth.furo.one/login/${CLIENT_ID}`;

const Stack = createNativeStackNavigator();

export default function App() {
  const linking = {
    prefixes: ['exp://'],
    config: {
      screens: {
        Home: '*',
      },
    },
  };

  const Stack = createNativeStackNavigator();

  return (
    <NavigationContainer linking={linking}>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const HomeScreen = () => {
  const [accessToken, setAccessToken] = useState(null);

  const openURLFromInAppBrowserOrLinking = useCallback(async (url) => {
    try {
      if (await InAppBrowser.isAvailable()) {
        const response = await InAppBrowser.openAuth(url, 'exp://', {
          // iOS Properties
          ephemeralWebSession: false,

          // Android Properties
          showTitle: false,
          enableUrlBarHiding: true,
          enableDefaultShare: false,
          showInRecents: true,
          forceCloseOnRedirection: true,
        });

        if (response.type === 'success' && response.url) {
          console.log(response);
          Linking.openURL(response.url);
        }
      } else {
        Linking.openURL(url);
      }
    } catch (error) {
      console.error(error);
    }
  }, []);

  useEffect(() => {
    const handleDeepLink = ({ url }) => {
      const code = url.split('?')[1]?.match(/code=([^&]+)/)?.[1];
      if (code) {
        authenticateWithCode(code)
          .then((response) => {
            const { access_token } = response;
            setAccessToken(access_token);
          })
          .catch((e) => {
            console.log('error: ', e.response.data);
          });
      }
    };

    Linking.addEventListener('url', handleDeepLink);

    return () => {
      Linking.removeAllListeners('url');
    };
  }, []);

  const authenticateWithCode = async (code) => {
    const { data } = await axios.post(
      'https://api.furo.one/sessions/code/authenticate',
      { code },
    );
    return data;
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>[Furo] React Native(Expo) Example</Text>
      <View style={styles.content}>
        {accessToken ? (
          <>
            <Text>{accessToken}</Text>
            <Button title="Clear" onPress={() => setAccessToken(null)} />
          </>
        ) : (
          <Button
            title="Login"
            onPress={() => openURLFromInAppBrowserOrLinking(LOGIN_URL)}
          />
        )}
      </View>
      <StatusBar style="auto" />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    fontSize: 16,
    fontWeight: '600',
  },
  content: {
    marginTop: 10,
    width: '80%',
    alignItems: 'center',
  },
});

Now the Furo login integration is complete in your React Native app.

When you run the app and click the Login button, the Furo login page will open in the InApp browser, and once the login is completed, the accessToken will be displayed on the screen.