Handling Authentication With Google

Authentication is hard. Let Google Handle it for you.

Handling user data is probably one of the hardest parts to get right of any application (web or not). There are just so many places for something to go wrong that it becomes really hard to ensure that your app is really secure.

Even big companies can fall prey to an insecure authentication system leading to awkward things like data breaches which in turn leads to other nasty stuff like lawsuits which I’m pretty sure you’d like to avoid.

This is why it is essential that you follow the latest best practices when designing an authentication system for your web application. Better yet, you could get someone more experienced to do it for you while you focus on the core features of your app.

Enter Google’s Firebase. Firebase, in this case, is the “someone more experienced” who will handle our authentication for us.

With Firebase, you can leverage the experience of some of the smartest minds in software development to build an authentication system that has been battle tested for years and vetted by the pros in the industry.

Firebase is actually a suite of solutions for common problems developers face when building mobile apps but for the purposes of this article, we will focus on authentication alone.

What you need to follow along

  1. A Google Firebase account
  2. A local development setup
  3. Basic knowledge of React and React Router
  4. A bucket of coffee because why not?

WARNING: We will be using a tiny amount of React Hooks in this article so you should brush up your Hooks skills here and here.

To get a Firebase account, visit firebase.google.com and click on “Get Started”. Note that you have to be logged in to your Google account for this.

If everything goes well, you’ll be redirected to a console. This is where you can create new projects, and manage existing ones so let’s create a new project. Once you log in to the Firebase console, you will see a big blue button with the text “Add project”. That’s the one you want to click on.

Click on it and complete the form that pops up. Give your project an easily identifiable name and continue. I’ll call mine “Gasoline” and no you can’t ask why.

Once you complete and submit the form, you get redirected to the project view. This is where you can add apps to this project (in my case, “Gasoline”). You have the option to add different kinds of apps but for this tutorial, we will go with a web app.

[caption id="attachment_66092" align="alignnone" width="800"]

firebase page

Click the specified icon[/caption]On clicking on the web app option, you get a bunch of code that you’re supposed to add to your web app. This is just a link to the Firebase CDN and a config object for Firebase. It looks something like this:[caption id="attachment_66093" align="alignnone" width="811"]

programming script

copy the config object[/caption]

You want to copy the config object and keep it somewhere easily accessible because we’ll be needing it very soon.

One more thing to sort out before we get started. We want to enable authentication using email and password in our Firebase console.

On the left sidebar of the project overview page, click on “Develop” and you should see some options appear. Click on “Authentication” and you should see this page:

[caption id="attachment_66094" align="alignnone" width="818"]

menu dashboard

click "sign-in method"[/caption]

Click on “Sign-in method” and you should see a bunch of methods you could use to authenticate your users. We’ll start small and just enable “Email/Password” for now.

Okay! Let’s get started!

What we will build

To get you comfortable with the basics of Firebase, we’ll build a simple React.js app without any extra bells and whistles to distract you.

Our app will be a simple web app with a home page, two forms for signup and login, and a page only accessible by authenticated users.

Setup

NB: All the code written in this article can be found here => Repo.

Clone that repo and you should have all you need to follow along with this article. After cloning the repo, create a new file .env in the root of the project folder.

Remember those credentials Google gave us when we created our project? We’ll need them now. Update the .env file to look exactly like this:

FIREBASE_API_KEY = <replace_with_yours>FIREBASE_AUTH_DOMAIN = <replace_with_yours>FIREBASE_DATABASE_URL = <replace_with_yours>FIREBASE_PROJECT_ID = <replace_with_yours>FIREBASE_STORAGE_BUCKET = <replace_with_yours>FIREBASE_MESSAGING_SENDER_ID = <replace_with_yours>

When you’re done, run npm i and after all the packages are done installing, run npm start . Look ma, no Webpack!

You should see this very underwhelming page open up in a new tab:

login page

If you tried clicking on the “VIP Section” link, you’ll notice that an alert pops up telling you that you can only access this page when logged in. We’ll soon get to how that works but we’ll start with the basics first.

How was it built?

