Skip to content

Instantly share code, notes, and snippets.

@mattlockyer
Created November 2, 2019 04:01
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattlockyer/a8de509ed02c01cc08199efa524c9373 to your computer and use it in GitHub Desktop.
Save mattlockyer/a8de509ed02c01cc08199efa524c9373 to your computer and use it in GitHub Desktop.
JWT Token Module for Cloudflare Workers / Browser
/********************************
* Module for generating and verifying JWT tokens
* Designed to work on Cloudflare Workers
* Should work in the browser or any env that supports Web Crypto API
********************************/
/********************************
* Key secret is a random Uint8Array of length 64
* See below for instructions on generating random values / keys
********************************/
let key = new Uint8Array([...])
let header = { "alg": "HS256", "typ": "JWT" }
/********************************
* !!! Call init before using other functions !!!
*
* Converts key to CryptoKey type using importKey (below)
* Converts header to base64 header
********************************/
export const init = async () => {
key = await importKey(key)
header = encode(header)
}
/********************************
* Generates a JWT based on https://tools.ietf.org/html/rfc7519 (I think)
*
* Base64, then url encoded:
* (1) header
* (2) data
* (3) HMAC-SHA256 signature of (1) + (2)
* Joined with '.'
*
* @returns {string} the JWT token
********************************/
export const generateToken = async (data) => {
data = encode(JSON.stringify(data))
return [
header,
data,
encode(buf2txt(await sign(header + data)))
].join('.')
}
/********************************
* Split the token
* Verify the signature using the (1) + (2) as data input
*
* @returns {bool} if the token is valid or note
********************************/
export const verifyToken = async (token) => {
token = token.split('.')
return await verify(txt2buf(decode(token[2])), token[0] + token[1])
}
/********************************
* @returns {json} base64 decoded data (2) of JWT token
********************************/
export const parseToken = (token) => JSON.parse(decode(token.split('.')[1]))
/********************************
* Sign and Verify
********************************/
const sign = async (data) => crypto.subtle.sign(
{ name: "HMAC" }, key,
new TextEncoder().encode(data)
).catch((e) => console.error(e));
const verify = async (sig, data) => crypto.subtle.verify(
{ name: "HMAC" }, key, sig,
new TextEncoder().encode(data)
).catch((e) => console.error(e));
/********************************
* Importing keys
********************************/
const importKey = async (key) => crypto.subtle.importKey(
"raw", key,
{ name: "HMAC", hash: { name: "SHA-256" } },
false,
["sign", "verify"]
).catch((e) => console.log(e))
/********************************
* Helpers
********************************/
const decode = (base64) => JSON.parse(atob(base64.replace(/-/g, '+').replace(/_/g, '/')))
const encode = (json) => btoa(JSON.stringify(json)).replace(/\+/g, '-').replace(/\//g, '_');
const buf2txt = (buf) => String.fromCharCode(...new Uint8Array(buf))
const txt2buf = (str) => {
const len = str.length, bytes = new Uint8Array(len)
for (let i = 0; i < len; i++)
bytes[i] = str.charCodeAt(i)
return bytes.buffer;
}
/********************************
* Generate key in browser using the following functions
********************************/
// const key = new Uint8Array(64)
// crypto.getRandomValues(key)
// console.log(key)
// Or...
// const generateKey = async () => crypto.subtle.generateKey(
// { name: "HMAC", hash: { name: "SHA-256" } },
// true,
// ["sign", "verify"]
// ).catch((e) => console.log(e))
// const exportKey = (key) => crypto.subtle.exportKey(
// "raw", //can be "jwk" or "raw"
// key //extractable must be true
// ).catch((e) => console.error(2));
// const genKey = async () => {
// const key = await exportKey(await generateKey())
// console.log(key)
// console.log(new Uint8Array(key))
// console.log(JSON.stringify(key))
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment