DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Handling Firebase authentication in a React-Native app
Uzodike Oguejiofor
Uzodike Oguejiofor

Posted on

Handling Firebase authentication in a React-Native app

Introduction

Firebase is a backend service provider that enables the building of software applications. It offers services ranging from authentication, real-time database, machine learning and storage. Over the years, firebase has been widely adopted by many companies around the world as a go-to platform in cases of building out software applications quickly.

In this tutorial, we will explore how to enable a Firebase email and password authentication in a React-Native app. This article will focus solely on implementing the signup, sign in and signout functionalities in a React-Native app.


Prerequisites

Before engaging this article, a good understanding of the following is required :

  • React-Native and Expo
  • Firebase
  • React Navigation
  • Development Environment (Visual Studio Code)

Getting Started

This article follows distinct steps of achieving the goal of enabling Firebase authentication in a React-Native app. These steps include :

  • Setting up a development environment
  • Installing Firebase
  • Setting up Firebase app
  • Integrating Firebase in the React-Native app
  • Building up authentication screens
  • Handling Firebase authentication
  • Observing Firebase user state
  • Conclusion

Setting up a development environment

Create a new React-Native project in a terminal window(command line, Powershell, Git Bash, etc) using expo-cli. Expo initializes a new project for building your React-Native app.

//creates a new React-Native project

expo init [project name]
Enter fullscreen mode Exit fullscreen mode

Installing Firebase

After creating the React-Native app with Expo, install the Firebase SDK by running this command.

yarn add firebase

//If you are using npm, run this instead
npm install firebase
Enter fullscreen mode Exit fullscreen mode

Setting up Firebase project

Adding a new Firebase project
Get started by creating a new firebase project. Click on the card that says Add Project to create a new firebase project for the application

Authentication image

Once the project has been created and registered, enable the email and password authentication method. It is under the same section as phone and anonymous authentication method, but only the email and password authentication method is required for now.


Integrating Firebase in the React-Native app

Firebase is built for rendering backend services, that is why it’s called Backend as a Service(Baas). In the React-Native app created with Expo, create a folder called services in the root and then create a file called Firebase.js in it.

In Firebase.js import firebase as thus :

import firebase from "firebase"
Enter fullscreen mode Exit fullscreen mode

Go to the Firebase project, navigate to the project settings and copy the configuration object containing keys and identifiers for the app.

Firebase Configuration

Paste the copied object directly under the firebase import statement in Firebase.js

Below the firebaseConfig object, add the code below

// Initialize Firebase
if (!firebase.apps.length) {
  firebase.initializeApp(firebaseConfig);
}
Enter fullscreen mode Exit fullscreen mode

This piece of code enables/initializes firebase in our React-Native app.


Building up authentication screens

Create a SignUp.js file and SignIn.js file and a Home.js file. These files handles the beautiful UI of the app.

SignUp.js

import React, { useState } from "react";
import { Pressable, StyleSheet, Text, TextInput, Image, View } from "react-native";
import { Ionicons } from "@expo/vector-icons";

const SignUp = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [visible, setVisibility] = React.useState({ name: "eye-off" });

  //Toggles the eye icon to show the password
  const ToggleVisibilty = () => {
    if (visible.name === "eye") {
      setVisibility({ name: "eye-off" });
    } else {
      setVisibility({ name: "eye" });
    }
  };

  //Handles password visibility when the eye icon is pressed
  const secureTextEntry = () => {
    if (visible.name === "eye") {
      return false;
    } else if (visible.name === "eye-off") {
      return true;
    }
  };

  //Handles email input
  const handleEmailChange = (text) => {
    setEmail(text);
  };

  //Handles password input
  const handlePasswordChange = (text) => {
    setPassword(text);
  };

  //Handles confirm password input
  const handleConfirmPasswordChange = (text) => {
    setConfirmPassword(text);
  };

  //Handles sign up
  const handleSubmit = async () => {
   console.log("User signed up!")
  };

  return (
    <View style={styles.container}>
      <View style={styles.headerContainer}>
        <Text style={{ fontSize: 40, fontFamily: "QuicksandBold", color: "#fff" }}>Create</Text>
        <Image
          source={require("../../assets/images/broxnbg.png")}
          style={{
            height: 30,
            width: 50,
            top: 9,
            marginRight: 5,
            transform: [{ rotate: "-10deg" }],
          }}
        />
        <Text style={{ fontSize: 40, fontFamily: "QuicksandBold", color: "#fff" }}>account</Text>
      </View>
      <View style={styles.form}>
        <TextInput
          style={styles.email}
          defaultValue={email}
          onChangeText={handleEmailChange}
          textContentType="emailAddress"
          placeholder="Email Address"
          placeholderTextColor="grey"
          keyboardType="email-address"
          returnKeyType="next"
        />
        <View style={styles.passwordContainer}>
          <TextInput
            style={styles.password}
            defaultValue={password}
            onChangeText={handlePasswordChange}
            placeholder="Enter Password"
            placeholderTextColor="grey"
            returnKeyType="next"
            secureTextEntry={secureTextEntry()}
            textContentType="password"
            keyboardType="default"
            autoCorrect={false}
          />
          <Ionicons
            name={visible.name}
            size={24}
            color="#1da"
            style={styles.eyeContainer}
            onPress={ToggleVisibilty}
          />
        </View>
        <View style={styles.passwordContainer}>
          <TextInput
            style={styles.password}
            defaultValue={confirmPassword}
            onChangeText={handleConfirmPasswordChange}
            placeholder="Confirm Password"
            placeholderTextColor="grey"
            returnKeyType="go"
            secureTextEntry={secureTextEntry()}
            textContentType="password"
            keyboardType="default"
            autoCorrect={false}
          />
        </View>
        <Pressable
          style={styles.registerContainer}
        >
          <Text style={styles.register}>want to sign in?</Text>
        </Pressable>
        <Pressable style={styles.button} onPress={handleSubmit}>
          <Text style={{ fontFamily: "QuicksandBold", fontSize: 20 }}>SIGN UP</Text>
        </Pressable>
      </View>
    </View>
  );
};