If you haven’t cloned the project repo, please do that now so that you can follow along with the code. I’ll focus only on the parts of the app that uses Firebase so that this doesn’t become yet another React tutorial. God knows we’ve had enough of those.

Open up src/utilities/firebase.js and let’s go through what that file is doing.

import * as firebase from 'firebase/app';import 'firebase/auth';import dotenv from 'dotenv';dotenv.config();const fireBaseConfig = { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, databaseURL: process.env.FIREBASE_DATABASE_URL, projectId: process.env.FIREBASE_PROJECT_ID, storageBucket: process.env.FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,};

export default firebase.initializeApp(fireBaseConfig);

At the top, we’re importing the required packages to get Firebase to work properly. Again, dotenv is just there to help us read our .env file so don’t focus on that part.

Notice that we’re creating a fireBaseConfig object we need for Firebase to work properly. At the end of this file, we’re just initializing Firebase with our config object and exporting it in one go. Pretty simple stuff so far.

We now have Firebase configured and ready to go. It’s literally as simple as that. No fluff.

Signing up new users

Let’s go through the relevant part of src/components/Signup.jsx and how it works.

... omitted for brevity ...const [formData, setFormData] = useState({
email: '',
password: '',
});const handleInputChange = event => setFormData({
...formData,
[event.target.name]: event.target.value,
});const onFormSubmit = async (event) => {
event.preventDefault();
await fBase
.auth()
.createUserWithEmailAndPassword(formData.email, formData.password); return <Redirect to="/protected" />;
};... omitted for brevity ...

So this is the relevant part of the Signup component worth talking about. Going through this code, you’ll notice that we only use fBase (this is the Firebase instance we initialized in firebase.js ) once in the onFormSubmitfunction.

What is going on there anyway? So because we’re using Firebase for authentication, we get a bunch of methods at our disposal (full list here).

NB:One aspect of Firebase that I really like is the very descriptive names of all the methods. You want to strive for that in any project you work on.

There are various methods available to us for creating new users but for the sake of this article, we’ll go with the aptly named createUserWithEmailAndPassword() which does exactly what it says on the box.

So this method takes in two arguments: the new user’s email and password. It then creates the new user for us and stores that user in the free database given to us by Firebase.

Since calling this method is going to result in a Promise, we have to await it and when it completes successfully, we’re going to redirect the user to the protected page.

Try it out yourself. Start up the server by running npm start, visit the Signup page, create a new dummy user, go back to your Firebase console, and refresh the page. You should see a new user there like so:

data query

It’s seriously that easy to create a new user with Firebase. I’m not going to include error handling here because this article will get quite long if I do but just know that Firebase catches errors like weak passwords and invalid emails for you even if you don’t manually set it up.

Try it for yourself and see. Go back to the Signup page, open your browser console, fill in the signup form and use a weak password (try “weak”) and click on submit. You’ll notice that the request will fail and you’ll see an error in your console telling you that your password is weak.

NB: You still want to set up password validation though because Firebase will allow nonsense passwords like “password”.

Bonus points if you can set up error handling with descriptive messages for the user, and a loader animation for when the request is pending.

Login

You saw how easy it was to create a new user using Firebase. Well, signing in that new user is just as simple. Let’s open src/components/Login.jsx and see how it’s done.

... omitted for brevity ...const [formData, setFormData] = useState({
email: '',
password: '',
});const handleInputChange = event => setFormData({
...formData,
[event.target.name]: event.target.value,
});const onFormSubmit = async (event) => {
event.preventDefault();
await fBase
.auth()
.signInWithEmailAndPassword(formData.email, formData.password); return <Redirect to="/protected" />;
};... omitted for brevity ...

The relevant parts of this component are almost identical to the Signup component. There is only one difference.

Where we used fBase.auth().createUserWithEmailAndPassword to sign the user up, we are using fBase.auth().signInWithEmailAndPassword to log the user in. Seriously that’s the only difference.

Again, validation is already handled for us in the background. Try signing in the user we just created but use a wrong password and check your console. You should see a console error telling you that the password is invalid.

You can set up error handling and display this error to the user in less than 5 minutes. All the problems of safely storing passwords, issuing tokens, deciding on how to store the tokens, etc. have all been taken care of for us.

Checking if a user is logged in

We’ve seen how easy it is to create a new user, and how to sign users in. How do we check if a user is currently signed in or not? You might have guessed it by now: Call an fBase.auth() method. Let’s open src/Root.jsx and check how it’s done.

... omitted for brevity ...constructor(props) {
super(props);
this.state = { user: null };
this.history = createBrowserHistory();
}componentDidMount() {
fBase.auth().onAuthStateChanged((auth) => {
if (auth) {
this.setState({ user: auth.providerData[0] });
}
});
}... omitted for brevity ...

So this is the root component through which all other components get rendered. When this component mounts, we’re going to call yet another fBase.auth() method to help us check if a user is currently signed in.

If a user is currently signed in, auth will contain the details of that user such as the displayName, email, method of authentication, etc. If not, auth will be null .

It’s as simple as that. You don’t have to manually check sessionStorage, localStorage, or even fiddle around with Cookies (yuck!) to assert that a user is currently signed in.

Now how did we get “VIP Section” to only render when a user is logged in? Well since we can now check if a user is logged in and retrieve the details of that user, I decided to use a render method to conditionally render the protected component. Check here for more details.

The basic gist is that we check if this.state.user !== null and if it is, then we know that a user is currently signed in and we can allow the user to access the page. Here’s the code for that:

... omitted for brevity ...function PrivateRoute({ component: Component, user, ...rest }) {
if (!user) {
window.alert('You must be signed in to access that page');
return <Redirect to="/" />;
} return (<Route {...rest} render={props =><Component {...props} />} />);
}... this is where I export this function ...

And this is how we use it in the Router Switch component in src/Root.jsx:

<PrivateRoute path="/protected" component={Protected} user={user} />

Now there are other (and I’m sure, much better) ways to do this but you should get the gist by now. Call fBase.auth().onAuthStateChanged and if it returns anything other than null, then a user is currently signed in. Easy peasy.

Logout

You should know by now what I’m about to show you haha. To log out a user, all you have to do is call fBase.auth().signOut() wherever you want and the currently logged in user is automatically signed out.

Conclusions

So I wanted to clarify something before ending this article (btw if you followed it through to the end, you’re awesome and I love you). You DON’Thave to use Firebase to get a secure authentication system.

You can create a reasonably secure auth system on your own if you follow the current (they change all the time) best practices regarding authentication.

The reason I encourage people to use Firebase, however, is that it has been tested rigorously over the years and there is a far less chance of your users' data getting breached than if you went solo. You really don’t want those lawsuits.

This article BARELY even scratched the surface of what Firebase can do. I mean BARELY. If you want to really (and I mean really) take advantage of the full capabilities of Firebase, then you should check out Jeff Delaney's courses on Firebase.

DISCLAIMER: I don’t know Jeff personally and I’m not getting any commissions by recommending his courses. I only recommend him because he has a way of explaining things that makes everything a lot simpler. Go check him out. Seriously.If you have any questions, have a better approach to all these, or found bugs in my ugly code, or just want to say hi, please let me know by commenting.

Related posts

The latest articles from Andela.

Visit our blog

Customer-obsessed? 4 Steps to improve your culture

If you get your team's culture right, you can create processes that will allow you to operationalize useful new technologies. Check out our 4 steps to transform your company culture.

How to Build a RAG-Powered LLM Chat App with ChromaDB and Python

Harness the power of retrieval augmented generation (RAG) and large language models (LLMs) to create a generative AI app. Andela community member Oladimeji Sowole explains how.

Navigating the future of work with generative AI and stellar UX design

In this Writer's Room blog, Carlos Tay discusses why ethical AI and user-centric design are essential in shaping a future where technology amplifies human potential.

We have a 96%+
talent match success rate.

The Andela Talent Operating Platform provides transparency to talent profiles and assessment before hiring. AI-driven algorithms match the right talent for the job.