Building a Scalable Authentication System with JWT in a MERN Stack Application

RMAG news

Introduction:

Authentication is a fundamental aspect of web development, ensuring that users can securely access protected resources. JSON Web Tokens (JWT) have become a popular choice for implementing authentication in modern web applications due to their simplicity and scalability. In this tutorial, we’ll explore how to implement a scalable authentication system using JWT in a MERN (MongoDB, Express.js, React, Node.js) stack application. We’ll cover user registration, login, token generation, and secure authorization.

Prerequisites:

Before we begin, make sure you have the following prerequisites installed:

Node.js and npm (Node Package Manager)
MongoDB (you can use a local or remote instance)
React.js (if you’re building a frontend)

Setting Up the Backend:

Initialize a New Node.js Project:

Create a new directory for your project and initialize a new Node.js project by running the following commands:

mkdir mern-authentication
cd mern-authentication
npm init -y

Install Required Packages:

Install the necessary packages for our backend using the following command:

npm install express mongoose jsonwebtoken bcryptjs body-parser cors

express: Web framework for Node.js.

mongoose: MongoDB object modeling tool.

jsonwebtoken: Library for generating JWT tokens.

bcryptjs: Library for hashing passwords securely.

body-parser: Middleware for parsing request bodies.

cors: Middleware for enabling Cross-Origin Resource Sharing.

Create a MongoDB Database:

Set up a MongoDB database either locally or using a cloud service like MongoDB Atlas. Note down the connection URI.

Create the Backend Server:

Create a file named server.js and set up a basic Express server with MongoDB connection:

// server.js

const express = require(express);
const mongoose = require(mongoose);
const bodyParser = require(body-parser);
const cors = require(cors);

const app = express();
const PORT = process.env.PORT || 5000;

// Middleware
app.use(bodyParser.json());
app.use(cors());

// Connect to MongoDB
mongoose.connect(mongodb://localhost:27017/mern_auth, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => console.log(MongoDB connected))
.catch(err => console.log(err));

// Routes
app.use(/api/auth, require(./routes/auth));

// Start the server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Create Routes for Authentication:

Create a folder named routes and add a file named auth.js inside it. This file will contain routes for user registration, login, and token verification.

// routes/auth.js

const express = require(express);
const router = express.Router();
const bcrypt = require(bcryptjs);
const jwt = require(jsonwebtoken);
const User = require(../models/User);
const { check, validationResult } = require(express-validator);

// Register a new user
router.post(/register, [
check(name, Please enter a name).not().isEmpty(),
check(email, Please include a valid email).isEmail(),
check(password, Please enter a password with 6 or more characters).isLength({ min: 6 })
], async (req, res) => {
// Validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

const { name, email, password } = req.body;

try {
// Check if user already exists
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ msg: User already exists });
}

// Create new user
user = new User({ name, email, password });

// Hash password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);

// Save user to database
await user.save();

// Generate JWT token
const payload = {
user: { id: user.id }
};
jwt.sign(payload, jwtSecret, { expiresIn: 3600 }, (err, token) => {
if (err) throw err;
res.json({ token });
});

} catch (err) {
console.error(err.message);
res.status(500).send(Server Error);
}
});

// Login route
router.post(/login, [
check(email, Please include a valid email).isEmail(),
check(password, Password is required).exists()
], async (req, res) => {
// Validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

const { email, password } = req.body;

try {
// Check if user exists
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ msg: Invalid credentials });
}

// Compare passwords
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: Invalid credentials });
}

// Generate JWT token
const payload = {
user: { id: user.id }
};
jwt.sign(payload, jwtSecret, { expiresIn: 3600 }, (err, token) => {
if (err) throw err;
res.json({ token });
});

} catch (err) {
console.error(err.message);
res.status(500).send(Server Error);
}
});

module.exports = router;

Create a User Model:

Create a folder named models and add a file named User.js inside it. This file will define the user schema and model.

// models/User.js

const mongoose = require(mongoose);

const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});

module.exports = User = mongoose.model(user, UserSchema);

Conclusion:

In this tutorial, we’ve implemented a scalable authentication system using JSON Web Tokens (JWT) in a MERN stack application. We’ve covered user registration, login, token generation, and secure authorization. This authentication system provides a solid foundation for building secure and scalable web applications. Feel free to extend this implementation by adding features like password reset, email verification, and role-based access control.

Please follow and like us:
Pin Share