export default SignUp;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    width: "100%",
    height: "100%",
    backgroundColor: "#0C0C1C",
  },
  headerContainer: {
    flexDirection: "row",
    width: "80%",
    height: 50,
    marginBottom: 40,
    top: -20,
  },
  form: {
    width: "80%",
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
    top: -40,
  },
  email: {
    width: "100%",
    height: 60,
    backgroundColor: "#0ff1",
    borderRadius: 5,
    marginBottom: 35,
    padding: 10,
    fontSize: 18,
    fontFamily: "QuicksandBold",
    color: "#fff",
  },
  password: {
    width: "85%",
    height: 60,
    borderRadius: 5,
    marginBottom: 35,
    padding: 10,
    fontSize: 18,
    fontFamily: "QuicksandBold",
    color: "#fff",
  },

  passwordContainer: {
    flexDirection: "row",
    width: "100%",
    height: 60,
    backgroundColor: "#0ff1",
    borderRadius: 5,
    marginBottom: 35,
  },
  eyeContainer: {
    position: "absolute",
    right: 10,
    top: 20,
  },

  button: {
    width: "100%",
    height: 50,
    backgroundColor: "#1da",
    borderRadius: 5,
    justifyContent: "center",
    alignItems: "center",
    top: 30,
    padding: 10,
  },

  register: {
    fontFamily: "QuicksandBold",
    color: "#fff",
    fontSize: 18,
  },
  registerContainer: {
    top: -20,
    flexDirection: "row",
    alignSelf: "flex-end",
  },
});
Enter fullscreen mode Exit fullscreen mode

That is quite a lot of code!

Image description

But it does look good!

The SignUp.js consists of TextInput fields for user email, user password and confirm password.

The SignUp component consists of the following functions :

  • The handleEmailChange(), handlePasswordChange() and handleConfirmPasswordChange() functions enables the update of values of the different TextInput with the onChangeText attribute.

  • The ToggleVisibility() function switches the eye icon depending on the visibility of the password.

  • secureTextEntry() function handles password visibility when the eye icon is pressed.

SignIn.js

import { useNavigation } from "@react-navigation/core";
import React, { useState } from "react";
import { Image, Pressable, StyleSheet, Text, TextInput, View } from "react-native";
import { Ionicons } from "@expo/vector-icons";

