How to use PassportJS for authentication in NodeJS
Tags: node, javascript, authentication, beginners
Published: 2023-01-25 06:57:14

---
title: How to use PassportJS for authentication in NodeJS
published: true
description: A guide on the fundamentals in using passportJS for user authentication in expressJS
tags: #nodejs #javascript #authentication #beginners
cover_image: https://images.pexels.com/photos/346798/pexels-photo-346798.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2
# Use a ratio of 100:42 for best results.
# published_at: 2023-01-17 07:53 +0000
---
> **NB:** 
1. Basic knowledge of node.js is needed to follow along with this article
2. You should be on the root path of the project when typing in the commands from the command line snippet 

The majority of the codebases I've worked on over the years have always favoured using JSON web-tokens (JWT) or Authentication-as-a-Service platforms ([Auth0](https://auth0.com), [Okta](https://okta.com) etc) for authentication logic. 
These are indeed excellent choices! however, on smaller projects I find these to always seem to be overkill. Recently I started working on a chrome extension that performs social sign-in using twitter OAuth API and decided to use passport.js to outsource some of the heavy  lifting involved in setting up authentication. Below I walk through the benefits and step-by-step guide in getting started with passport.js for authentication

### The Why
Authentication is a very delicate process in the creation of software and having the right strategy to use for your application can be a chore. In order not to get caught up in a dilemma of the _perfect_ authentication flow to use that would handle millions of users, I like to stick to simpler tools that would serve my end users effectively and only scale when the need arises (and not a moment before). For a frontend heavy application like the chrome extension I'm working on, I found setting up authentication with passport.js to be the more effective option for my use case as I will require very little usage of the backend.

### What is PassportJS
According to the official docs 
> Passport is authentication middleware for node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more.

Let's break this down so we understand it at a more fundamental level. 

**passport is authentication middleware for node.js:** A middleware is just a function that runs in between a request/response cycle and node.js is a runtime environment that enables us run javascript outside the browser.

**can be unobtrusively dropped in to any express-based application:** This means we can use passport in any node.js application in a way that keeps our code lean, clean and easy to maintain.

