DEV Community

Cover image for Implementing OTP Phone Authentication in React Native
OpenReplay Tech Blog
OpenReplay Tech Blog

Posted on • Originally published at blog.openreplay.com

Implementing OTP Phone Authentication in React Native

by Joseph Chege

Phone number-based auth is one of the many authentication strategies you can add to your applications. An OTP (one-time password) SMS verification minimizes spamming and fraud and can be used to verify logins automatically. This article will implement phone authentication using Firebase in a React Native Expo application.

Creating the Firebase App

Firebase provides several authentication providers, one of them being phone number-based authentication. To set this up:

  • Navigate to your Firebase console and Add Project.
  • Provide the application name and Create project.
  • Once the project creation process is done, add a new web application.

Project dashboard

Register the app to Add Firebase to your web app. Next, you will be presented with some guidelines for installing Firebase. Copy the Firebase configurations that use npm. You will use this code later in this guide to initialize a Firebase SDK to React Native.

Firebase configurations

On your console dashboard, navigate to authentication, and Get Started with Firebase Auth by adding your first sign-in method. Select Phone as a native provider, enable it, and Save.

Firebase Phone Number Authentication

Firebase Phone Number Authentication

It's good to note that Phone Authentication requires additional configuration steps. Firebase Authentication signs in a user by sending an SMS message to the user's phone. This guide follows Firebase Phone Number Authentication using JavaScript to set up the correct configuration steps for the Phone provider inside your React Native apps.

Configuring React Native App

Navigate to the directory where you want your project to live and open a terminal that points to this directory. Run the following command to initialize an Expo application:

npx create-expo-app firebase-phone-auth
Enter fullscreen mode Exit fullscreen mode

Once the installation is done, proceed to the newly created directory.

cd firebase-phone-auth
Enter fullscreen mode Exit fullscreen mode

Install the following dependencies:

  • For navigation:
npx expo install @react-navigation/native @react-navigation/stack react-native-safe-area-context react-native-gesture-handler
Enter fullscreen mode Exit fullscreen mode
  • Firebase SDK:
npx expo install firebase
Enter fullscreen mode Exit fullscreen mode
  • For Firebase reCAPTCHA verifier:
npx expo install expo-firebase-recaptcha react-native-webview
Enter fullscreen mode Exit fullscreen mode

On the root directory, change the file app.config.json to app.config.js* and edit it as below:

export default {
"expo": {
    "name": "fbphoneauth",
    "slug": "fbphoneauth",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
    "image": "./assets/splash.png",
    "resizeMode": "contain",
    "backgroundColor": "#ffffff"
    },
    "updates": {
    "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": [
    "**/*"
    ],
    "ios": {
    "supportsTablet": true
    },
    "android": {
    "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
    }
    },
    "web": {
    "favicon": "./assets/favicon.png"
    }
},
"extra" :{
    firebaseApiKey: your_firebase_api_key,
    firebaseAuthDomain: your_firebase_auth_domain,
    firebaseProjectId: your_firebase_project_id,
    firebaseStorageBucket: your_firebase_storage_bucket,
    firebaseMessagingSenderId: your_firebase_messaging_sender_id,
    firebaseAppId: your_firebase_app_id
}
}
Enter fullscreen mode Exit fullscreen mode

In the above section, add Firebase code you copied earlier to the extra section. Ensure these Firebase configurations reflects based on the above-highlighted areas.

To connect to the Firebase SDK using the above configurations, create a config directory in the root folder. Inside the directory, create a firebase.js file:

import config from '../app.config';

const firebaseConfig = {
    apiKey: config.extra.firebaseApiKey,
    authDomain: config.extra.firebaseAuthDomain,
    projectId: config.extra.firebaseProjectId,
    storageBucket: config.extra.firebaseStorageBucket,
    messagingSenderId: config.extra.firebaseMessagingSenderId,
    appId: config.extra.firebaseAppId
};

module.exports = firebaseConfig;
Enter fullscreen mode Exit fullscreen mode

This way, React Native can communicate with your Firebase app.

Creating a Home Screen

Let's create a screen to welcome the user and a Sign in button. On the project root directory, create a screens directory. Inside the directory, create a Home.js to display the home screen as follows:

  • Import the necessary packages:
import {View,Text,StyleSheet,Button} from 'react-native';
Enter fullscreen mode Exit fullscreen mode
  • The render function:
const HomeScreen = ({navigation}) => {
    return (
        <View style={styles.container}>
            <Text style={styles.text}>Hurray!!, we are on the home screen</Text>
            <Button onPress={ () => navigation.navigate("SignIn")} title="Go to Sign In" />
        </View>
    )
}
Enter fullscreen mode Exit fullscreen mode
  • The styles:
const styles = StyleSheet.create({
    text:{
        color: "#aaa"
    },
    container:{
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    }
});
Enter fullscreen mode Exit fullscreen mode
  • Export the render function by default:
export default HomeScreen;
Enter fullscreen mode Exit fullscreen mode

Creating a Phone Sign-in Screen

Inside the screens directory, create SignInScreen.js files for creating the signing-in methods. Inside this file:

  • Import the needed packages:
import {View,Text,StyleSheet,TextInput,Button} from 'react-native';
import {React,useState,useRef} from 'react';
import {getApp,initializeApp} from 'firebase/app';
import {FirebaseRecaptchaVerifierModal,FirebaseRecaptchaBanner} from 'expo-firebase-recaptcha';
import {getAuth,PhoneAuthProvider,signInWithCredential} from 'firebase/auth';
import fbConfig from '../config/firebase';
Enter fullscreen mode Exit fullscreen mode
  • Initialize the Firebase SDK:
try{
    firebase.initializeApp(fbConfig);
}catch(error){
    console.log("Initializing error ",error);
}
Enter fullscreen mode Exit fullscreen mode
  • Initialize an instance for the app and auth:
const app = getApp();
const auth = getAuth(app);

if (!app?.options || Platform.OS === 'web') {
    throw new Error(
    'This example only works on Android or iOS, and requires a valid Firebase config.'
    );
}
Enter fullscreen mode Exit fullscreen mode

In the render function:

  • Define the state variables:
const recaptchaVerifier = useRef(null);

const [phoneNumber,setPhoneNumber] = useState('');
const [verificationId,setVerificationID] = useState('');
const [verificationCode,setVerificationCode] = useState('');

const firebaseConfig = app ? app.options : undefined;
const [info,setInfo] = useState("");
const attemptInvisibleVerification = false;
Enter fullscreen mode Exit fullscreen mode

This sets what we need in this application. First, you need the phoneNumber to carry authentication. A verificationId that sets the OTP code sent by Firebase via SMS and verificationCode that confirms and verifies the received verification code.

It's essential to remember that Firebase necessitates a reCAPTCHA verifier to avoid misuse, such as by making sure the request for a phone number verification comes from one of the authorized domains of your application. When using the Firebase SDK, you need to utilize the recaptchaVerifier object, and the reCAPTCHA will be generated automatically.

This guide will use the FirebaseRecaptchaBanner (a reCAPTCHA widget) that requires user interaction to complete authentication successfully. You can also use an invisible reCAPTCHA to validate the user without requiring user action, in contrast to the reCAPTCHA widget.

  • Define the function for sending the verification code:
