Skip to content

Instantly share code, notes, and snippets.

@CyrusRoshan
Forked from bcnzer/cloudflareworker-verifyjwt.js
Last active September 9, 2019 14:04
Show Gist options
  • Save CyrusRoshan/3a93d56e7cf1b4cf849c7eecb16116e2 to your computer and use it in GitHub Desktop.
Save CyrusRoshan/3a93d56e7cf1b4cf849c7eecb16116e2 to your computer and use it in GitHub Desktop.
Sample Cloudflare worker that gets a Cloudflare Access JWT, validates it, and returns a result
import * as cookie from 'cookie';
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
/**
* Fetch and log a request
* @param {Request} request
*/
export async function handleRequest(request: Request) {
var resp: Response;
try {
resp = await respond(request);
} catch (e) {
resp = new Response(`Error: ${e}`, {status: 500});
}
return resp;
}
async function respond(request: Request) {
let isValid = await isValidJwt(request);
if (!isValid) {
console.log('is NOT valid');
return new Response('Invalid JWT', {status: 403});
} else {
console.log('is valid');
}
return new Response('Valid JWT, good job!', {status: 200});
}
/**
* Parse the JWT and validate it.
*
* We are just checking that the signature is valid, but you can do more that.
* For example, check that the payload has the expected entries or if the signature is expired..
*/
async function isValidJwt(request: Request) {
const encodedToken = getJwt(request);
if (encodedToken === null) {
return false;
}
const token = decodeJwt(encodedToken);
// Is the token expired?
let expiryDate = new Date(token.payload.exp * 1000);
let currentDate = new Date(Date.now());
if (expiryDate <= currentDate) {
console.log('expired token');
return false;
}
return isValidJwtSignature(token);
}
/**
* For this example, the JWT is passed in as part of the Authorization header,
* after the Bearer scheme.
* Parse the JWT out of the header and return it.
*/
function getJwt(request: Request) {
const rawCookieHeader = request.headers.get('Cookie');
if (!rawCookieHeader) {
return '';
}
const cookies = cookie.parse(rawCookieHeader);
if (!cookies) {
return '';
}
const authJWT = cookies['CF_Authorization'];
if (!authJWT) {
return '';
}
return authJWT;
}
/**
* Parse and decode a JWT.
* A JWT is three, base64 encoded, strings concatenated with ‘.’:
* a header, a payload, and the signature.
* The signature is “URL safe”, in that ‘/+’ characters have been replaced by ‘_-’
*
* Steps:
* 1. Split the token at the ‘.’ character
* 2. Base64 decode the individual parts
* 3. Retain the raw Bas64 encoded strings to verify the signature
*/
function decodeJwt(token: string) {
const parts = token.split('.');
const header = JSON.parse(atob(parts[0]));
const payload = JSON.parse(atob(parts[1]));
const signature = atob(parts[2].replace(/_/g, '/').replace(/-/g, '+'));
console.log(header);
return {
header: header,
payload: payload,
signature: signature,
raw: {header: parts[0], payload: parts[1], signature: parts[2]},
};
}
/**
* Validate the JWT.
*
* Steps:
* Reconstruct the signed message from the Base64 encoded strings.
* Load the RSA public key into the crypto library.
* Verify the signature with the message and the key.
*/
async function isValidJwtSignature(token: any) {
const encoder = new TextEncoder();
const data = encoder.encode([token.raw.header, token.raw.payload].join('.'));
const signature = new Uint8Array(
Array.from(token.signature).map((c: any) => c.charCodeAt(0)),
);
/*
const jwk = {
alg: 'RS256',
e: 'AQAB',
ext: true,
key_ops: ['verify'],
kty: 'RSA',
n: RSA_PUBLIC_KEY
};
*/
const upToDateJWKReq = await fetch(
'https://[CHANGE THIS TO YOUR WEBSITE].cloudflareaccess.com/cdn-cgi/access/certs',
);
const upToDateJWKJson = await upToDateJWKReq.json();
for (var i = 0; i < upToDateJWKJson.keys.length; i++) {
const jwk = Object.assign({key_ops: ['verify']}, upToDateJWKJson.keys[i]);
const key = await crypto.subtle.importKey(
'jwk',
jwk,
{name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256'},
false,
['verify'],
);
if (crypto.subtle.verify('RSASSA-PKCS1-v1_5', key, signature, data)) {
return true;
}
}
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment