5-how-to-secure-a-rest-api-with-oauth-20-and-jwt-in-expressjs.html

How to Secure a REST API with OAuth 2.0 and JWT in Express.js

In the digital age, securing APIs is paramount to protecting sensitive information and ensuring that only authorized users can access your resources. One of the most effective ways to secure a REST API is by implementing OAuth 2.0 and JSON Web Tokens (JWT). In this article, we'll explore how to set up a secure REST API using these technologies in an Express.js application, complete with code examples and actionable insights.

What is OAuth 2.0?

OAuth 2.0 is an authorization framework that allows third-party applications to obtain limited access to a web service on behalf of a user. It enables applications to securely interact with APIs without sharing user credentials. OAuth 2.0 works with tokens, which are issued to users and can be used to authenticate API requests.

Key Components of OAuth 2.0:

  • Authorization Server: Issues access tokens to clients after authenticating users.
  • Resource Server: Hosts the API and protects resources using access tokens.
  • Client: The application requesting access to the resource server on behalf of the user.
  • Resource Owner: The user who grants access to their resources.

What is JWT?

JSON Web Tokens (JWT) are an open standard used to securely transmit information between parties as a JSON object. They are compact, URL-safe, and can be verified and trusted because they are digitally signed. JWTs can be used for authentication and information exchange.

Structure of a JWT:

A JWT consists of three parts: 1. Header: Contains metadata about the token, including the algorithm used for signing. 2. Payload: Contains the claims, which are the statements about an entity (typically, the user) and additional data. 3. Signature: Created by signing the encoded header and payload with a secret key, ensuring the token's integrity.

Setting Up Your Express.js Environment

Before we dive into securing our API, let’s set up a simple Express.js application.

Step 1: Initialize Your Project

Open your terminal and create a new directory for your project:

mkdir express-oauth-jwt
cd express-oauth-jwt
npm init -y

Step 2: Install Required Packages

Install Express.js, JSON Web Token, and other necessary packages:

npm install express jsonwebtoken bcryptjs body-parser dotenv

Step 3: Create Basic Server Structure

Create a new file named server.js and set up a simple Express server:

const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');

dotenv.config();

const app = express();
app.use(bodyParser.json());

const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.send('Welcome to the Secure API!');
});

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Implementing OAuth 2.0 and JWT

Step 4: User Registration and Authentication

To implement OAuth 2.0, we need to allow users to register and log in. Let’s create a basic user model and implement registration and login endpoints.

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

let users = []; // This will act as our user database for this example

// User Registration
app.post('/register', (req, res) => {
    const { username, password } = req.body;
    const hashedPassword = bcrypt.hashSync(password, 8);
    users.push({ username, password: hashedPassword });
    res.status(201).send({ message: 'User registered successfully!' });
});

// User Login
app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const user = users.find(u => u.username === username);

    if (!user || !bcrypt.compareSync(password, user.password)) {
        return res.status(401).send({ accessToken: null, message: 'Invalid credentials!' });
    }

    const token = jwt.sign({ username: user.username }, process.env.JWT_SECRET, { expiresIn: '1h' });
    res.status(200).send({ accessToken: token });
});

Step 5: Protecting Routes with Middleware

To secure our API endpoints, we’ll create middleware that verifies the JWT in the request headers.

const verifyToken = (req, res, next) => {
    const token = req.headers['x-access-token'];
    if (!token) return res.status(403).send({ message: 'No token provided!' });

    jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
        if (err) return res.status(401).send({ message: 'Unauthorized!' });
        req.userId = decoded.username;
        next();
    });
};

// Protected Route
app.get('/protected', verifyToken, (req, res) => {
    res.status(200).send(`Hello ${req.userId}, you have accessed a protected route!`);
});

Step 6: Testing the API

You can test the API using Postman or any other API testing tool.

  1. Register a User: Send a POST request to /register with a JSON body containing username and password.
  2. Login: Send a POST request to /login with the same credentials. You will receive an access token.
  3. Access Protected Route: Send a GET request to /protected with the token in the x-access-token header.

Conclusion

Securing a REST API with OAuth 2.0 and JWT in Express.js is a straightforward process that enhances the security of your application. By implementing user authentication and token-based authorization, you can ensure that your API is well-protected against unauthorized access.

Key Takeaways:

  • OAuth 2.0 allows secure authorization without sharing user credentials.
  • JWTs provide a compact and secure way to transmit information.
  • Implementing middleware in Express.js helps protect sensitive routes.

By following this guide, you can build a robust and secure REST API that leverages industry-standard practices for authorization and authentication. Now, take this knowledge, implement it in your applications, and enjoy the peace of mind that comes with a secure API. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.