const handleSendVerificationCode = async () => {
    try{
        const phoneProvider = new PhoneAuthProvider(auth); // initialize the phone provider.
        const verificationId = await phoneProvider.verifyPhoneNumber(
            phoneNumber,
            recaptchaVerifier.current
        ); // get the verification id
        setVerificationID(verificationId); // set the verification id
        setInfo('Success : Verification code has been sent to your phone'); // If Ok, show message.
    }catch(error){
        setInfo(`Error : ${error.message}`); // show the error
    }
};
Enter fullscreen mode Exit fullscreen mode
  • Define the function for verifying the entered verification code:
 const handleVerifyVerificationCode = async () => {
    try{
        const credential = PhoneAuthProvider.credential(verificationId,verificationCode); // get the credential
        await signInWithCredential(auth,credential); // verify the credential
        setInfo('Success: Phone authentication successful'); // if OK, set the message
        navigation.navigate("Welcome"); // navigate to the welcome screen
    }catch(error){
        setInfo(`Error : ${error.message}`); // show the error.
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Render the phone number input fields and execute the above-created methods:
return (
    <View style={styles.container}>

        <FirebaseRecaptchaVerifierModal 
            ref={recaptchaVerifier}
            firebaseConfig={firebaseConfig}
        />

        {
            info && <Text style={styles.text}>{info}</Text>
        }

        { // show the phone number input field when verification id is not set.
            !verificationId && (
                <View>
                    <Text style={styles.text}>Enter the phone number</Text>

                        <TextInput
                            placeholder='+2547000000'
                            autoFocus
                            autoCompleteType='tel'
                            keyboardType='phone-pad'
                            textContentType='telephoneNumber'
                            onChangeText={ (phoneNumber) => setPhoneNumber(phoneNumber)}
                        />

                        <Button 
                            onPress={ () => handleSendVerificationCode()}
                            title= "Send Verification Code"
                            disabled={!phoneNumber}
                        />
                </View>
            )

        }

        { // if verification id exists show the confirm code input field.
            verificationId && (
                <View>
                    <Text style={styles.text}>Enter the verification code</Text>

                    <TextInput
                        editable={!!verificationId}
                        placeholder= "123456"
                        onChangeText={setVerificationCode}
                    />

                    <Button
                        title= "Confirm Verification Code"
                        disabled={!verificationCode}
                        onPress = {() => handleVerifyVerificationCode()}
                    />
                </View>
            )
        }

        {attemptInvisibleVerification && <FirebaseRecaptchaBanner/>}
    </View>
)
Enter fullscreen mode Exit fullscreen mode
  • Define some styles:
const styles = StyleSheet.create({
text:{
    color: "#aaa"
},
container:{
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
}
})
Enter fullscreen mode Exit fullscreen mode
  • Export the render function:
export default SignInScreen;
Enter fullscreen mode Exit fullscreen mode

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.

OpenReplay

Start enjoying your debugging experience - start using OpenReplay for free.

Creating the Authenticated Screen

If the user confirms the send code and Firebase verifies it, create a screen that will be triggered for successful authentication. Inside the screens directory create Welcome.js files for showing once logged in. Inside this file:

  • Import the necessary packages:
import {View,Text,StyleSheet} from 'react-native';
Enter fullscreen mode Exit fullscreen mode
  • Define the render function:
const WelcomeScreen = () => {
return (
    <View style={styles.container}>
        <Text style={styles.text}>Hurray!!, we are on the welcome screen</Text>
    </View>
)
}
Enter fullscreen mode Exit fullscreen mode
  • Define the styles:
const styles = StyleSheet.create({
text:{
    color: "#aaa"
},
container:{
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
}
})
Enter fullscreen mode Exit fullscreen mode
  • Export the render function:
export default WelcomeScreen;
Enter fullscreen mode Exit fullscreen mode

Connecting up the Created Screens

Now that everything is set, use React navigation to control the application stack. Edit App.js as follows:

  • Import the necessary packages:
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
Enter fullscreen mode Exit fullscreen mode
  • Import the screens:
import Home from './screens/Home';
import SignIn from './screens/SignInScreen';
import Welcome from './screens/Welcome';
Enter fullscreen mode Exit fullscreen mode
  • Define the stack:
const Stack = createStackNavigator();
Enter fullscreen mode Exit fullscreen mode
  • Change the rendered content in the render function:
export default function App() {
    return (
        <NavigationContainer>
        <Stack.Navigator initialRouteName='Home'>
            <Stack.Screen name="Home" component={Home} options={{title:"Welcome"}} />
            <Stack.Screen name="SignIn" component={SignIn} options={{title:"SignInScreen"}} />
            <Stack.Screen name="Welcome" component={Welcome} options={{title:"Welcome"}} />
        </Stack.Navigator>
        </NavigationContainer>
    );
}
Enter fullscreen mode Exit fullscreen mode

Running and Testing the App

Ensure your development is up and running with the following command to start the application:

### for android
npm run 
### for ios
npm run ios
Enter fullscreen mode Exit fullscreen mode

Here is how the application Home screen will be launched on your device:

Home screen

Proceed to GO TO SIGN IN and provide a valid phone number as follows:

Send verification code

To verify your number, click SEND VERIFICATION CODE. This will launch the Firebase ReCAPTCHA banner:

ReCAPTCHA banner

Check that you're not a robot. A verification code will be sent to the provided phone number. Confirm the code.

Firebase verification code

If everything was successful, a logged-in welcome screen should be on your device as such:

Logged-in welcome screen

Conclusion

This guide taught you how you set up Firebase Phone Authentication in a React Native app. I hope you found it helpful.

A TIP FROM THE EDITOR: For another way of doing auth in React Native, check out Google OAuth with Firebase in React Native.

newsletter

Top comments (0)