Authentication and Authorization in Node.js with Passport.js

Authentication and authorization are two key concepts for building secure web applications. Authentication ensures that a user is who they claim to be, while authorization determines what an authenticated user is allowed to do. In this module, we will explore how to implement authentication and authorization in Node.js using Passport.js and JSON Web Tokens (JWT).


Table of Contents

  1. Introduction to Authentication and Authorization
  2. Overview of Passport.js
  3. Setting Up Passport.js in Node.js
  4. Local Authentication Strategy
  5. Using JWT for Authentication
  6. Role-based Authorization
  7. Securing Routes with Middleware
  8. Conclusion

1. Introduction to Authentication and Authorization

Authentication

Authentication is the process of verifying the identity of a user. It typically involves validating a username and password, but can also include multi-factor authentication, social login (e.g., Google, Facebook), or biometric verification.

Authorization

Authorization is the process of determining what an authenticated user is allowed to do. For example, an administrator may have permission to access sensitive data, while a regular user may only have access to their own account information.

In this module, we will focus on how to authenticate users and protect routes based on roles using Passport.js and JSON Web Tokens (JWT).


2. Overview of Passport.js

Passport.js is an authentication middleware for Node.js that supports over 500 different authentication strategies, such as local username/password login, OAuth, OpenID, and more. It is designed to be simple to use and easy to integrate with Node.js applications.

Passport is highly extensible and allows developers to define custom authentication strategies, including integrating third-party authentication providers (e.g., Google, Facebook, GitHub).


3. Setting Up Passport.js in Node.js

To get started with Passport.js, first install the required dependencies:

bashCopyEditnpm install passport passport-local express-session bcryptjs --save
  • passport: The main Passport.js package.
  • passport-local: The strategy for local authentication (username and password).
  • express-session: For maintaining user sessions across requests.
  • bcryptjs: A library for hashing passwords securely.

Once the dependencies are installed, set up Passport.js in your Node.js application:

const express = require('express');
const passport = require('passport');
const session = require('express-session');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');

const app = express();

// Session setup
app.use(session({
secret: 'secret-key',
resave: false,
saveUninitialized: true
}));

// Passport setup
app.use(passport.initialize());
app.use(passport.session());

4. Local Authentication Strategy

The LocalStrategy allows us to authenticate users with a username and password. Here, we will define a local authentication strategy where the user’s password is securely compared using bcrypt.

Step 1: Configure LocalStrategy

passport.use(new LocalStrategy(
function(username, password, done) {
// Find user by username (in a real app, you would query the database)
const user = { username: 'admin', password: '$2a$10$...' }; // Example user (hashed password)

// Check if user exists
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}

// Compare passwords
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) return done(err);
if (!isMatch) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));

Step 2: Serialize and Deserialize User

Passport needs to serialize the user into the session and deserialize the user when a request is made. This is how Passport keeps track of the logged-in user:

passport.serializeUser(function(user, done) {
done(null, user.username);
});

passport.deserializeUser(function(username, done) {
// Find user by username (in a real app, you would query the database)
const user = { username: 'admin' }; // Example user
done(null, user);
});

5. Using JWT for Authentication

JWT (JSON Web Token) is a compact, URL-safe means of representing claims between two parties. It is commonly used for user authentication in modern web applications.

Step 1: Install JWT Dependencies

npm install jsonwebtoken --save

Step 2: Generate JWT Token

Once a user is authenticated, we can generate a JWT token to be sent to the client:

const jwt = require('jsonwebtoken');

// Generate JWT token
const token = jwt.sign({ username: user.username }, 'secret-key', { expiresIn: '1h' });

// Send token to client (usually in the response body or headers)
res.json({ token });

Step 3: Verify JWT Token

To protect routes, we need to verify the JWT token on each request:

const verifyToken = (req, res, next) => {
const token = req.headers['authorization'];

if (!token) {
return res.status(403).send('Token is required');
}

jwt.verify(token, 'secret-key', function(err, decoded) {
if (err) {
return res.status(500).send('Failed to authenticate token');
}
req.user = decoded;
next();
});
};

6. Role-based Authorization

Role-based authorization allows us to restrict access to specific routes based on the user’s role. For example, only admins should be able to access certain admin-specific routes.

Step 1: Define Roles in JWT Payload

When generating the JWT token, include the user’s role:

const token = jwt.sign({ username: user.username, role: user.role }, 'secret-key', { expiresIn: '1h' });

Step 2: Create Middleware to Check Roles

You can create middleware to check a user’s role before allowing them to access a route:

const authorizeRole = (role) => {
return (req, res, next) => {
if (req.user.role !== role) {
return res.status(403).send('Permission denied');
}
next();
};
};

Example: Protecting Admin Routes

app.get('/admin', verifyToken, authorizeRole('admin'), (req, res) => {
res.send('Welcome to the Admin Dashboard');
});

7. Securing Routes with Middleware

In addition to using Passport.js or JWT, you can secure routes by using middleware functions that verify whether the user is authenticated and authorized to access a particular resource.

Example: Securing Routes with Passport.js

app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}));

Example: Securing Routes with JWT

app.get('/profile', verifyToken, (req, res) => {
res.send(`Hello ${req.user.username}, welcome to your profile!`);
});

8. Conclusion

In this module, we explored how to implement authentication and authorization in Node.js using Passport.js and JSON Web Tokens (JWT). We covered the basics of setting up Passport.js with the local strategy and how to protect routes using JWTs. Additionally, we introduced role-based authorization to control access to sensitive routes.