Skip to content

Conversation

@tamalchowdhury
Copy link
Collaborator

@tamalchowdhury tamalchowdhury commented Nov 10, 2025

Verifying JWTs in Cloudflare Workers

Learn how to securely verify JSON Web Tokens (JWTs) in Cloudflare Workers using the cloudflare-worker-jwt and jose libraries for fast, edge-based authentication.

Related issues & labels (optional)

  • Adds a new integration guide to third party tools section

Summary by CodeRabbit

  • Documentation
    • Added a comprehensive guide for verifying JWTs in Cloudflare Workers: prerequisites and Wrangler setup, two verification approaches (cloudflare-worker-jwt and jose) with example snippets, a helper for extracting public keys from JWKS, token extraction and import guidance, common error handling, local testing with wrangler dev, and deployment steps.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Walkthrough

A new MDX documentation page was added that explains how to verify JSON Web Tokens (JWTs) in Cloudflare Workers, covering prerequisites, Wrangler setup, JWKS handling, two verification approaches (cloudflare-worker-jwt and jose), sample code, local testing, and deployment steps.

Changes

Cohort / File(s) Summary
JWT Verification Documentation
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx
New MDX file added. Provides end-to-end guide: prerequisites and Wrangler scaffolding, helper extractPublicKey for JWKS modulus extraction, two verification approaches (cloudflare-worker-jwt and jose) with sample worker code, token extraction, Web Crypto public key import, error handling, local testing (wrangler dev), and deployment instructions.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Worker
    participant JWKS as JWKS Endpoint
    Note over Worker: Verification flow (two alternatives)
    Client->>Worker: HTTP request with Authorization: Bearer <token>
    Worker->>Worker: extract token from header
    Worker->>JWKS: GET /.well-known/jwks.json
    JWKS-->>Worker: JWKS (keys)
    alt Use cloudflare-worker-jwt
        Worker->>Worker: extract modulus & import key (WebCrypto)
        Worker->>Worker: cloudflare-worker-jwt.verify(token, key)
    else Use jose
        Worker->>Worker: build JWK / import via crypto.subtle
        Worker->>Worker: jose.verify(token, key)
    end
    alt verification succeeds
        Worker-->>Client: 200 OK (authorized)
    else verification fails
        Worker-->>Client: 401 / 403 (error)
    end
Loading

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

  • Documentation-only change; no API or runtime code modified.
  • Suggested review focus:
    • Verify code snippets (Web Crypto import, modulus-to-key logic) for correctness.
    • Ensure JWKS URL placeholders and header extraction examples are accurate.
    • Confirm consistency with repository docs style and formatting.

Poem

🐇 A rabbit hops through docs at night,

Fetching keys by moonlit byte,
Tokens checked, the path made right,
Workers guard the edge till light,
Hooray — the JWTs take flight! 🚀

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately describes the main change: adding a guide for verifying JWTs in Cloudflare Workers, which matches the new documentation file added.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tamal/feat/verify-jwt-cloudflare-workers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 10, 2025

Deploying kinde-docs-preview with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1e828b8
Status: ✅  Deploy successful!
Preview URL: https://e48ac4da.kinde-docs-preview.pages.dev
Branch Preview URL: https://tamal-feat-verify-jwt-cloudf.kinde-docs-preview.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (1)

119-146: Clarify the helper function's actual purpose and output.

The function is documented as extracting "the public key" but only returns the modulus (the n value). The exponent (e) is hardcoded as 'AQAB' in both implementations. Either clarify in the documentation that this function extracts only the modulus, or rename it to extractJwksModulus to reflect its actual behavior.

For example:

// Before
export async function extractPublicKey(jwksUrl) {
	try {
		// Fetch the JWKS JSON from the URL
		const response = await fetch(jwksUrl);
		if (!response.ok) {
			throw new Error(`Failed to fetch JWKS: ${response.statusText}`);
		}

		const jwks = await response.json();
		if (!jwks.keys || jwks.keys.length === 0) {
			throw new Error('No keys found in JWKS');
		}

		// Extract the 'n' (modulus) from the first key
		const { n } = jwks.keys[0];
		if (!n) {
			throw new Error("'n' key not found in the JWKS");
		}

		return n;
	} catch (error) {
		console.error(`Error extracting modulus: ${error.message}`);
		throw error;
	}
}

Option A (recommended): Update the comment and function name:

-// This function extracts the public key from your JWKS URL.
+// This function extracts the modulus (n value) from your JWKS URL for RSA public key construction.
 
-export async function extractPublicKey(jwksUrl) {
+export async function extractJwksModulus(jwksUrl) {

Option B: Enhance to return both n and e:

- return n;
+ return { n, e: jwks.keys[0].e || 'AQAB' };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3322f07 and 1b0150e.

📒 Files selected for processing (1)
  • src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.28.0)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx

[high] 311-311: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🔇 Additional comments (2)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (2)

308-312: Static analysis flag is a false positive; no action needed.

Gitleaks flagged the curl command at line 311 due to the Authorization header. However, this is instructional documentation with a clearly marked placeholder token (YOUR_JWT), not an actual secret. No changes are required. This is appropriate guidance for users testing their Worker locally.


1-44: Excellent documentation structure and coverage.

The guide is well-organized with clear step-by-step instructions, prerequisites, two implementation approaches, testing guidance, and deployment steps. The introduction effectively explains the use case and limitations of Cloudflare Workers. Once the variable shadowing issues in the code examples are resolved (as noted in earlier comments), this will be a high-quality integration guide.

Comment on lines 163 to 211
```jsx
import { verify } from '@tsndr/cloudflare-worker-jwt';
import { extractPublicKey } from './helper';

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

const jwksUrl = 'https://<your_kinde_account>.kinde.com/.well-known/jwks';
const publicKey = await extractPublicKey(jwksUrl);

// Define the public key in JWK format
const jwk = {
kty: 'RSA',
e: 'AQAB',
n: publicKey,
alg: 'RS256',
use: 'sig',
};

try {
// Import the public key
const publicKey = await crypto.subtle.importKey(
'jwk',
jwk,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' },
},
false,
['verify']
);

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 });
}
},
};
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Resolve variable shadowing and make status codes consistent.

Line 188 reassigns publicKey from a string (the modulus) to a CryptoKey object. Use distinct variable names to improve clarity:

 import { verify } from '@tsndr/cloudflare-worker-jwt';
 import { extractPublicKey } from './helper';
 
 export default {
 	async fetch(request) {
 		const token = request.headers.get('Authorization')?.split(' ')[1];
 		if (!token) {
 			return new Response('No token provided', { status: 401 });
 		}
 
 		const jwksUrl = 'https://<your_kinde_account>.kinde.com/.well-known/jwks';
-		const publicKey = await extractPublicKey(jwksUrl);
+		const modulus = await extractPublicKey(jwksUrl);
 
 		// Define the public key in JWK format
 		const jwk = {
 			kty: 'RSA',
 			e: 'AQAB',
-			n: publicKey,
+			n: modulus,
 			alg: 'RS256',
 			use: 'sig',
 		};
 
 		try {
 			// Import the public key
 			const publicKey = await crypto.subtle.importKey(
 				'jwk',
 				jwk,
 				{
 					name: 'RSASSA-PKCS1-v1_5',
 					hash: { name: 'SHA-256' },
 				},
 				false,
 				['verify']
 			);
 
 			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 });
+			return new Response(`Error verifying token: ${error.message}`, { status: 401 });
 		}
 	},
 };

Also align the error status code with Option 2 (use 401 for verification failures instead of 500).

🤖 Prompt for AI Agents
In
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx
around lines 163 to 211, fix the variable shadowing and status code: rename the
string modulus variable (currently `publicKey`) to something distinct like
`publicKeyModulus` or `jwkN`, build the JWK using that name, and rename the
imported CryptoKey (currently also `publicKey`) to `publicCryptoKey` (or
`importedPublicKey`) to avoid reassignment and confusion; also change the catch
response status from 500 to 401 so verification failures return 401 and update
the error response message accordingly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (1)

320-322: Minor: Add comma before "and" in the conclusion.

The closing statement would benefit from a comma before "and" to improve readability:

-With your Cloudflare Worker deployed and JWT verification fully configured, you now have a scalable, secure solution...
+With your Cloudflare Worker deployed, and JWT verification fully configured, you now have a scalable, secure solution...
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b0150e and 20abdd1.

📒 Files selected for processing (1)
  • src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.29.0)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx

[high] 297-297: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 LanguageTool
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx

[uncategorized] ~41-~41: Loose punctuation mark.
Context: ...fication: - cloudflare-worker-jwt: Lightweight and optimized for Cloudflar...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~42-~42: Loose punctuation mark.
Context: ...zed for Cloudflare Workers. - jose: More versatile, supporting a wide range...

(UNLIKELY_OPENING_PUNCTUATION)


[typographical] ~100-~100: It appears that a comma is missing.
Context: ...ant to use git for version control? No 2. Do you want to deploy your application? **...

(COMMA_BEFORE_QUESTION_WITH_MD)


[uncategorized] ~322-~322: Use a comma before “and” if it connects two independent clauses (unless they are closely connected and short).
Context: ...d! With your Cloudflare Worker deployed and JWT verification fully configured, you ...

(COMMA_COMPOUND_SENTENCE_2)

🔇 Additional comments (8)
src/content/docs/integrate/third-party-tools/verifying-jwts-cloudflare-workers.mdx (8)

1-44: ✓ Metadata and introduction are clear.

The frontmatter is complete, and the introduction effectively explains the edge deployment benefits and introduces both verification libraries.


46-86: ✓ Prerequisites and Wrangler setup are well-structured.

The installation steps are clear with visual confirmation. The browser screenshot aids user orientation.


88-150: ✓ Project setup and helper function are correct.

The extractPublicKey helper function properly validates the JWKS response and extracts the modulus. Error handling is appropriate.


154-211: ✓ Cloudflare Worker JWT example resolves past feedback.

Variable shadowing is fixed: modulus (line 174) correctly distinguishes the JWKS modulus from the imported publicKey CryptoKey (line 187). Error status code is consistent at 401 (line 206). The code flow is correct.


213-268: ✓ Jose example is consistent with first approach.

Variable shadowing fix applied (line 233 uses modulus, line 246 imports as publicKey). Error handling returns 401 consistently (line 263). The payload destructuring from jwtVerify is correct.


273-301: ✓ Testing section provides clear, practical guidance.

The local testing workflow is well-documented. The curl command (line 297) uses YOUR_JWT as an explicit placeholder—this is appropriate for documentation and the security scanning flag is a false positive. The steps to retrieve the actual token from Kinde are clear.

Confirm that users understand YOUR_JWT is a placeholder and should not commit actual tokens to version control.


303-318: ✓ Deployment instructions are clear.

The wrangler deploy command and confirmation screenshot provide good guidance for final deployment.


41-42: Minor documentation note: Grammar tool flagged markdown list items.

Static analysis flagged loose punctuation on lines 41–42 (list items with colons) and a missing comma on line 100. These appear to be false positives given markdown documentation conventions. The formatting is appropriate for the target format.

Also applies to: 100-100

@tamalchowdhury tamalchowdhury changed the title feat: verifying jwts in cloudflare workers feat: guide - verifying jwts in cloudflare workers Nov 13, 2025
Copy link
Member

@onderay onderay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved

@onderay onderay merged commit febe64b into main Nov 26, 2025
6 checks passed
@onderay onderay deleted the tamal/feat/verify-jwt-cloudflare-workers branch November 26, 2025 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants