Created
November 2, 2019 04:01
-
-
Save mattlockyer/a8de509ed02c01cc08199efa524c9373 to your computer and use it in GitHub Desktop.
JWT Token Module for Cloudflare Workers / Browser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************** | |
* 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