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]
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
Setting up 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
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"
Go to the Firebase project, navigate to the project settings and copy the configuration object containing keys and identifiers for the app.
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);
}
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",
},
});
That is quite a lot of code!
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()
andhandleConfirmPasswordChange()
functions enables the update of values of the differentTextInput
with theonChangeText
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",
},
});
The SignIn
component is basically the same as the the SignUp
component. It just has a difference of one less TextInput
.
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);
});
};
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";
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);
}
}
};
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);
});
};
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";
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);
}
}
};
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,
},
});
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);
});
};
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");
}}
>
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>;
}
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)