DEV Community

Nikollas Betiol
Nikollas Betiol

Posted on

How to handle authentication on Node JS using Firebase 🔥

When we start to build an API with Node, the first thing that comes in mind is Authentication.
Then we think in JWT, hash passwords, define the secret keys of JWT, and this is a little bit boring when we need to build a small service or API.

In this article, we are going to show how this works using Google Sign-in, but the same applies to any of the other login providers that are available on Firebase. For further detail on how to set up them, see this link from Firebase docs

Firebase

Firebase is Google's mobile application development platform that helps you build, improve, and grow your app. Inside Firebase, we got modules like Authentication, Push Notifications, Realtime Database, and more cool things.

What are we'll do

A NodeJS backend that authenticates requests through Firebase.

To begin, we need to create a project on Firebase.

Firebase setup

The initial screen is to create a new Project on Firebase, click at Create Project

Alt Text

Now, we got a screen to give a name to our application, for this example, my project name is firebase-auth-server
Alt Text

Now Firebase is creating our project.
Alt Text

After created, Firebase console will give us a dashboard with several services, please select the Authentication menu.

And then we have a list of providers that Firebase gives us, but for us first, we need to select Authentication by Google.

Alt Text

Great! Firebase can now accept authentication of our frontend by Google.

Backend setup

Now we need to start the project and install the express.

mkdir server
npm init -y
npm install express cors
npm install -D nodemon

After this, we need to create a file called index.js on the root of the project and create the code bellow.

const express = require("express");

const app = express();

app.use("/", (req, res) => {
  res.send("Hello World");
});

app.listen(4000, () => console.log("The server is running at PORT 4000"));

The code above will create a basic express server and this is our start.

After this, we need to create a basic endpoint following the code below:

/**
 * index.js
 */
const express = require("express");
const cors = require("cors");
const authMiddleware = require("./auth-middleware");

const app = express();
app.use(cors());

const books = [
  {
    id: 1,
    name: "Harry Potter",
    image:
      "https://pmpub-catalogue.s3-eu-west-1.amazonaws.com/covers/web/9781781100240.jpg",
  },
  {
    id: 2,
    name: "Clean Code",
    image:
      "https://images-na.ssl-images-amazon.com/images/I/41jEbK-jG+L._SX374_BO1,204,203,200_.jpg",
  },
  {
    id: 3,
    name: "Javascript: The good parts",
    image: "https://images-na.ssl-images-amazon.com/images/I/81kqrwS1nNL.jpg",
  },
];

app.use("/", authMiddleware);

app.get("/books", (request, response) => {
  return response.send({ books });
});

app.listen(4000, () => console.log("The server is running at PORT 4000"));

Now, we need to back at package.json file and add a start script and test our code.

{
  "name": "firebase-auth-server",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Nikollas Betiol",
  "license": "MIT",
  "scripts": {
    "start:dev": "nodemon index.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.3"
  }
}

After, our package.json file should look like the code above,
then we can execute the script to run the application.

npm run start:dev

Let's navigate to http://localhost:4000/books

Now, you must have this.

Alt Text

Great, we have an endpoint on our API that returns a list of books, but you can notice that everybody can access our endpoint and we don't wanna this 🤔

Let's fix this using the firebase-admin, is the library that we will use to integrate with Firebase.

Here we gotta install the firebase-admin

npm install firebase-admin

Lets back at the firebase console and download the credentials. You can follow this step clicking here.

Create and Save the file inside the firebase folder.

Your code should look like this.

/*
  firebase/index.js
*/
const firebase = require("firebase-admin");

const credentials = require("./credentials.json");

firebase.initializeApp({
  credential: firebase.credential.cert(credentials),
  databaseURL: "https://<yourproject>.firebaseio.com",
});

module.exports = firebase;

Now we need to create an auth middleware to filter our requests and authorize or deny requests.

Then, we need to create a file called auth-middleware.js

touch auth-middleware.js

and use the code bellow


/*
    auth-middleware.js
*/
const firebase = require("./firebase/admin");

function authMiddleware(request, response, next) {
  const headerToken = request.headers.authorization;
  if (!headerToken) {
    return response.send({ message: "No token provided" }).status(401);
  }

  if (headerToken && headerToken.split(" ")[0] !== "Bearer") {
    response.send({ message: "Invalid token" }).status(401);
  }

  const token = headerToken.split(" ")[1];
  firebase
    .auth()
    .verifyIdToken(token)
    .then(() => next())
    .catch(() => response.send({ message: "Could not authorize" }).status(403));
}

module.exports = authMiddleware;

After, we can back at the index.js file and add the auth-middleware middleware.

/**
 * index.js
 */
const express = require("express");
const authMiddleware = require("./auth-middleware");

const app = express();

const books = [
  { id: 1, name: "Harry Potter" },
  { id: 2, name: "Clean Code" },
  { id: 3, name: "Javascript: Good practices" },
];

app.use("/", authMiddleware);

app.get("/books", (request, response) => {
  return response.send({ books });
});

app.listen(4000, () => console.log("The server is running at PORT 4000"));

Cool, I think that the backend is ready to receive requests from our frontend!

Frontend

Let's start creating a project using create-react-app
You can find the CSS here

npm install -g create-react-app
create-react-app frontend
cd frontend/
npm install firebase react-router-dom react-router

Now we need to create two files;

touch Login.js
touch BookList.js

In the file Login.js, paste the code below:

/**
 * src/Login.js
 */
import React from "react";

export default function Login() {
  return <h1>Login</h1>;
}

and in the file BookList.js, paste the code:

/**
 * src/BookList.js
 */
import React from "react";

export default function BookList() {
  return <h1>BookList</h1>;
}

We just created two important files in our application, let's configure the App.js to use react-router.

NOTE: THIS IS NOT THE BEST WAY TO CREATE AN AUTHORIZATION FLOW, THIS PROJECT IS JUST AN EXAMPLE

/**
 * src/App.js
 */
import React from "react";
import "./App.css";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Login from "./Login";
import BookList from "./BookList";

export default function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Switch>
          <Route path={"/login"}>
            <Login />
          </Route>
          <Route path={"/book-list"}>
            <BookList />
          </Route>
        </Switch>
      </BrowserRouter>
    </div>
  );
}

Now, you can follow this documentation Setup Web Project Configuration and get the configs.

Alt Text

Alt Text

Alt Text

Let's create a file called firebase.js inside the src folder and paste the code below, with this code we'll create the firebase config.

/**
 * src/firebase.js
 */
import firebase from "firebase/app";
import "firebase/auth";

const firebaseConfig = {
  apiKey: "your apiKey here",
  authDomain: "your authDomain here",
  databaseURL: "your databaseURL here",
  projectId: "your projectId here",
  storageBucket: "your storageBucket here",
  messagingSenderId: "your messagingSenderId here",
  appId: "your appId here",
};

firebase.initializeApp(firebaseConfig);

const auth = firebase.auth();

export { auth, firebase };

Now, we gonna back to the file Login.js and paste this code.
Your code should look like this:

/**
 * src/Login.js
 */
import React from "react";
import { useHistory } from "react-router-dom";
import { auth, firebase } from "./firebase";

export default function Login() {
  const history = useHistory();
  async function googleLogin() {
    //1 - init Google Auth Provider
    const provider = new firebase.auth.GoogleAuthProvider();
    //2 - create the popup signIn
    await auth.signInWithPopup(provider).then(
      async (result) => {
        //3 - pick the result and store the token
        const token = await auth?.currentUser?.getIdToken(true);
        //4 - check if have token in the current user
        if (token) {
          //5 - put the token at localStorage (We'll use this to make requests)
          localStorage.setItem("@token", token);
          //6 - navigate user to the book list
          history.push("/book-list");
        }
      },
      function (error) {
        console.log(error);
      }
    );
  }
  return (
    <div>
      <button onClick={googleLogin} className="login-button">
        GOOGLE
      </button>
    </div>
  );
}

Then back to the terminal and run the application

npm start

Once started React will open a browser window

Navigate to http://localhost:3000/login

You can click in the GOOGLE button

Alt Text

Cool, after login you must be redirected to the booklist.

Back to the BookList.js component and paste the code below

/**
 * src/BookList.js
 */
import React, { useEffect, useState } from "react";

export default function BookList() {
  //create state to store our book list
  const [books, setBooks] = useState([]);

  useEffect(() => {
    async function loadBooks() {
      //fetch the book list
      const request = await fetch("http://localhost:4000/books", {
        //use the authorization
        headers: {
          Authorization: "Bearer " + localStorage.getItem("@token"),
        },
      });

      const allBooks = await request.json();
      //set the book list on state
      setBooks(allBooks.books);
    }
    //invoke the function
    loadBooks();
  }, []);

  return (
    <div className="container">
      <h1>BookList</h1>
      {/* map the book list to show book name and image */}
      {books.map((book) => (
        <div key={book.id} className="booklist">
          <img className="image" alt={book} src={book.image} />
          <h3>{book.name}</h3>
        </div>
      ))}
    </div>
  );
}

No, we can see the booklist \o/

Alt Text

Conclusion

This is enough push to get you started with firebase auth on the server. You can check out more possibilities exploring the Firebase Docs.
I hope this helped you to learn how to create an integration with firebase auth and if you wanna see the source code, check on my Github.
Thank you \o/

Oldest comments (10)

Collapse
 
giacomette profile image
Roberto da Cruz Giacomette

Muito bom, otimo tutorial

Collapse
 
filiplusnia profile image
Filip Luśnia

shouldn't "const firebase = require("./firebase/admin");" be inside authMiddleware function?

Collapse
 
molebatsi profile image
Obakeng Molebatsi

Well written and a great starting point

Collapse
 
lrob profile image
lrob • Edited

Thanks for this tutorial. I created a version of it using docker-compose. You can find it on my Github. In this version I also added the possibility to login using the method based on email and password.

Collapse
 
betiol profile image
Nikollas Betiol

Awesome!

Collapse
 
sarcasticverma profile image
Shivam Verma

I have a doubt -- does the token expire? And if it does how should we handle refreshing it?

Collapse
 
betiol profile image
Nikollas Betiol • Edited

Hey thanks for your comment.
You can take a look at the firebase doc
firebase.google.com/docs/auth/admi...
firebase.google.com/docs/reference...

Collapse
 
gulshanaggarwal profile image
Gulshan Aggarwal

Useful content @betiol. Keep it up♥

Collapse
 
taakie profile image
Taakie

Oi, um Brazileiro aqui!
Eu vi que o seu Google estava em pt kkk

bom trabalho mano, isso mim ajudou bastante!

Collapse
 
betiol profile image
Nikollas Betiol

kkkkk tmj mano!!