Last active
August 27, 2022 06:02
-
-
Save whaaaley/d2750999decf734dcdc0d0e84abc8cf7 to your computer and use it in GitHub Desktop.
Compress JWT Payload
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
import * as crypto from 'node:crypto' | |
import pako from 'pako' | |
import basex from 'base-x' | |
const base16 = basex('0123456789abcdef') | |
const base62 = basex('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz') | |
let header = JSON.stringify({ alg: 'HS256', typ: 'JWT' }) | |
header = Buffer.from(header).toString('base64url') | |
function walkClaims (claims, cb) { | |
const aud = claims.aud | |
aud._id = cb(aud._id) | |
for (let i = 0; i < aud.orgRoles.length; i++) { | |
const org = aud.orgRoles[i] | |
org._id = cb(org._id) | |
for (let j = 0; j < org.projectRoles.length; j++) { | |
const project = org.projectRoles[j] | |
project._id = cb(project._id) | |
} | |
} | |
return claims | |
} | |
function compressClaims (claims) { | |
return walkClaims(claims, id => base62.encode(base16.decode(id))) | |
} | |
function decompressClaims (claims) { | |
return walkClaims(claims, id => base16.encode(base62.decode(id))) | |
} | |
export function createToken (claims, secret) { | |
claims = compressClaims(claims) // could be removed safely | |
const payload = Buffer | |
.from(pako.deflate(JSON.stringify(claims), { level: 9 })) // with pako | |
// .from(JSON.stringify(claims), { level: 9 }) // without pako | |
.toString('base64url') | |
const headerPayload = header + '.' + payload | |
const hmac = crypto | |
.createHmac('sha256', secret) | |
.update(headerPayload) | |
.digest('base64url') | |
return headerPayload + '.' + hmac | |
} | |
export function verifyToken (token, secret) { | |
const [header, payload, signature] = token.split('.') | |
const newSignature = crypto | |
.createHmac('sha256', secret) | |
.update(header + '.' + payload) | |
.digest('base64url') | |
if (signature === newSignature) { | |
let claims = JSON.parse(Buffer | |
.from(pako.inflate(Buffer.from(payload, 'base64url'))) // with pako | |
// .from(Buffer.from(payload, 'base64url')) // without pako | |
.toString()) | |
claims = decompressClaims(claims) // could be removed safely | |
return claims | |
} | |
throw new Error('Token failed to verify') | |
} |
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
function mongoId () { | |
return crypto.randomBytes(12).toString('hex') | |
} | |
const secret = crypto.randomBytes(256) | |
const claims = { | |
exp: Date.now() + 3600000, | |
aud: { | |
_id: mongoId(), | |
orgRoles: [{ | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}, { | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}, { | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}, { | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}, { | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}, { | |
_id: mongoId(), | |
role: 'editor', | |
projectRoles: [{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
},{ | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}, { | |
_id: mongoId(), | |
role: 'admin' | |
}] | |
}] | |
} | |
} | |
console.log('') | |
console.log('--- secret ---') | |
console.log(secret.toString('hex')) | |
console.log('') | |
console.log('--- create token ---') | |
const token = createToken(claims, secret) | |
console.log(token) | |
console.log('token length >>', token.length) | |
console.log('') | |
console.log('--- verify token ---') | |
const restoredClaims = verifyToken(token, secret) | |
console.log(restoredClaims) |
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
$ node index.js | |
--- secret --- | |
21292aaa8126a51c98a78feaf9fbcb0283e7ab994b0d31bd972645304609f236e3981708d43fb8e667658fff84a4a95407c777358b3c72629fe87a764e6987101e60486f96c98f8a27ca576291d862d20db1b0e97533cc9612e74af454394d15c3ee79e393aa8c43c5693ad6d201088f8a2efec2de60df4c79fac8595bb0f9aa529e465c8a785a76bb4561b7088ed419229d130caf5a8d93011ea0ff2e9cec085186e46e63c2767b0b7c2b4ba141b29f51605615b393b7892312caa7dac6f41ba1356649ef33f97cbacff62ed56e0cfbd80afa2af5497ed12f288267d00d085616502b469ac3821563de6785a3d364f78362ef66a00d39da12b19de353def7ef | |
--- create token --- | |
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eNqVlEuPozgUhf8L69qAsYHakRBAvMQbwqg14ukAEyBAgFCq_97ZtLrVPbG6Vpblz_a5x-f6gyq3gXqnEaIhD1haYBj4RqX3gnr_oP6tnwMFLe6cRg03iAgw4Qnx1BvVj9jt_ysn6v2fHxgtlZMXiddIwuKFBnr9xMYn81wqi3rux-d8GPumzOfft4JDb2XGOCmGaW6TJ9A_t6bFte6oz7cfaFAtTrncIBZmHXCX7TXJ9LNnTGCxR9NcUks3XqMsB6Q2jkWdKVQnrF2WgAY1H5z0o2dwaECP_P4aBVeblUtunEycPlW30x_ot1_UarMH29FOltY3Arus_t49JsLn0-LmOw0kQUl1SFDP6JjRj0CP5gFxFmwJ6l08SP55mPxdXkczKAmnKnx78Ktuk9ZCHeO0ITwK9DwNhqwo58a9F_iMICBv3T5DS7w7aouTS0myD7qLsAWJw_samh_SAX8hfADqtGEUsq6V6t0dLgRJIHW5SSvGwau07MxIhEIvW7ngh93JwsO8ZcVAOHXOFHmouhIbx1hRZe01CntvCrPFEyIsmGbaB6_RvfGvWX0Y_GRmHkMkEMMXM3QgKDlUtRQ39EP8e_egJST8Pp_Wo9Kdqi4MCXUqbNYmDoD5eCkZw7deo5YHrTrv_dXR6tTdCD4DuStvxa43q9_HILsTssfq4a51rmrPQ2YAj-EIaIMEBgpcWNNYrgfRJroXivrpPhfIhI7Nc0P6hdZt1B25On-7qIe-2mxCSthWvEpLnrlVNm8rmxNSwqjltCayrMT9UVJDmvBJ0mBnPIUNdnSGgreq-Ws02YAilfmg8kZ62z2PcH-KuSbFLNdN7LN5NWL22FsHV_lgtLRyRhB3yxc617Ksu107lbx5eRL1PsG9lF4rxF0uWTOLOFJ4gnodO3HY5sit_WE-nDAhfHzWTw_h5JZH8eBKjvAa3faYzbHbhntjpTeRQAJaCMZdP0cji5gVrcH_uPft8_M7b3lrsA.l68Gj_TiuSKbYY4xAYG9EzoIM2180HWeOjcWRu3T1lQ | |
token length >> 1075 | |
browser gzip >> 857 | |
--- verify token --- | |
{ | |
exp: 1661583419225, | |
aud: { | |
_id: 'e741bb9d39e9703e001c296d', | |
orgRoles: [ [Object], [Object], [Object], [Object], [Object], [Object] ] | |
} | |
} |
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
$ node index.js | |
--- secret --- | |
ffe3d5c790ef4a2e00235e990cd54cf8e6bfca14b9f0f157407b725d9a9226b0c4e2c6fa5e618b156497b7fea56024ead688712e418efd76e09131c192bfe3efb80bab2a87a080217cec4f00c57669659026dde59561e36c35d338cc8c8fc08088d38d714dd8e6947c43bd7781c5b97eabf37783eea8aa49a6296db53384e4603bddb59691845451df91c2d61ccc584ff5d94d9eeb425eddd38950726f4c7178dec4e1fc026b6578dfd5cd0c90294ab7c81c1742ff7e4c123edc13657a76476f6acb2609a14310287969764894e76ef1748d4c5b396fd4824a388ea86dce96b9cd6903ed2c49ccf6cac426067a42f10768e7a060e7fbe3a0d29dea6322386068 | |
--- create token --- | |
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjE1ODMzOTE3MzcsImF1ZCI6eyJfaWQiOiI3NmZjNTJlN2JhYWI0ZGY1YjRmOTE2NGYiLCJvcmdSb2xlcyI6W3siX2lkIjoiMWYwMzMyMDkyZWEzY2QzY2JhZDVmZGU1Iiwicm9sZSI6ImVkaXRvciIsInByb2plY3RSb2xlcyI6W3siX2lkIjoiNmVhOTZlMDEwZjgzZWQ2OWRjNzYxOTkzIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiI5NDZlYWY5NzY4MmQwN2JjZGZiMjk4ZTciLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImE1YzUzMzE3NWRjNmIxNGMxMmY1YmU4MiIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiMThkZTA0NDdiN2Q4Y2FhZmIxNDdhNjA0Iiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiI0NzA2MTZjZjRkNWNhYTI4NWZiMDU4ZGIiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6Ijc3MDA4ZTU5YjkzM2MzMzhlNzdkMjE5MyIsInJvbGUiOiJhZG1pbiJ9XX0seyJfaWQiOiIzNWNiNzBhYzhhM2RiMDVkMWRhMTg4NGEiLCJyb2xlIjoiZWRpdG9yIiwicHJvamVjdFJvbGVzIjpbeyJfaWQiOiJjZjVjYTRhNDBiYjQyMGM4MTM1NWZjOGEiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImU5OGNhMDY3ZDJkYWMwMGRiMmU5NzFlYiIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiOWNjMzc5ZmNjYzhlMjhkZjI2NzU1MWJmIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiIxZTFhNTViMGVmOTQ5ZTAwYmUxMmRhMmEiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImU0ODZhZTMyNDNjYmY2YzMyODUwZmJjOCIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiMmE0ODVlMTg5NDY0Yzk3M2E5NzU2YmIyIiwicm9sZSI6ImFkbWluIn1dfSx7Il9pZCI6IjczMDI0Nzk0Njg3ZWViZjkwNjhlZTY3OCIsInJvbGUiOiJlZGl0b3IiLCJwcm9qZWN0Um9sZXMiOlt7Il9pZCI6IjYzYTYzMTg5ZmI5ZjJiY2JiNDQ0ODNkYyIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiZWIzZmI1YTcwZjkxNDczMWEyYzcyYmIwIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiJmYTgyZjlmMTE1NjY5OWU2YTYyMGFlMDEiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImY0N2FiZDRhOGYyMzE0MzRmODBlZWUzOSIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiNGY0MDI3OTY2NjY1OTkyMTdmNGZkM2JlIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiJiNmU2NTVmNjJmYTU5ZWRlNzFiODNlYmMiLCJyb2xlIjoiYWRtaW4ifV19LHsiX2lkIjoiNGU3YzZmZTkyZTVhMTBjNjE5MDIyZTcwIiwicm9sZSI6ImVkaXRvciIsInByb2plY3RSb2xlcyI6W3siX2lkIjoiY2MwMmM0M2YyOTg4YmU2ZTc1ZjhiMWY3Iiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiJhZTgzZjg2MmFkNjFhYTRmOGJiZGMyYTQiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImM5MmFjMDI5ZjQxMmUzNmEwNGQxY2VlYyIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiNGNmNmE2MmNhYWQwMzgxMDQyNmVlNTBhIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiIxYzhlN2YxZjE2MjUzNzUyZjg3MWY0NjgiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6IjI5YTIwZjFjYmE5MTM5NjAyZDcwZDIwZiIsInJvbGUiOiJhZG1pbiJ9XX0seyJfaWQiOiI0MjFlNDNlZjI3Mzc0MzNmMGVlNDZmM2YiLCJyb2xlIjoiZWRpdG9yIiwicHJvamVjdFJvbGVzIjpbeyJfaWQiOiJmODU5MWU2YmI0MGRiYjcwNWIwZGFmNmYiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImIyNzg4ZTAzMTFiNDhhZTVmYmI5OTY4NyIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiNzUyZDkwMzA3YmQ1N2Y5M2E5MjVlOWJjIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiI5ZDBlMjkwMmZkYjU2ZDQwYzZkMTkyNzgiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImFlNTMyYTM5MGEwMzQwNDM1NDYyYzAyNSIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiMWYyZWZmNzdiOTY4MWEyMjliZmVjNGY0Iiwicm9sZSI6ImFkbWluIn1dfSx7Il9pZCI6ImU5ZjI5YjVkOGJlYjgzNDIxZTE0ZTQ4YyIsInJvbGUiOiJlZGl0b3IiLCJwcm9qZWN0Um9sZXMiOlt7Il9pZCI6IjczNGFjNDJjMjIyNjEwZTA1YWUyMjAzNiIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiZTIyZGNhNzJhOGE2NzNjZjVmOTMzMGI3Iiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiJjOTQzNTE2NjViYzk1MTM3NjJiNjI5N2IiLCJyb2xlIjoiYWRtaW4ifSx7Il9pZCI6ImRiMzUxMDdkNjY3OGM4OGY5YTM2YmI4YSIsInJvbGUiOiJhZG1pbiJ9LHsiX2lkIjoiZTRkMGJiOTE4NDUwMDQxZWI3ZjA3NGFjIiwicm9sZSI6ImFkbWluIn0seyJfaWQiOiI0NTc0OWU1OTBlMzRmMDM4OGEwMWYyNDAiLCJyb2xlIjoiYWRtaW4ifV19XX19.nx-uHrDXk4IIMp1zV9ZHG5XtvGLop6DJ8B0juX61BFg | |
token length >> 3125 | |
browser gzip >> 1322 | |
--- verify token --- | |
{ | |
exp: 1661583391737, | |
aud: { | |
_id: '76fc52e7baab4df5b4f9164f', | |
orgRoles: [ [Object], [Object], [Object], [Object], [Object], [Object] ] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This test shows that compressing jwt payloads can yield around a 65% savings when using pako.