In this article I would like to demonstrate a simple way by which you could implement authentication in an Next.js app using firebase authentication.

The reason for writing this post is that there are many gotcha’s when it comes to using firebase with Next.js. I will try to enumerate them in the best way that I can in this article.

We will start with scaffolding a simple Next.js application using the command line tool create-next-app .

npx create-next-app myapp

Once the boilerplate code is created keep in mind this might take a few seconds we will test if everything went fine using the command.

npm run dev

If you see this you are doing fine 👍.

Open the project directory in your favorite editor( VS code 😄) and go to the pages directory.

Create a new file in directory called dashboard.js and add the following code.

// pages/dashboard.js import React from 'react'; import Nav from '../components/nav'; class Dashboard extends React.Component { render() { return ( <div> <Nav /> <h1>Dashboard Page</h1> <p>You can't go into this page if you are not authenticated.</p> </div> ) } } export default Dashboard;

Replace the contents of the index.js with the following code.

import React from 'react' import Link from 'next/link' import Head from '../components/head' import Nav from '../components/nav' import { auth, firebase } from '../src/firebase' class Home extends React.Component { render() { return ( <div> <Head title="Home" /> <Nav /> <div className="hero"> <h1 className="title">Welcome to Firebase Authentication in Next.js!</h1> <p className="description"> Click on the Dashboard link to visit the dashboard page. </p> <div className="row"> <Link href="/dashboard"> <a className="card"> <h3>Go to Dashboard→</h3> <p>Visit Dashboard</p> </a> </Link> </div> </div> <style jsx>{` .hero { width: 100%; color: #333; } .title { margin: 0; width: 100%; padding-top: 80px; line-height: 1.15; font-size: 48px; } .title, .description { text-align: center; } .row { max-width: 880px; margin: 80px auto 40px; display: flex; flex-direction: row; justify-content: space-around; } .card { padding: 18px 18px 24px; width: 220px; text-align: left; text-decoration: none; color: #434343; border: 1px solid #9b9b9b; } .card:hover { border-color: #067df7; } .card h3 { margin: 0; color: #067df7; font-size: 18px; } .card p { margin: 0; padding: 12px 0 0; font-size: 13px; color: #333; } `}</style> </div> ) } } export default Home

As of right now you can visit the dashboard page without any issue by clicking the link available in the index page. We will change that shortly.

Now, before we create the HOC we should probably wire up firebase. There are many tutorials to show you how to wire up firebase in a web app.Their documentation provides a detailed explanation.

Firebase Documentation

However in this tutorial we have to do things in a slightly different way.

First install firebase to your app using the following command

npm i --save firebase

Create two directories in the root directory.

mkdir src/firebase; mkdir src/helpers

We will use firebase directory for our firebase initialization and the helpers directory for our HOC.

Create a index.js file in the firebase directory and create a withAuth.js file in the helpers directory.

Add the following code to firebase/index.js .

// firebase/index.js import firebase from 'firebase/app'; import 'firebase/auth'; const config = { 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 }; if (!firebase.apps.length) { firebase.initializeApp(config); } const auth = firebase.auth(); export { auth, firebase };

One of the gotcha’s you would come across if you were to use firebase with Next.js is the following message.

Firebase App named '[DEFAULT]' already exists.

This is because a new app instance is loaded multiple times. We can overcome said error using the following snippet which checks if the length of the firebase apps exceeds 0 and doesn’t initialize an instance if this is the case just as we have done above.

if (!firebase.apps.length) { firebase.initializeApp(config); }

To use environment variables in Next.js app we use the following technique.

Create a file called next.config.js in the root of the project directory and add the following configuration.

/* next.config.js */ const webpack = require('webpack'); require('dotenv').config(); module.exports = { webpack: config => { const env = Object.keys(process.env).reduce((acc, curr) => { acc[`process.env.${curr}`] = JSON.stringify(process.env[curr]); return acc; }, {}); config.plugins.push(new webpack.DefinePlugin(env)); return config; } };

Don’t forget to install dotenv npm package. You can do this using the following command.

npm i --save dotenv

Create a .env file and add your firebase keys.

FIREBASE_API_KEY=your api key

FIREBASE_AUTH_DOMAIN=your auth domain

FIREBASE_DATABASE_URL=your database url

FIREBASE_PROJECT_ID=your project Id

FIREBASE_STORAGE_BUCKET=your storage bucket

FIREBASE_MESSAGING_SENDER_ID=your messaging sender id

Add the following code to the helpers/withAuth.js file.

import React from 'react'; import router from 'next/router'; import { auth } from '../firebase'; const withAuth = (Component) => { return class extends React.Component { constructor(props) { super(props); this.state = { status: 'LOADING', } } componentDidMount() { auth.onAuthStateChanged(authUser => { console.log(authUser); if(authUser) { this.setState({ status: 'SIGNED_IN' }); }else { router.push('/'); } }); } renderContent() { const { status } = this.state; if(status == 'LOADING') { return <h1>Loading ......</h1>; }else if(status == 'SIGNED_IN') { return <Component { ...this.props } /> } } render() { return ( <React.Fragment> {this.renderContent()} </React.Fragment> ); } }; } export default withAuth;

The HOC performs a basic functionality. It has a state which can be of two values ‘LOADING’ and ‘SIGNED_IN’ . Initially the component is in loading state. In the componentDidMount() life-cycle function we check if the user is authenticated using the auth.onAuthStateChanged() which checks if a user exists. We simple redirect to the root page if such a user doesn’t exist.

Since there is some asynchronicity involved in the process. It is good practice to show some form of loader during this time. For now we will only show a ‘Loading…’ message.

Add the HOC to the dashboard page component.

// Add this at the top of the page

import withAuth from '../src/helpers/withAuth';

Change the export statement to the following.

export default withAuth(Dashboard);

Now run the app and try navigating to the dashboard page. You will find yourself getting redirected to the home page.This is because you’re not authenticated yet.

Make sure you enable Google Sign-in in your firebase console as we will be using it for authentication.

Replace the code in the pages/index.js file with the following

import React from 'react' import Link from 'next/link' import Head from '../components/head' import Nav from '../components/nav' import { auth, firebase } from '../src/firebase' class Home extends React.Component { handleSignIn = () => { var provider = new firebase.auth.GoogleAuthProvider(); provider.addScope('https://www.googleapis.com/auth/contacts.readonly'); auth.signInWithPopup(provider) .then(() => { alert('You are signed In'); }) .catch(err => { alert('OOps something went wrong check your console'); console.log(err); }); } handleLogout = () => { auth.signOut().then(function() { alert('Logout successful'); }).catch(function(error) { alert('OOps something went wrong check your console'); console.log(err); }); } render() { return ( <div> <Head title="Home" /> <Nav /> <div className="hero"> <h1 className="title">Welcome to Firebase Authentication in Next.js!</h1> <p className="description"> Click on the Dashboard link to visit the dashboard page. </p> <div className="row"> <Link href="/dashboard"> <a className="card"> <h3>Go to Dashboard→</h3> <p>Visit Dashboard</p> </a> </Link> <button onClick={this.handleSignIn}>Sign In using google</button> <button onClick={this.handleLogout}>Logout</button> </div> </div> <style jsx>{` .hero { width: 100%; color: #333; } .title { margin: 0; width: 100%; padding-top: 80px; line-height: 1.15; font-size: 48px; } .title, .description { text-align: center; } .row { max-width: 880px; margin: 80px auto 40px; display: flex; flex-direction: row; justify-content: space-around; } .card { padding: 18px 18px 24px; width: 220px; text-align: left; text-decoration: none; color: #434343; border: 1px solid #9b9b9b; } .card:hover { border-color: #067df7; } .card h3 { margin: 0; color: #067df7; font-size: 18px; } .card p { margin: 0; padding: 12px 0 0; font-size: 13px; color: #333; } `}</style> </div> ) } } export default Home

Restart your app and try going to the dashboard page using the dashboard link. If you find yourself getting redirected to the same page.

🎆 🎆 You app is working. Now try signing in using the Sign-in button. You will be prompted with a popup. You can use one of your gmail accounts.

After logging in you should be able to visit the dashboard page.You can logout using the logout button..duh. 😄

That’s it. If something went wrong consider commenting below.

If you like the post don’t forget to clap..

And one more important thing..Don’t forget to subscribe to Pewdiepie.

Peace…. 🙏 ✌️