ALL ARTICLES

Implementing User Authentication in a MERN Stack Application: A Comprehensive Guide

The MERN stack, which includes MongoDB, Express.js, React, and Node.js, is a popular set of technologies for building full-stack web applications.…

Qasim Ali 4 August 2024 3 min read
Implementing User Authentication in a MERN Stack Application: A Comprehensive Guide

Introduction to MERN Stack Authentication

The MERN stack, which includes MongoDB, Express.js, React, and Node.js, is a popular set of technologies for building full-stack web applications. One crucial aspect of any web application is user authentication, which allows users to securely log in and access protected resources.

This guide will walk you through setting up authentication in a MERN stack application, covering everything from creating user models in MongoDB to implementing secure authentication mechanisms using JWT (JSON Web Tokens).

Setting Up MongoDB and User Model

MongoDB is a NoSQL database used in the MERN stack. To implement authentication, we first need to define a user model that will store user credentials such as email, password, and JWT tokens.

Here’s an example of how to create a User model with Mongoose in MongoDB:

Creating the User Schema in MongoDB

The first step is to create a user schema that will be used to store user data, including email, password, and JWT tokens.

javascript
const mongoose = require('mongoose');
  const bcrypt = require('bcryptjs');
  
  const userSchema = new mongoose.Schema({
    email: {
      type: String,
      required: true,
      unique: true
    },
    password: {
      type: String,
      required: true
    },
    tokens: [{
      token: {
        type: String,
        required: true
      }
    }]
  });
  
  userSchema.methods.generateAuthToken = async function() {
    const user = this;
    const token = jwt.sign({ _id: user._id.toString() }, 'secretkey');
    user.tokens = user.tokens.concat({ token });
    await user.save();
    return token;
  };
  
  userSchema.pre('save', async function(next) {
    const user = this;
    if (user.isModified('password')) {
      user.password = await bcrypt.hash(user.password, 8);
    }
    next();
  });
  
  const User = mongoose.model('User', userSchema);
  module.exports = User;

User Registration Endpoint

Now, let's create a registration endpoint to allow users to sign up. This will involve hashing the user's password and saving the user data to the database.

javascript
const express = require('express');
  const router = new express.Router();
  const User = require('../models/User');
  
  router.post('/register', async (req, res) => {
    try {
      const { email, password } = req.body;
      const user = new User({ email, password });
      await user.save();
      const token = await user.generateAuthToken();
      res.status(201).send({ user, token });
    } catch (e) {
      res.status(400).send(e);
    }
  });
  
  module.exports = router;

User Login Endpoint

Next, let's create a login endpoint where users can authenticate themselves using their email and password. We will compare the entered password with the one stored in the database and issue a JWT token upon successful login.

javascript
const express = require('express');
  const router = new express.Router();
  const User = require('../models/User');
  
  router.post('/login', async (req, res) => {
    try {
      const { email, password } = req.body;
      const user = await User.findOne({ email });
      if (!user) {
        return res.status(400).send({ error: 'Invalid login credentials' });
      }
      const isMatch = await bcrypt.compare(password, user.password);
      if (!isMatch) {
        return res.status(400).send({ error: 'Invalid login credentials' });
      }
      const token = await user.generateAuthToken();
      res.send({ user, token });
    } catch (e) {
      res.status(500).send(e);
    }
  });
  
  module.exports = router;

Protecting Routes with JWT Authentication

To protect certain routes, we can verify the JWT token on incoming requests. This ensures that only authenticated users can access protected resources.

javascript
const express = require('express');
  const jwt = require('jsonwebtoken');
  const User = require('../models/User');
  
  const auth = async (req, res, next) => {
    try {
      const token = req.header('Authorization').replace('Bearer ', '');
      const decoded = jwt.verify(token, 'secretkey');
      const user = await User.findOne({ _id: decoded._id, 'tokens.token': token });
      if (!user) {
        throw new Error();
      }
      req.token = token;
      req.user = user;
      next();
    } catch (e) {
      res.status(401).send({ error: 'Please authenticate.' });
    }
  };
  
  module.exports = auth;

Written by

Qasim Ali

Share

How can we help you?

Are you ready to push boundaries and explore new frontiers of innovation?

Let's Work Together