Verifying JWTs in Cloudflare Workers

By Vlad Ionash — Published

Cloudflare Workers provide a serverless execution environment that enables developers to deploy applications globally at the edge, closer to users, for enhanced performance and reduced latency. This scalable, secure, and cost-effective platform simplifies the deployment process, eliminating the need for server management and significantly reducing operational costs.

There are currently caveats to operating Cloudflare Workers such as the inability to run most server side libraries and most packages containing dependencies that are not supported. More specifically, they only support Node.js packages that use webpack or another polyfill bundler since it is not Node.js. They will only run JavaScript code using the V8 engine on the edge network not in a Node.js runtime so any Node.js package that has Node.js specific dependencies like fs and net/http are not compatible.

We will have to find more easily integrated ways to replace some of our middlewares that do JWT signing. This where two specific libraries come into play, cloudflare-worker-jwt and jose.

The choice between cloudflare-worker-jwt and jose depends largely on your specific needs and environment. If you’re working exclusively within Cloudflare Workers and need a lightweight, optimized solution for JWT verification, cloudflare-worker-jwt is likely the better choice. On the other hand, if you need a more versatile library that can handle a wide range of cryptographic operations beyond just JWTs, and across different environments, jose would be more appropriate.

We will be showing how to do the JWT verification on both!

cloudflare-worker-jwt library

Link to this section

This library is designed to work within the Cloudflare Workers environment, which allows you to run JavaScript and WebAssembly code at the edge, closer to your users. Here’s how we set this up.

Step 1: Set up your Cloudflare Worker.

Link to this section

If you haven’t already, you’ll first need to set up a Cloudflare Worker. You can do this by visiting the Cloudflare Workers dashboard and creating a new worker. You’ll be provided with a script editor where you can write your code.

Step 2: Install the cloudflare-worker-jwt library.

Link to this section

To use the cloudflare-worker-jwt library in your Worker, you’ll typically use a tool like wrangler that supports the use of npm packages in Workers. However, you can’t directly use npm packages in Cloudflare Workers without bundling them first. You might need to use a bundler like Webpack or Rollup to include the library in your Worker script.

If you’re using Wrangler, you can add the library to your package.json and bundle your project with it. Here’s how you can add it:

{
    "dependencies": {
        "cloudflare-worker-jwt": "^version"
    }
}

Replace "^version" with the latest version of the cloudflare-worker-jwt library.

Step 3: Verify the JWT in your worker.

Link to this section

Once you have the library ready to use in your Worker, you can use it to verify JWTs:

import { verify } from 'cloudflare-worker-jwt';

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const token = request.headers.get('Authorization')?.split(' ')[1];
  if (!token) {
    return new Response('No token provided', { status: 401 });
  }

  const publicKey = 'YOUR_PUBLIC_KEY_HERE'; // Use your actual public key from the jwks endpoint

  try {
    const isValid = await verify(token, publicKey, {algorithm: "RS256"}));

    if (isValid) {
      return new Response('Token is valid', { status: 200 });
    } else {
      return new Response('Token is invalid', { status: 401 });
    }
  } catch (error) {
    return new Response(`Error verifying token: ${error.message}`, { status: 500 });
  }
}

It then uses the verify function from the cloudflare-worker-jwt library to check if the token is valid with the rs256 algorithm, using a public key that you must provide. You can grab your Kinde JWKS by navigating to this url: https://<YOUR_DOMAIN>.kinde.com/.well-known/jwks. You can also define the public key to point to the above URL for a more straightforward, adaptable way to manage the credentials.

Caveats and considerations

Link to this section

The verify function might have different parameters or options depending on the version of the library, so always check the latest documentation for the most accurate information. Error handling is crucial when dealing with authentication mechanisms. Make sure to appropriately handle any errors that might occur during the verification process.

Using the jose library by panva to verify a JWT in a Cloudflare Worker involves a slightly different approach compared to other libraries because jose is designed with modern JavaScript features and standards in mind.

Step 1: Set up your Cloudflare Worker environment

Link to this section

Ensure you have a Cloudflare Worker project set up. You can do this by visiting the Cloudflare Workers dashboard and creating a new worker. You’ll be provided with a script editor where you can write your code.

Step 2: Add the jose library

Link to this section

You would typically use a tool like wrangler to manage your Cloudflare Worker projects. Since Cloudflare Workers do not natively support NPM packages, you will need to bundle your project along with its dependencies using a bundler like Webpack or Rollup. Generally, you can install jose by running npm install jose or yarn add jose in your project directory. Alternatively, use a bundler configured to target a WebWorker environment to include jose in your Cloudflare Worker script.

Step 3: Verify the JWT

Link to this section

With your environment set up and jose added to your project, you can now write the code to verify JWTs in your Cloudflare Worker. Here’s an example:

import {jwtVerify} from "jose";

const handleRequest = async (request) => {
    const token = request.headers.get("Authorization")?.split(" ")[1]; // Assuming the token is in the Authorization header
    if (!token) {
        return new Response("No token provided", {status: 401});
    }

    const publicKey = await crypto.subtle.importKey(
        "jwk", // Assuming the key is in JWK format
        {
            kty: "RSA",
            e: "AQAB",
            n: "YOUR_PUBLIC_KEY_N_HERE",
            alg: "RS256",
            use: "sig"
        },
        {
            name: "RSASSA-PKCS1-v1_5",
            hash: {name: "SHA-256"}
        },
        false,
        ["verify"]
    );

    try {
        const {payload} = await jwtVerify(token, publicKey);
        // Optionally, check the payload for required claims

        return new Response("Token is valid", {status: 200});
    } catch (error) {
        return new Response(`Token verification failed: ${error.message}`, {status: 401});
    }
};

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event.request));
});

Key points to consider:

Link to this section

This example assumes your public key is in JWK format, like the one supplied by Kinde. You can grab your Kinde JWKS by navigating to this url:

https://<YOUR_DOMAIN>.kinde.com/.well-known/jwks.

You can also define the public key to point to the above uri for a more straightforward, adaptable way to manage the credentials.

💡 After verifying the signature, you may want to inspect the JWT payload for specific claims (e.g., issuer, audience, expiration) to further validate the token’s authenticity and appropriateness for your application.

Whichever method you choose to deploy, you should now have everything you need to verify your JWTs on Cloudflare Workers!