const signIn = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [visible, setVisibility] = React.useState({ name: "eye-off" });

  const ToggleVisibilty = () => {
    if (visible.name === "eye") {
      setVisibility({ name: "eye-off" });
    } else {
      setVisibility({ name: "eye" });
    }
  };

  const secureTextEntry = () => {
    if (visible.name === "eye") {
      return false;
    } else if (visible.name === "eye-off") {
      return true;
    }
  };

  const handleEmailChange = (text) => {
    setEmail(text);
  };

  const handlePasswordChange = (text) => {
    setPassword(text);
  };

  const handleSignInClick = async () => {
    await handleSignIn(email, password);
    console.log("Login successful");
  };

  return (
    <View style={styles.container}>
      <View style={styles.headerContainer}>
        <Text
          style={{
            fontSize: 40,
            fontFamily: "QuicksandBold",
            color: "#fff",
          }}
        >
          Sign in
        </Text>
        <Image
          source={require("../../assets/images/broxnbg.png")}
          style={{ height: 30, width: 50, top: 9, transform: [{ rotate: "-10deg" }] }}
        />
      </View>
      <View style={styles.form}>
        <TextInput
          style={styles.email}
          defaultValue={email}
          onChangeText={handleEmailChange}
          textContentType="emailAddress"
          placeholder="Email Address"
          placeholderTextColor="grey"
          returnKeyType="next"
        />
        <View style={styles.passwordContainer}>
          <TextInput
            style={styles.password}
            defaultValue={password}
            onChangeText={handlePasswordChange}
            placeholder="Enter Password"
            placeholderTextColor="grey"
            returnKeyType="go"
            secureTextEntry={secureTextEntry()}
            textContentType="password"
            keyboardType="default"
            autoCorrect={false}
          />
          <Ionicons
            name={visible.name}
            size={24}
            color="#1da"
            style={styles.eyeContainer}
            onPress={ToggleVisibilty}
          />
        </View>
        <Pressable style={styles.forgotContainer}>
          <Text style={styles.forgot}>Forgot Password?</Text>
        </Pressable>
        <Pressable style={styles.button} onPress={handleSignInClick}>
          <Text style={{ fontFamily: "QuicksandBold", fontSize: 20 }}>SIGN IN</Text>
        </Pressable>
        <Pressable
          style={{
            alignItems: "center",
            justifyContent: "center",
            top: "50%",
            height: 30,
          }}
        >
          <Text
            style={{
              alignItems: "center",
              justifyContent: "center",
              fontFamily: "QuicksandBold",
              fontSize: 16,
              color: "white",
            }}
          >
            Do not have an account? Register
          </Text>
        </Pressable>
      </View>
    </View>
  );
};

export default SignIn;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    width: "100%",
    height: "100%",
    backgroundColor: "#0C0C1C",
  },
  headerContainer: {
    flexDirection: "row",
    width: "80%",
    height: 50,
    marginBottom: 40,
    top: -20,
  },
  form: {
    width: "80%",
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
    top: -40,
  },
  email: {
    width: "100%",
    height: 60,
    backgroundColor: "#0ff1",
    borderRadius: 5,
    marginBottom: 35,
    padding: 10,
    fontSize: 18,
    fontFamily: "QuicksandBold",
    color: "#fff",
  },
  password: {
    width: "85%",
    height: 60,
    borderRadius: 5,
    marginBottom: 35,
    padding: 10,
    fontSize: 18,
    fontFamily: "QuicksandBold",
    color: "#fff",
  },

  passwordContainer: {
    flexDirection: "row",
    width: "100%",
    height: 60,
    backgroundColor: "#0ff1",
    borderRadius: 5,
    marginBottom: 35,
  },
  eyeContainer: {
    position: "absolute",
    right: 10,
    top: 20,
  },

  button: {
    width: "100%",
    height: 50,
    backgroundColor: "#1da",
    borderRadius: 5,
    justifyContent: "center",
    alignItems: "center",
    top: 30,
    padding: 10,
  },

  forgot: {
    fontFamily: "QuicksandBold",
    color: "#fff",
    fontSize: 18,
  },

  forgotContainer: {
    top: -20,
    flexDirection: "row",
    alignSelf: "flex-end",
  },
});
Enter fullscreen mode Exit fullscreen mode

The SignIn component is basically the same as the the SignUp component. It just has a difference of one less TextInput.

Image description


Handling Firebase authentication

In Firebase.js just below the firebase initialization snippet, add a function to handle user sign up and another function to handle user sign in.

Handling user sign up

// signup function
export const handleSignUp = async (email, password) => {
  await firebase
    .auth()
    .createUserWithEmailAndPassword(email, password)
    .then(() => {
      console.log(firebase.auth().currentUser);
      const user = firebase.auth().currentUser;
    })
    .catch((error) => {
      console.error(error);
    });
};
Enter fullscreen mode Exit fullscreen mode

This function enables user to create an account with an email and password using createUserwithEmailandPassword method provided by firebase auth API.

handleSignUp takes in two parameters email and password. These parameters takes the value of the current state of TextInput when handleSignUp function is called.

The handleSignUp function is being exported, which means that we need it to be imported into SignUp.js for usage.

In SignUp.js import handleSignup function as thus:

//importing handleSignUp() function
import { handleSignUp } from "../../services/firebase/firebaseConfig";
Enter fullscreen mode Exit fullscreen mode

After importing the handleSignUp function, we call it inside the handleSubmit function.

 //Handles sign up
  const handleSubmit = async () => {
    if (email === "" && password !== confirmPassword && password === "" && confirmPassword === "") {
      console.error("Invalid Credentials");
    } else {
      try {
        await handleSignUp(email, password);
      } catch (error) {
        console.error(error);
      }
    }
  };
Enter fullscreen mode Exit fullscreen mode

This function is called when the submit button is clicked. In the handleSubmit function, it checks if the email, password and confirm password value provided are valid. If any of the TextInput is empty, it returns an error message. If the email provided is already in use, it also returns an error.

Handling user sign in

// sign in function
export const handleSignIn = async (email, password) => {
  await firebase
    .auth()
    .signInWithEmailAndPassword(email, password)
    .then(() => {
      console.log(firebase.auth().currentUser);
    })
    .catch((error) => {
      console.error(error);
    });
};
Enter fullscreen mode Exit fullscreen mode

handleSignIn function enables user to sign in to an already created account with an email and password using SignInwithEmailandPassword method provided by firebase auth API.

The handleSignIn function makes sure that a request is made to the firebase API in order for a user to login to their account.

The handleSignIn function is also being exported, which implies that it needs to be accessed or called it in SignIn.js

In SignIn.js import handleSignIn function as thus:

//importing handleSignUp function
import { handleSignIn } from "../../services/firebase/firebaseConfig";
Enter fullscreen mode Exit fullscreen mode

After importing the handleSignIn function, we call it inside the handleSubmit function.

 //Handles sign  in
  const handleSubmit = async () => {
    if (email === "" || password === "") {
      console.error("Invalid Credentials");
    } else {
      try {
        await handleSignIn(email, password);
      } catch (error) {
        console.error(error);
      }
    }
  };
Enter fullscreen mode Exit fullscreen mode

This function is called when the submit button is pressed. In the handleSubmit function, it checks for email and password value validity. If any of the TextInput is empty, it returns an error message. It also returns an error if the user does not exist in the app.

Profile.js

import React from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { handleSignout } from "../../services/firebase/firebaseConfig";
import firebase from "firebase";

const Profile = () => {
  const navigation = useNavigation();
  return (
    <View style={styles.container}>
      <Text>{firebase.auth().currentUser.email}</Text>

      <Pressable
        style={styles.button}
        onPress={() => {
         console.log('sign out')
        }}
      >
        <Text>sign out</Text>
      </Pressable>
    </View>
  );
};

export default Profile;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
  button: {
    position: "absolute",
    top: 25,
    backgroundColor: "#11DDAA",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 15,
    height: 55,
    width: 55,
  },
});
Enter fullscreen mode Exit fullscreen mode

This is shown when a user is authenticated. In the code above, CurrentUser is being used to get the currently signed in or authenticated user. In this case, it displays the email of the current user.

The user can decide to sign out at any point and their should be a functionality that enables that the user signs out comfortably.

Going back to Firebase.js we add the functionality that enables the user to sign out just below the handleSigIn function.

Handling user sign out

//handles user sign out
export const handleSignout = async () => {
  await firebase
    .auth()
    .signOut()
    .then(() => {
      console.log("Signed Out");
    })
    .catch((error) => {
      console.error(error);
    });
};
Enter fullscreen mode Exit fullscreen mode

handleSignout function enables user to end authenticated sessions.

The handleSignout function is imported into Home.js where it the function call occurs.

The user needs to sign out when the sign out button is pressed. Now, the button is updated to have the handleSignout function in it and also enable the user to navigate back to Login.js

<Pressable
        style={styles.button}
        onPress={() => {
          handleSignout();
          navigation.navigate("SignInScreen");
          console.log("user signed out");
        }}
      >

Enter fullscreen mode Exit fullscreen mode

In Pressable the onPress attribute is updated and the user can click the button to sign out of the application. The user is navigated to the sign in screen.


Observing Firebase user state

The user sessions need to be tracked in order to make the app even more efficient. There needs to be screens and components that need authentication to be accessed. If the user is not authenticated, the login screen is displayed, if the user is authenticated, the Profile screen is displayed.

App.js

import * as React from "react";
import firebase from "firebase";

import Profile from "./Profile"
import SignIn form "./SignIn"

export default function App() {
  const [user, setUser] = React.useState();

  React.useEffect(() => {
    firebase.auth().onAuthStateChanged((user) => {
      setUser(user);
    });
  }, []);

  const User = firebase.auth().currentUser;
  return <View>{User ? <Profile /> : <SignIn />}</View>;
}
Enter fullscreen mode Exit fullscreen mode

The code snippet above is simply trying to explain that when the user is signed in, display the Home screen and when the user is signed out, display the Login screen.

onAuthstateChanged observes for any change in the user state and updates the component.

Conclusion

This article goes through a basic implementation of Firebase authentication. I believe with this, you can integrate Firebase authentication in a React-Native app.

Firebase offers other authentication options like Google, Apple, Facebook and Phone authentications. In this article, we explored only the email and password method of authentication.

To learn more about Firebase and how to use it, visit the Firebase website and you can check out the Firebase documentation.

Top comments (0)

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.