How to Create Registration & Authentication with Express & PassportJS
Tags: javascript, node, express, passportjs
Published: 2021-01-01 01:11:08

---
canonical_url: https://kelvinmwinuka.com/how-to-create-registration-authentication-with-express-passportjs/
---

In this article, I'm going to demonstrate how to build a user registration and authentication system in ExpressJS. In the previous article, we [set up a MongoDB connection using Mongoose](https://kelvinmwinuka.com/how-to-set-up-mongoose-with-expressjs/). Here we'll be using that connection to save user data and use it for authentication.

This project is available on [Github](https://github.com/kelvinmwinuka/express-tutorial). Feel free to clone it if you'd like to follow along.

<!-- wp:paragraph -->
<p>Let's start by setting up the necessary packages and libraries for this portion of the project.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Run the following command to install the necessary package:</p>
<!-- /wp:paragraph -->

```
npm install passport passport-local express-session bcrypt connect-mongo express-flash joi
```

<!-- wp:paragraph -->
<p>Here's a breakdown of the packages we've just installed:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li><a href="http://www.passportjs.org/" target="_blank" rel="noreferrer noopener">passport</a> and <a href="http://www.passportjs.org/packages/passport-local/" target="_blank" rel="noreferrer noopener">passport-local</a> - User authentication.</li><li><a href="https://www.npmjs.com/package/express-session" target="_blank" rel="noreferrer noopener">express-session</a> - Sessions in ExpressJS.</li><li><a href="https://www.npmjs.com/package/bcrypt" target="_blank" rel="noreferrer noopener">bcrypt</a> - Password encryption and comparison on authentication.</li><li><a href="https://www.npmjs.com/package/connect-mongo" target="_blank" rel="noreferrer noopener">connect-mongo</a> - Mongo store for express sessions.</li><li><a href="https://www.npmjs.com/package/express-flash" target="_blank" rel="noreferrer noopener">express-flash</a> - Flashing messages for display in the front-end.</li><li><a href="https://joi.dev/api/?v=17.3.0" target="_blank" rel="noreferrer noopener">joi</a> - User input validation.</li></ol>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>Include bootstrap (optional, as long as the form can send post data to the server, it will work).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the <em><strong>base.html</strong></em> file, add the link and script tags for the bootstrap imports. They are imported once and then included in every template that extends the base template.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>At this stage, base.html file should look like this:</p>
<!-- /wp:paragraph -->

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{{ title }}</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link 
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" 
      rel="stylesheet" 
      integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" 
      crossorigin="anonymous">
    {% block styles %}
      {# This block will be replaced by child templates when importing styles #}
    {% endblock %}
  </head>
  <body>
    {% block content %}
      {# This block will be replaced by child templates when adding content to the  #}
    {% endblock %}

    <!-- Bootstrap JavaScript Bundle with Popper -->
    <script 
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" 
      integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" 
      crossorigin="anonymous">
    </script>
    {% block scripts %}
      {# This block will be replaced by child templates when importing scripts #}
    {% endblock %}
  </body>
</html>
```

<!-- wp:heading -->
<h2>Implementation</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Go into the entry point file and require the following packages:</p>
<!-- /wp:paragraph -->

```javascript
const session = require('express-session')
const MongoStore = require('connect-mongo')(session)
const passport = require('passport')
```

Right after the app declaration, add built-in express middleware to parse incoming requests with url-encoded data to process the data that will be received from the forms.

```javascript
var app = express()
app.use(express.urlencoded({extended: true}))
```

Next, set up the session middleware. Make sure to place this code after the mongoose connection as we will use the existing mongoose connection to store the session data. Otherwise, you'll have to create a new connection for this.

```javascript
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  store: new MongoStore({
    mongooseConnection: mongoose.connection,
    collection: 'sessions'
  }),
  cookie: {
    secure: false
  }
}))
```

<!-- wp:paragraph -->
<p>Let's walk through the code above:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>We're adding the session middleware to the app.</li><li>secret - The string used to encrypt the session. Declare this in the .env file or system environment variables.</li><li>resave - Determines whether the session object is saved back into the session store even if it was not modified by the request.</li><li>saveUninitialized - Determines whether a new session should be saved into the store even before it's modified.</li><li>store - The store used to save session data.</li></ol>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3>Update models</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In this section, I'm referring to the user model we created in the previous article. <a href="https://kelvinmwinuka.com/how-to-set-up-mongoose-with-expressjs/" target="_blank" rel="noreferrer noopener">Take a look here</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Now we need to update the user model in order to enable authentication and password hashing upon saving. We're doing this in the model in order to avoid writing the authentication login in multiple places shall we need it.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This logic is unique to this model so it makes sense to have it here. Navigate to the User.js model file we created previously and add the following code right after the first require statement:</p>
<!-- /wp:paragraph -->

```javascript
const bcrypt = require('bcrypt')

const saltRounds = 10
```

After the schema definition, add the following code:

```javascript
userSchema.pre('save', async function(next){
  if (this.isNew) this.password = await bcrypt.hash(this.password, saltRounds)
  next()
})

userSchema.static('userExists', async function({username, email}){
  let user = await this.findOne({ username })
  if (user) return { username: 'This username is already in use' }
  user = await this.findOne({ email })
  if (user) return { email: 'This email address is already in use' }
  return false
})

userSchema.static('authenticate', async function(username, plainTextPassword){
  const user = await this.findOne({ $or: [ {email: username}, {username} ] })
  if (user && await bcrypt.compare(plainTextPassword, user.password)) return user
  return false
})
```

<!-- wp:paragraph -->
<p>There are a few things happening here:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>The first is a pre-save hook. This runs before every document save. We use it to determine if the current document is new (not an update call). If the document is new, hash the password. Always save a hashed password rather than plain text.</li><li>The second block is a static method that checks if the user exists. We will query the database by username and then email. If a user is found, return an object specifying which one is already in use. Otherwise, return false.</li><li>The third method is a static method added to the schema. We're using this to authenticate the user. If the user exists and the password comparison between plainTextPassword and the hashed user password passes, return the user object. Otherwise, return false for. failed authentication.</li></ol>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3>Registration</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Create the registration form; a simple form that collects the user's name, username, email address and password.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Place this code in 'register.html' in the views folder.</p>
<!-- /wp:paragraph -->

```html
{% extends 'base.html' %}

{% set title = 'Register' %}

{% block styles %}
  <style>
    form {
      margin-top: 20px;
      margin-left: 20px;
      margin-right: 20px;
    }
  </style>
{% endblock %}

{% block content %}
  <form action="/register" method="POST">
    <div class="mb-3">
      <label for="name" class="form-label">Name</label>
      <input 
        type="text" 
        class="form-control {% if messages.name_error %}is-invalid{% endif %}" 
        id="name" 
        name="name"
        value="{{ messages.name or '' }}"
        placeholder="Full Name">
      <div class="invalid-feedback">{{ messages.name_error }}</div>
    </div>
    <div class="mb-3">
      <label for="username" class="form-label">Username</label>
      <input 
        type="text" 
        class="form-control {% if messages.username_error %}is-invalid{% endif %}" 
        id="username" 
        name="username"
        value="{{ messages.username or '' }}"
        placeholder="Username">
      <div class="invalid-feedback">{{ messages.username_error }}</div>
    </div>
    <div class="mb-3">
      <label for="email" class="form-label">Email address</label>
      <input 
        type="email" 
        class="form-control {% if messages.email_error %}is-invalid{% endif %}" 
        id="email"
        name="email"
        value="{{ messages.email or '' }}"
        placeholder="Email Address">
      <div class="invalid-feedback">{{ messages.email_error }}</div>
    </div>
    <div class="mb-3">
      <label for="password" class="form-label">Password</label>
      <input 
        type="password" 
        class="form-control {% if messages.password_error %}is-invalid{% endif %}" 
        id="password" 
        name="password" 
        value="{{ messages.password or '' }}"
        placeholder="Password">
      <div class="invalid-feedback">{{ messages.password_error }}</div>
    </div>
    <div>
      <button type="submit" class="btn btn-primary">Sign me up!</button>
    </div>
  </form>
{% endblock %}

{% block scripts %}
{% endblock %}
```

<!-- wp:paragraph -->
<p>We're using nunjucks to implement some dynamic behaviour.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The first is adding the is-invalid class to the form controls using flashed messages from the server. This adds an error message attached to the form control.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The second is setting the previous value entered by the user (an optional UX feature for the purposes of this tutorial).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>After creating the register template, create the routes associated with the template.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Create a folder named 'routes' in the root of the project. This folder will hold all of our routes. Create a file 'register.js' in this folder. The contents of this file should be as follows:</p>
<!-- /wp:paragraph -->

```javascript
var router = require('express').Router()
const Joi = require('joi')
const { User } = require('../models')

const validateRegistrationInfo = async (req, res, next) => {
  for(let [key, value] of Object.entries(req.body)) {
    req.flash(`${key}`, value)
  }
  /* Validate the request parameters.
  If they are valid, continue with the request.
  Otherwise, flash the error and redirect to registration form. */
  const schema = Joi.object({
    name: Joi.string().required(),
    username: Joi.string().alphanum().min(6).max(12).required(),
    email: Joi.string()
        .email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(),
    password: Joi.string().min(8).required()
  })

  const error = schema.validate(req.body, { abortEarly: false }).error
  if (error) {
    error.details.forEach(currentError => {
      req.flash(`${currentError.context.label}_error`, currentError.message)
    })
    return res.redirect('/register')
  }

  /** Check if user exists */
  const userExists = await User.userExists(req.body)
  if (userExists) {
    for(let [key, message] of Object.entries(userExists)) {
      req.flash(`${key}`, message)
    }
    return res.redirect('/register')
  }

  next()  
}

router.get('/register', (req, res) => res.render('register.html'))

router.post('/register', validateRegistrationInfo, async (req, res) => {
  let savedUser = await (new User(req.body)).save()
  res.redirect('/')
})

module.exports = router
```

<!-- wp:paragraph -->
<p>The first significant block of code is a function called <em><strong>validateRegistrationInfo</strong></em>. This is middleware that will be used to validate the user's registration information.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the first phase of the validation, we immediately flash the current information for pre-population in case we redirect back to the registration page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Phase 2 is validating each entry against a validation schema. The <a href="https://joi.dev/" target="_blank" rel="noreferrer noopener">Joi</a> package makes this process easy.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If there are any errors on validation, flash each error message for that particular entry before redirecting to the register page. Display this error message in the template.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The final phase of validation is checking whether the username/email provided are already in use. If they are, flash the error message before redirecting to the register route.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Create a GET route that simply renders 'register.html'. This is the route we redirect to when validation fails.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Create a post route that receives the data entered by the user in the request body passing the validation middleware to it. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the route handler itself, we don't have to worry about invalid data as it would have passed all the validation checks if the handler is being executed.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Create a new user using the provided data, save it, and redirect to the home page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Export this router object and import it in the entry file as follows:</p>
<!-- /wp:paragraph -->

```javascript
// Import rotues
app.use('/', require('./routes/register'))
```

<!-- wp:heading {"level":3} -->
<h3>Authentication</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Now that we've taken care of the registration, it's time to implement the authentication logic of our application.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Start by creating a login form. This form has a username/email field and a password field. We'll also include a condition that checks for an error message to display in an alert. This will be displayed when we redirect to the login page after flashing a message.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Place this form in a 'login.html' template file in the views folder alongside the register template.</p>
<!-- /wp:paragraph -->

```html
{% extends 'base.html' %}

{% set title = 'Login' %}

{% block styles %}
  <style>
    form {
      margin-top: 20px;
      margin-left: 20px;
      margin-right: 20px;
    }
  </style>
{% endblock %}

{% block content %}
  <form action="/login" method="POST">
    {% if messages.error %}
      <div class="alert alert-danger" role="alert">{{ messages.error }}</div>
    {% endif %}
    <div class="mb-3">
      <label for="name" class="form-label">Username or Email</label>
      <input 
        type="text" 
        class="form-control {% if messages.name_error %}is-invalid{% endif %}" 
        id="username" 
        name="username"
        value="{{ messages.name or '' }}">
      <div class="invalid-feedback">{{ messages.name_error }}</div>
    </div>
    <div class="mb-3">
      <label for="name" class="form-label">Password</label>
      <input 
        type="password" 
        class="form-control {% if messages.name_error %}is-invalid{% endif %}" 
        id="password" 
        name="password"
        value="{{ messages.name or '' }}">
      <div class="invalid-feedback">{{ messages.name_error }}</div>
    </div>
    <div>
      <button type="submit" class="btn btn-primary">Login</button>
    </div>
  </form>
{% endblock %}

{% block scripts %}
{% endblock %}
```

<!-- wp:paragraph -->
<p>The next task is to define the passport strategy used to authenticate the user. We're using the strategy from passport-local because we're authenticating against our own stored user credentials.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Create a new file in the root of the project called 'passport-helper.js' with the following contents:</p>
<!-- /wp:paragraph -->

```javascript
const LocalStrategy = require('passport-local').Strategy
const { User } = require('./models')

module.exports = (app, passport) => {

  passport.use(new LocalStrategy((username, password, done) => {
    User.authenticate(username, password)
    .then( user => {
      done(null, user)
    })
    .catch( error => {
      done(error)
    })
  }))

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

  passport.deserializeUser((id, done) => {
    User.findById(id, (error, user) => {
      if (error) return done(error)
      done(null, user)
    })
  })

  app.use(passport.initialize())
  app.use(passport.session())
}
```

<!-- wp:paragraph -->
<p>The first step is importing the Strategy and the User model.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The second step is configuring the strategy. We create a new instance of the strategy passing it a function that takes username, password and a verify callback (done) function that is executed after the authentication process is complete.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The authentication logic is placed inside this function. In order to keep this clean, we will simply use the 'authenticate' static method we created in the user model.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>When authenticating in passport, a user object is passed to the verify callback upon successful authentication, otherwise false is returned (provided there's no error thrown in which case, pass the error).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Our authenticate method returns a user object if the user is found and false otherwise so its output is perfect for this scenario.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Once we've configured the strategy, we have to specify the user serialization and deserialization logic.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This step is optional if you're not using sessions, but we're trying to create a login system with sessions so in our case, it's necessary.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The serializeUser method takes a function with a user object and a callback as parameters which determines the data that will be stored in the session itself. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To keep the data stored in the session small, we store only the user ID in the session. This serialization process happens on initial login.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The deserializeUser method takes a function that receives the user ID and a callback. This method runs on all subsequent requests after login/serialization. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The user ID is grabbed from the session and the user is retrieved from the database. Once the user is retrieved, they are stored in req.user.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>After serialization/deserialization, make sure to add passport initialize and session middleware to the app. We'll wrap all of this up in a function that takes our app and passport objects as parameters.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Our passport configuration is now complete. The next step is to initialize passport.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the application entry file, import the function we created in the previous step and then execute it, passing the app and passport objects.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Make sure to have the require statement after the passport require statement. The initialization function must be called after session middleware is defined because the passport session middleware utilises it.</p>
<!-- /wp:paragraph -->

```javascript
const initializePassport = require('./passport-helper')
...
initializePassport(app, passport)
```

Now let's create the login routes. Inside the routes folder, create a file called 'login.js' and add the following code:

```javascript
const createLoginRoutes = passport => {
  const router = require('express').Router()

  router.get('/login', (req, res) => {
    if (req.isAuthenticated()) return res.redirect('/')
    res.render('login.html')
  })
  
  router.post(
    '/login',
    passport.authenticate('local', {
      failureRedirect: '/login', 
      successRedirect: '/',
      failureFlash: 'User not found', 
    }),
    (error, req, res, next) => {
      if (error) next(error)
    }
  )

  router.get('/logout', (req, res) => {
    req.logout()
    res.redirect('/login')
  })
  
  return router
}

module.exports = createLoginRoutes
```

<!-- wp:paragraph -->
<p>Instead of creating routes in the same way we did in the register route file, we're doing it a bit differently here.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Since we're going to need the passport object, we will instead export a function that accepts a passport object as a parameter, defines the routes and returns the router object.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The first route is a GET route for '/login'. This renders the form when there is no active session. Use the 'isAuthenticated' method provided by passport in the request object in order to determine whether there is currently an active session.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The second route is a POST route from '/login'. This route accepts the form input from the user.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Pass the passport.authenticate middleware to this route to handle the authentication. This middleware accepts the strategy type and an options object.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the options object, specify the redirect path in case of failure and in case of success. The failureFlash property specifies the message to flash in case of authentication failure. This is the message you should check for and display on the login page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Finally, create a logout route that calls req.logout to end the current user's session. This logout method is also provided by passport.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Now import the login route creator in the entry file and pass the passport object to it: </p>
<!-- /wp:paragraph -->

```javascript
app.use('/', require('./routes/auth')(passport))
```

Update the home page route to the following:

```javascript
app.get('/', async (req, res) => {
  if (!req.isAuthenticated()) return res.redirect('/login')
  res.render('home.html')
})
```

<!-- wp:paragraph -->
<p>The home page route is now a protected route. This means it should only be accessed by an authenticated user. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We achieve this by using the req.isAuthenticated method to make sure the user is authenticated. If not, redirect to the login page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Go back to the register route file and update the GET route. to the following:</p>
<!-- /wp:paragraph -->

```javascript
router.get('/register', (req, res) => {
  if (req.isAuthenticated()) return res.redirect('/')
  res.render('register.html')
})
```

<!-- wp:heading -->
<h2>Conclusion</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In this article, I demonstrated how to create a simple registration/authentication system in ExpressJS using PassportJS. However, an authentication system is not complete without a password reset feature. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The next article will be a tutorial on <a href="https://kelvinmwinuka.com/how-to-handle-password-reset-in-expressjs/" target="_blank" rel="noreferrer noopener">creating a password reset feature using mongoose and NodeMailer.</a></p>
<!-- /wp:paragraph -->

The post [How to Create Registration & Authentication with Express & PassportJS](https://kelvinmwinuka.com/how-to-create-registration-authentication-with-express-passportjs/) appeared first on [Kelvin Mwinuka](https://kelvinmwinuka.com).

If you enjoyed this article, consider following my [website](https://kelvinmwinuka.com) for early access to my content before it gets published here (don’t worry, it’s still free with no annoying pop-up ads!). Also, feel free to comment on this post. I’d love to hear your thoughts!