English
  • express-js
  • javascript
  • tutorial
  • auth
  • authentication
  • jwt
  • identity
  • api

Protect your Express.js API with JWT and Logto

Learn how to protect your Express.js API endpoints with JSON Web Tokens (JWT) and Logto.

Gao
Gao
Founder

Introduction

When you're developing a web application, it's crucial to protect your API endpoints from unauthorized access. Imagine you're building an online shopping website; you definitely don't want cyber shoplifters exploiting your API.

Assuming you've already built an Express.js application with user authentication, where users must sign in before taking certain actions. If not, you can kickstart your journey with Logto. It requires just a few lines of code to establish a user authentication flow.

However, even after user authentication, you face various choices to protect your API endpoints. Unfortunately, most of these options have their downsides:

  • Session-based authentication: Tying your API to a session store, which isn't scalable and doesn't suit microservices well.
  • Calling an authentication service: This introduces an extra network call, increasing latency and costs. Some authentication services even charge based on API call volume, potentially leading to hefty expenses.

In this tutorial, we'll demonstrate how to fortify your API endpoints using JSON Web Tokens (JWT) and Logto. This approach offers scalability and minimal extra costs.

Prerequisites

Before diving in, ensure you have the following:

  • A Logto account. If you don't have one, you can sign up for free.
  • An Express.js project that needs API protection and a client application that consumes the API.
  • Basic familiarity with JSON Web Token (JWT).

Define your API resource in Logto

Logto takes full advantage of RFC 8707: Resource Indicators for OAuth 2.0 to secure your API endpoints. This means you can define your API resources using their actual URLs.

Navigate to the "API resources" tab in the Logto Console and click "Create API resource" to create a new one. For instance, if you wish to protect the /api/products endpoint, you can use the URL https://yourdomain.com/api/products as the identifier.

Create API resource

Obtain an access token in your client application

To proceed, you'll need to integrate the Logto SDK into your client application. This application might differ from your Express.js backend; for example, you might have a React app using Express.js as the backend API server.

You'll also need to tweak the Logto SDK configuration to inform Logto that you want to request an access token for your API in this grant. Here's an example using React:

Once a user signs in with Logto, isAuthenticated within the Logto SDK will become true:

Now, you can use the getAccessToken method to retrieve an access token for your API:

Lastly, include this access token in the Authorization header when making requests to your API:

Verify the access token in your API

In your Express.js application, install the jose library for JWT verification:

As we're using Bearer authentication, extract the access token from the Authorization header:

Subsequently, create a middleware to verify the access token:

You can now employ this middleware to protect your API endpoints:

With this approach, you don't need to contact the Logto server every time a request arrives. Instead, you fetch the JSON Web Key Set (JWKS) from the Logto server once and subsequently verify access tokens locally.

Role-based access control

Up to this point, we've only verified that a user has logged in with Logto. We still don't know if the user possesses the appropriate permission to access the API endpoint. This is because Logto permits anyone to obtain an access token for an existing API resource.

To address this, we can employ role-based access control (RBAC). In Logto, you can define roles and assign permissions to them. Consult this tutorial to learn how to define roles and permissions in Logto.

After defining roles and permissions, you can add the scopes option to the LogtoProvider component:

Logto will then only issue an access token with the appropriate scope(s) to the user. For instance, if a user only has the read:products scope, the access token will solely contain that scope:

If a user has both the read:products and write:products scopes, the access token will contain both scopes with a space as the delimiter:

In your Express.js application, you can verify if the access token contains the correct scope(s) before granting access to the API endpoint:

Conclusion

Protecting API endpoints while ensuring scalability is no small feat. At Logto, we strive to simplify request authentication for developers, allowing you to focus more on your business logic.

For any questions, feel free to join our Discord server. Our community is always happy to assist you.