Authentication and Authorization in Node.js

Authentication and authorization are key components of most modern web applications. Authentication is the process of verifying the identity of a user, while authorization ensures that a user has permission to access certain resources or perform certain actions. In this module, we’ll explore how to implement authentication and authorization in a Node.js application using popular techniques like JSON Web Tokens (JWT) and Passport.js.


Table of Contents

  1. Introduction to Authentication and Authorization
  2. Setting Up Passport.js for Authentication
  3. Using JSON Web Tokens (JWT) for Stateless Authentication
  4. Protecting Routes with JWT Authentication
  5. Implementing Authorization with Role-Based Access Control (RBAC)
  6. Conclusion

1. Introduction to Authentication and Authorization

Before diving into the code, let’s understand the concepts of authentication and authorization.

  • Authentication: The process of verifying who a user is. It typically involves checking credentials like a username and password. If the credentials match, the user is authenticated.
  • Authorization: After authentication, authorization ensures that the user has the necessary permissions to access a resource or perform a certain action. For example, only users with an “admin” role should be allowed to access the admin panel.

In Node.js, you can implement both using various techniques and libraries. The most common strategies are using sessions, cookies, or JSON Web Tokens (JWT).


2. Setting Up Passport.js for Authentication

Passport.js is a popular middleware for authentication in Node.js applications. It supports various authentication strategies, including local authentication (username/password), OAuth, and social logins.

First, install Passport.js and the necessary modules:

npm install passport passport-local express-session

Then, set up Passport in your application:

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

app.use(session({
secret: 'your_secret_key',
resave: false,
saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

Define a simple local strategy:

passport.use(new LocalStrategy((username, password, done) => {
// Replace with your own logic to check credentials
if (username === 'admin' && password === 'password123') {
return done(null, { id: 1, username: 'admin' });
} else {
return done(null, false, { message: 'Invalid credentials' });
}
}));

Define how to serialize and deserialize the user:

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

passport.deserializeUser((id, done) => {
// Replace with your logic to fetch user by id
done(null, { id: 1, username: 'admin' });
});

Now, you can create a login route that authenticates users:

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

3. Using JSON Web Tokens (JWT) for Stateless Authentication

JSON Web Tokens (JWT) are a popular method for implementing stateless authentication. With JWT, the server does not store session information; instead, it creates a signed token that contains user information. The token is then sent with each request, and the server verifies the token to authenticate the user.

To use JWT, install the jsonwebtoken library:

npm install jsonwebtoken

Here’s how to create a JWT after successful login:

const jwt = require('jsonwebtoken');

app.post('/login', (req, res) => {
const user = { id: 1, username: 'admin' }; // Replace with actual user data

const token = jwt.sign(user, 'your_secret_key', { expiresIn: '1h' });
res.json({ token });
});

To protect routes, create a middleware that verifies the JWT:

const jwt = require('jsonwebtoken');

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

if (token) {
jwt.verify(token, 'your_secret_key', (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
}

Now, protect a route by applying the authenticateJWT middleware:

app.get('/dashboard', authenticateJWT, (req, res) => {
res.send('Welcome to your dashboard');
});

4. Protecting Routes with JWT Authentication

JWT authentication works well in single-page applications (SPAs) and APIs because the token is stored in the client (usually in localStorage or a cookie). To access protected resources, the client sends the JWT in the request headers.

Here’s how you can protect a route:

app.get('/profile', authenticateJWT, (req, res) => {
res.json({ message: 'This is your profile', user: req.user });
});

If the user is authenticated with a valid JWT, they will be able to access the route. Otherwise, they will receive a 401 Unauthorized status.


5. Implementing Authorization with Role-Based Access Control (RBAC)

In addition to authentication, authorization is crucial for controlling access to resources. Role-Based Access Control (RBAC) is a method where permissions are assigned to users based on their roles.

For example, an “admin” might have access to all routes, while a “user” can only access their own profile. Here’s how you can implement RBAC:

function authorizeRole(role) {
return (req, res, next) => {
if (req.user.role !== role) {
return res.sendStatus(403); // Forbidden
}
next();
};
}

// Apply role-based authorization to a route
app.get('/admin', authenticateJWT, authorizeRole('admin'), (req, res) => {
res.send('Welcome, admin!');
});

In this example:

  • The authenticateJWT middleware checks if the user is authenticated.
  • The authorizeRole middleware ensures the user has the required role.

6. Conclusion

Authentication and authorization are critical in any modern web application. With Passport.js and JWT, you can easily handle user authentication and secure your routes. By combining authentication with role-based authorization, you can manage user access and protect sensitive data.

In the next module, we’ll dive deeper into “Error Handling and Debugging in Node.js”, focusing on how to handle exceptions, log errors, and improve the reliability of your Node.js applications.