UNPKG

node-webtokens

Version:

Simple, opinionated implementation of JWS and JWE compact serialization

118 lines (105 loc) 3.9 kB
const jws = require('./lib/jws.js'); const jwe = require('./lib/jwe.js'); const { responder } = require('./lib/common.js'); // ===== TOKEN GENERATION ===================================================== exports.generate = (alg, ...rest) => { // alg, payload, key[, cb] or alg, payload, keystore, kid[, cb] if (rest[0].constructor === Object) return jws.generate(alg, ...rest); // alg, enc, payload, key[, cb] or alg, enc, payload, keystore, kid[, cb] if (rest[1].constructor === Object) return jwe.generate(alg, ...rest); // There is no payload object where expected let idx = rest.length - 1; if (idx > 1 && idx < 5 && typeof rest[idx] === 'function') { rest[idx](error); } else { throw error; } } // ===== TOKEN PARSING ======================================================== exports.parse = (token) => new ParsedToken(token); function ParsedToken(token) { this.parts = typeof token === 'string' ? token.split('.') : []; if (this.parts.length === 3) { this.type = 'JWS'; } else if (this.parts.length === 5) { this.type = 'JWE'; } else { this.error = { message: 'Invalid token' }; return; } try { this.header = JSON.parse(Buffer.from(this.parts[0], 'base64')); } catch (error) { this.error = { message: `Non parsable header. ${error.message}` }; return; } if (this.type === 'JWS') { // Parsing exposes payload for JWS only; for JWE happens at verify try { this.payload = JSON.parse(Buffer.from(this.parts[1], 'base64')); } catch (error) { this.error = { message: `Non parsable payload. ${error.message}` }; } } } // ===== POST-PARSING UTLITIES ================================================ ParsedToken.prototype.setAlgorithmList = function(algList, encList) { // algList is ignored if not string or array of strings if (typeof algList === 'string') { this.algList = [algList]; } else if (Array.isArray(algList)) { this.algList = algList; } // encList is ignored if not string or array of strings if (typeof encList === 'string') { this.encList = [encList]; } else if (Array.isArray(encList)) { this.encList = encList; } return this; } ParsedToken.prototype.setTokenLifetime = function(lifetime) { // lifetime is ignored if not integer greater than 0 if (Number.isInteger(lifetime) && lifetime > 0) this.lifetime = lifetime; return this; } ParsedToken.prototype.setAudience = function(audList) { // audList is ignored if not string or array of strings if (typeof audList === 'string') { this.audList = [audList]; } else if (Array.isArray(audList)) { this.audList = audList; } return this; } ParsedToken.prototype.setIssuer = function(issList) { // issList is ignored if not string or array of strings if (typeof issList === 'string') { this.issList = [issList]; } else if (Array.isArray(issList)) { this.issList = issList; } return this; } // ===== TOKEN VERIFICATION =================================================== ParsedToken.prototype.verify = function(p0, cb) { // key[, cb] or keystore[, cb] cb = typeof cb === 'function' ? cb : undefined; if (this.error) return responder(null, this, cb); let key; if (p0.constructor !== Object) { key = p0; } else if (this.header.kid === undefined) { // Cannot extract key from keystore this.error = { message: 'Missing kid claim in header' }; return responder(null, this, cb); } else if (p0[this.header.kid] === undefined) { // Key not found in keystore this.error = { message: 'Key with id not found', kid: this.header.kid }; return responder(null, this, cb); } else { key = p0[this.header.kid]; } let verify = this.type === 'JWS' ? jws.verify : jwe.verify; return verify(this, key, cb); }