Securing a Node.js Express API with JWTs

On this page


A Node.js library for use as an Express middleware to secure endpoints with JWTs. The implementation uses a JWKS endpoint of an Authorization Server to get keys required for the verification of the token signature. There is also an example Express app which shows how to use the library. The example also shows how to decode and validate a JWT and access its claims.


Curity Identity Server is used in this example, but other OAuth servers can also be used.

Create a Node.js API

Create your own Node.js API according to an Online Article of your choice. Then install the Express JWT library, which we will use to perform the API's OAuth security:

  • npm install express-oauth-jwt

The library needs to be provided with the JWKS Endpoint of your Authorization Server, which will be a value such as this.

  • https://idsvr.example.com/oauth/v2/oauth-anonymous/jwks

Integrate the Security Library

Integrating the Curity Node.js Express Library then only requires a few lines of code. The code for a working OAuth secured Node.js API is provided below:

import express from 'express';
import {getSimpleJwksService, secure} from 'express-oauth-jwt';
const expressApp = express();
// Configure OAuth security to validate JWTs and to check the issuer + audience claims
const options = {
claims: [
name: 'iss',
value: 'https://idsvr.example.com/oauth/v2/oauth-anonymous',
name: 'aud',
value: 'api.example.com',
const jwksService = getSimpleJwksService('https://idsvr.example.com/oauth/v2/oauth-anonymous/jwks');
expressApp.use('/api/*', secure(jwksService, options));
// API routes can then access JWT claims in the request object
// In this example a role claim has been configured in the Curity Identity Server
expressApp.get('/api/data', (request, response) => {
const message = `API called for user with scope '${request.claims.scope}' and role '${request.claims.role}'`;
const data = {
const port = 3000;
expressApp.listen(port, () => {
console.log(`API is listening on HTTP port ${port}`);

Validate JWTs

The Express JWT library manages the technical details of JWT validation:

  • Looking up the kid value from the JWT header
  • Downloading the token signing public key from the Authorization Server's JWKS endpoint
  • Using the public key to verify the digital signature of the JWT
  • Checking of required scopes and claims from the options object
  • Caching the public key in memory for subsequent requests with the same kid value

Use Scopes and Claims

Once the token is validated, the API can access its scopes and claims via Express's request object. A real world API would then use the scopes and claims from the JWT to authorize access to business data, according to the below articles:

Test the API

The API's clients will call it via HTTP requests with an access token in the Authorization header, and this can be easily tested via a CURL request:

curl -i http://localhost:3000/api/data -H "Authorization: Bearer eyJraWQiOiIyMTg5NTc5MTYiLC ..."

In the event of a problem validating the JWT, the error reason is included in WWW-Authenticate response headers, as in the following example, where the access token is expired:

HTTP/1.1 401 Unauthorized
X-Powered-By: Express
WWW-Authenticate: Bearer
WWW-Authenticate: error="invalid_token"
WWW-Authenticate: error_description="'exp' claim timestamp check failed"
Date: Thu, 08 Apr 2021 12:55:04 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 0

Other Library Options

The Express JWT library has a number of more advanced options that can also be used if required. These are described in the GitHub repository's README file.


Integrating the Curity Express library is an easy way to implement OAuth security in your Node.js APIs. Once done you have full access to the access token's scopes and claims, so that your API can secure access to its data.

Join our Newsletter

Get the latest on identity management, API Security and authentication straight to your inbox.

Start Free Trial

Try the Curity Identity Server for Free. Get up and running in 10 minutes.

Start Free Trial