---
title: Validate JSON web tokens (JWT)
description: Extract a JWT from the Authorization header, verify its HMAC-SHA256 signature using the WebCrypto API, and validate its claims.
image: https://developers.cloudflare.com/core-services-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/rules/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

### Tags

[ Authentication ](https://developers.cloudflare.com/search/?tags=Authentication)[ Request modification ](https://developers.cloudflare.com/search/?tags=Request%20modification) 

# Validate JSON web tokens (JWT)

Extract a JWT from the Authorization header, verify its HMAC-SHA256 signature using the WebCrypto API, and validate its claims.

Caution when using in production

The example code contains a placeholder HMAC secret of `"mysecretsymmetrickey"`. To best protect your resources, change the secret to a strong, unique value in the Snippets editor before saving your code.

JavaScript

```

export default {

  async fetch(request) {

    const HMAC_SECRET = "mysecretsymmetrickey"; // Change this to your secret key

    const CLOCK_TOLERANCE = 30; // Tolerance for clock skew in seconds


    // Extract JWT token from "Authorization: Bearer" header

    function getJWTToken(request) {

      const authorizationHeader = request.headers.get("Authorization");

      if (authorizationHeader && authorizationHeader.startsWith("Bearer ")) {

        return authorizationHeader.substring(7, authorizationHeader.length);

      }

      return null;

    }


    // Convert a base64url string to a Uint8Array

    function base64urlToUint8Array(base64url) {

      const base64 = base64url

        .replace(/-/g, "+")

        .replace(/_/g, "/")

        .padEnd(base64url.length + ((4 - (base64url.length % 4)) % 4), "=");

      const binary = atob(base64);

      return Uint8Array.from(binary, (c) => c.charCodeAt(0));

    }


    // Decode a base64url-encoded JWT segment to a JSON object

    function decodeSegment(s) {

      const bytes = base64urlToUint8Array(s);

      return JSON.parse(new TextDecoder().decode(bytes));

    }


    // Validate JWT token structure, cryptographic signature, and claims.

    async function validateJWT(token) {

      const parts = token.split(".");

      if (parts.length !== 3) {

        throw new Error("Invalid JWT format");

      }


      const [headerB64, payloadB64, signatureB64] = parts;

      const decodedHeader = decodeSegment(headerB64);

      const decodedPayload = decodeSegment(payloadB64);


      // Verify the algorithm.

      // This prevents algorithm-confusion attacks where an attacker changes the "alg" header to bypass signature verification.

      // Never branch on the token's alg header to decide how to verify.

      // Always enforce the algorithm you expect.

      if (decodedHeader.alg !== "HS256") {

        throw new Error("Unsupported algorithm");

      }


      // Verify the cryptographic signature.

      // Import the shared secret as an HMAC-SHA256 key, then verify that the signature matches "header.payload".

      // If this fails, the token was tampered with or signed with a different key.

      const signature = base64urlToUint8Array(signatureB64);

      const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);

      const key = await crypto.subtle.importKey(

        "raw",

        new TextEncoder().encode(HMAC_SECRET),

        { name: "HMAC", hash: "SHA-256" },

        false,

        ["verify"],

      );

      const valid = await crypto.subtle.verify(

        { name: "HMAC" },

        key,

        signature,

        data,

      );


      if (!valid) {

        throw new Error("Invalid signature");

      }


      // Validate standard claims.

      // The claims a JWT must contain depends on your use case.

      const now = Math.floor(Date.now() / 1000);


      // exp (expiration): Reject tokens that have expired.

      // A small clock tolerance accounts for clock skew between servers.

      if (typeof decodedPayload.exp !== "number") {

        throw new Error("Missing exp claim");

      }

      if (decodedPayload.exp < now - CLOCK_TOLERANCE) {

        throw new Error("JWT has expired");

      }


      // iat (issued at): Ensure the token declares when it was created.

      if (typeof decodedPayload.iat !== "number") {

        throw new Error("Missing iat claim");

      }


      // Additional claims per RFC 7519 (https://www.rfc-editor.org/rfc/rfc7519.html):

      // - nbf (not before): Reject tokens used before their valid start time.

      //     if (decodedPayload.nbf > now + CLOCK_TOLERANCE) throw new Error("JWT is not yet valid");

      // - iss (issuer): Ensure the token was issued by a trusted authority.

      //     if (decodedPayload.iss !== "https://auth.example.com") throw new Error("Invalid issuer");

      // - aud (audience): Ensure the token is intended for your service.

      //     if (decodedPayload.aud !== "my-api") throw new Error("Invalid audience");

      // - sub (subject): Identify the principal the token refers to.

      // - jti (JWT ID): Unique token identifier, useful for preventing replay attacks.


      return true;

    }


    // Execute the function to extract JWT token

    const jwtToken = getJWTToken(request);


    // If the token is not provided, serve 401 Forbidden

    if (!jwtToken) {

      return new Response("Missing JWT token", { status: 401 });

    }


    try {

      // If the token is valid, the validateJWT function will not error and serve the actual response

      // An example of a valid token that will expire in 2033 is "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjI0OTkyMDAwLCJleHAiOjIwMDExMjAwMDB9._qgQ_TMrGfYgOoA8HtTZwEGoj8zAPWxsz8CT1jEAGzo"

      await validateJWT(jwtToken);

      return fetch(request);

    } catch {

      return new Response("Invalid JWT token", { status: 401 });

    }

  },

};


```

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/rules/","name":"Rules"}},{"@type":"ListItem","position":3,"item":{"@id":"/rules/snippets/","name":"Cloudflare Snippets"}},{"@type":"ListItem","position":4,"item":{"@id":"/rules/snippets/examples/","name":"Snippets examples"}},{"@type":"ListItem","position":5,"item":{"@id":"/rules/snippets/examples/jwt-validation/","name":"Validate JSON web tokens (JWT)"}}]}
```