**support authentication using a username and password, Facebook, Twitter:** passport.js gives you the flexibility to authenticate users with username/password, google, twitter and more. It does this by using `strategies` which are installed as separate packages from the npm directory and there are more than [500 strategies](https://www.passportjs.org/packages/) that can work with passport!

To top it all off this package is `81kB`! For context the [google-auth-library](https://www.npmjs.com/package/google-auth-library) which is the official library for implementing google OAuth has a size of `496kB`. Pretty easy to see why I am using passport.js on this project

### Project Scope and Setup
Without wasting too much time let's jump into the scope of the project.
Today we will be making a simple app that
allows users to login, view the current time of day and logout

First thing's first, let's setup our server. 
(**PS**: You might want to make sure you have [nodejs](https://nodejs.org) installed on your machine first)

Create folder
```bash
mkdir timely # create folder for project
cd timely # navigate into folder
touch index.js # create main entry point into your server
npm init -y # initialize npm
```

Install dependencies
```bash
npm install passport express express-session passport-local ejs # install dependencies
npm install nodemon --save-dev #install peer dependencies
```

Setup User Interface
 
We're going to be using [EJS](https://ejs.co/) as our templating engine so we can dynamically display user content on our website. If you're not quite sure what templating engines are, you can get an idea through on this amazing [article](https://www.educative.io/answers/what-are-template-engines).
 
```bash 
mkdir views # create views folder
cd views # navigate into views folder
touch authenticate.ejs && touch index.ejs # create out home and authentication page
```

Now we will be adding the `public` folder which houses our static assets for the project

```bash 
mkdir public # create public folder 
cd public # navigate into public folder 
touch main.js && touch styles.css
```

I've included the content for `main.js` and `styles.css` which you can copy over [here](https://github.com/arinze19/blog-repository/tree/main/passport_app/public)

Locate the `index.ejs` file and replace it's contents with the following as well

```html
<!-- navigate into timely/views/index.ejs and insert the following -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/styles.css" />
    <script defer src="/main.js"></script>
    <title>Timely | Who needs a rolex anyway 🤷‍♂️</title>
  </head>
  <body>
    <div class="header">
      <h1>Timely 🕰</h1>
    </div>
      <div class="container">
        <h1>Welcome User</h1>
        <div class="content"></div>
      </div>
  </body>
</html>
```

Create a Server 
```javascript
// Navigate into timely/index.js
const express = require('express');
const path = require('node:path');
const app = express();


app.use(express.static(path.join(__dirname, 'public'))); // Require static assets from public folder
app.set('views', path.join(__dirname, 'views')); // Set 'views' directory for any views being rendered res.render()
app.engine('html', require('ejs').renderFile); // Set view engine as EJS
app.set('view engine', 'ejs');

app.get('/', (req, res) => res.render('index'));

app.listen(3000, () => console.log('server is running on port 3000'));
```

Setup scripts in `package.json`
```json
{
  "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1",
   "start": "node index.js",
   "start:dev": "nodemon index.js"
 }
}
```

Run script 
```bash 
npm run start:dev
```

At this point we should get back a large text that reads "Welcome User" along side the current date and time when we visit `http://localhost:3000/`


![Home page of timely passportjs app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6umvt1dlnlu6sg36htdp.png)


Looks great! but we want only logged in users to have access to the time and date. so lets create an authentication page for users that aren't logged in.
Locate the `authentication.ejs` file inside your `views` folder and replace it's content with the following

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/styles.css" />
    <title>Timely | Who needs a rolex anyway 🤷‍♂️</title>
  </head>
  <body>
    <div class="header">
      <h1>Timely 🕰</h1>
    </div>
    <form action="/log-in" method="POST" class="container">
      <h1>please log in</h1>
      <div>
        <label for="username">Username</label>
        <input name="username" placeholder="username" type="text" />
      </div>
      <div>
        <label for="password">Password</label>
        <input name="password" type="password" />
      </div>
      <button>Log In</button>
    </form>
  </body>
</html>
```

Before we write out any more code, lets understand the passport packages we installed and how they fit together 

**passport:** This is the main passport module that helps us attach the information of a current logged in user to the `req.user` object

**passport-local:** Passport.js works in collaboration with strategy packages to perform the authentication flows you need for your application. We will be using `passport-local` for this as we will be authenticating our users with username and password only

**express-session:** This will enable us store and manage sessions of currently logged in users on the server

Now that we have a little idea of how passport works, let's create our passport middleware strategy. This will be the logic behind adding the user information into our `req.user` object. In your root folder create the following directories

```bash
mkdir strategy && cd strategy # create and move into strategy folder
touch local.js # create strategy file 
``` 
>**NB:** On real world projects it's best to [hash](https://dataspace.com/big-data-applications/what-does-it-mean-to-hash-data/) user passwords before storing them, for improved security and also use an actual database for storing user information. Here I will be storing user information in a json file on the system to avoid network calls and keep the sole focus of the article on passport.js.

Inside `local.js` replace it's content with the following

```javascript
const path = require('node:path'); // path module to find absolute paths on the system 
const fs = require('node:fs'); // file system module to manipulate files
const passport = require('passport'); // the main star of the show
const LocalStrategy = require('passport-local'); // the co-protagonist in this sequel 

const dbpath = path.join(__dirname, 'db.json'); // path to our custom in-house json database

passport.serializeUser((user, done) => { 
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  if (!fs.existsSync(dbpath)) { // if db does not exist, create db
    fs.writeFileSync(dbpath, JSON.stringify({ users: [] }));
  }

  const db = JSON.parse(fs.readFileSync(dbpath, { encoding: 'utf-8' }));

  let user = db.users.find((item) => item.id === id);

  if (!user) {
    done(new Error('Failed to deserialize'));
  }

  done(null, user);
});

passport.use(
  new LocalStrategy(async (username, password, done) => {
    if (!fs.existsSync(dbpath)) { // if db.json does not exist yet, we create it
      fs.writeFileSync(dbpath, JSON.stringify({ users: [] }));
    }

    const db = JSON.parse(fs.readFileSync(dbpath, { encoding: 'utf-8' }));

    let user = db.users.find((item) => {
      return item.username === username && item.password === password;
    });

    if (!user) {
      user = {
        id: Math.floor(Math.random() * 1000), // generate random id between numbers 1 - 999
        username,
        password,
      };

      db.users.push(user);
      fs.writeFileSync(dbpath, JSON.stringify(db));
    }

    done(null, user);
  })
);
```

this file will be run each time a request is made to our server. Let's breakdown what each method does exactly

**passport.use**: This is the first point of contact and is responsible for creating our users. It is run once and after successfully authenticating our user, it passes this information unto the next middleware which is the `passport.serializeUser`

**passport.serializeUser**: This creates a session in passport with our key being `user.id`. It's quite important for our `sessionIDs` to be unique as well to avoid conflict

**passport.deserializeUser**: On each request, This method receives our `sessionID` and scans our database to get the user information of the currently logged in user then attaches that information to the `req.user` object

Now That we know how passport works lets finally include it into the project which will enable us identify logged in users and re-route their requests accordingly

```javascript
const express = require('express');
const passport = require('passport');
const session = require('express-session');
const path = require('node:path');
const app = express();
require('./strategy/local'); // passport strategy will listen to every request

app.use(
  session({
    secret: 'SOME SECRET', // secret key to sign our session
    resave: false,
    saveUninitialized: false,
    cookie: {
      maxAge: 1000 * 60 * 60 * 24, // TTL for the session
    },
  })
);
// initialize passport package
app.use(passport.initialize());
// initialize a session with passport that authenticates the sessions from express-session
app.use(passport.session());

app.use(express.urlencoded({ extended: false }));

app.use(express.static(path.join(__dirname, 'public'))); // Require static assets from public folder
app.set('views', path.join(__dirname, 'views')); // Set 'views' directory for any views being rendered res.render()
app.engine('html', require('ejs').renderFile); // Set view engine as EJS
app.set('view engine', 'ejs');

app.get('/', (req, res) => {
  if (!req.user) {
    // if passport does not have record of this user, we redirect them to the authentication page
    res.render('authenticate');
  } else {
    res.render('index', { user: req.user });
  }
});

app.post(
  '/log-in',
  passport.authenticate('local', {
    // on Initial login, passport will redirect to either of these routes
    successRedirect: '/',
    failureRedirect: '/',
  })
);

app.listen(3000, () => console.log('server is running on port 3000'));
```

If we visit `http://localhost:3000/` on our browser again, we are immediately routed to the authentication page. 


![Authentication page using passportjs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2iaxy6g3xe97xka527k9.png)


Upon sign-up we are assigned a session cookie which you can view in our browser devtools console (right click on the page -> inspect -> Application -> cookies -> http://localhost:3000). 
Each time a request is sent to the server, this cookie is sent alongside it and from this cookie, passport can correctly identify if we are logged in or not. 
To further certify this point, you can go ahead and delete the cookie from the devtools console then refresh the page - In an instant you are immediately re-routed to the authentication page. 

Our homepage looks good but let's add a bit of personality to it by displaying the users username with `ejs` and also the ability for users to logout. 

In `index.ejs` make the following changes

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/styles.css" />
    <script defer src="/main.js"></script>
    <title>Timely | Who needs a rolex anyway 🤷‍♂️</title>
  </head>
  <body>
    <div class="header">
      <h1>Timely 🕰</h1>
    </div>
    <form class="container" action="/log-out" method="post">
      <h1>Welcome back, <%= user.username %></h1>
      <div class="content"></div>
      <button>Log out</button>
    </form>
  </body>
</html>
```

Now to handle the logout route on the server we can add the following code just above `app.listen()` method

```javascript
app.post('/log-out', (req, res) => {
  req.logOut((err) => { // clear out information in passport-session and redirects user
    if (err) {
      res.send('something went wrong');
    }

    res.redirect('/');
  });
});

app.use('*', (req, res) => res.redirect('/')); // to catch and redirect all other routes 
```

![Completed home page of timely passportjs app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ermzi4ua15nzdvf792nn.png)


>**NB:** You might have noticed that each time you make changes to server files, you are re-routed to the authentication page. This is because `sessionIDs` are stored in a cache created by `express-session` and once `nodemon` restarts the server, the `express-session` cache is wiped clean hence passport can not recognise us with the previous `sessionID`.

And that's it! Now we can fully log in and out of our application using passport.js. 
I do recommend reading more into their [documentations](https://www.passportjs.org/docs/) to discover new strategies you can use in your future projects. Till next time, Ciao!