Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

jwt-utils

Package Overview
Dependencies
Maintainers
3
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jwt-utils - npm Package Compare versions

Comparing version 1.0.1 to 1.0.3

48

errors.json

@@ -269,2 +269,26 @@ [

{
"type": "MISSING_IAT",
"fields": [
{
"key": "message",
"value": "iat field is missing in the JWT payload.",
"doc": null
},
{
"key": "level",
"value": "info",
"doc": null
},
{
"key": "namespace",
"value": "JWTPAYLOAD",
"doc": null
}
],
"doc": {
"description": "Iat is required.",
"tags": []
}
},
{
"type": "INVALID_EXP",

@@ -342,2 +366,26 @@ "fields": [

{
"type": "NO_FRESH_JWT",
"fields": [
{
"key": "message",
"value": "JWT has discarded by client. No fresh JWT.",
"doc": null
},
{
"key": "level",
"value": "info",
"doc": null
},
{
"key": "namespace",
"value": "JWTPAYLOAD",
"doc": null
}
],
"doc": {
"description": "The jwt has discarded by client. Current time is greater than client expiration.",
"tags": []
}
},
{
"type": "FUTURE_JWT",

@@ -344,0 +392,0 @@ "fields": [

@@ -127,2 +127,11 @@ /**

/**
* Iat is required.
*/
MISSING_IAT: {
message: 'iat field is missing in the JWT payload.',
level: 'info',
namespace: 'JWTPAYLOAD'
},
/**
* Invalid exp value.

@@ -155,2 +164,11 @@ */

/**
* The jwt has discarded by client. Current time is greater than client expiration.
*/
NO_FRESH_JWT: {
message: 'JWT has discarded by client. No fresh JWT.',
level: 'info',
namespace: 'JWTPAYLOAD'
},
/**
* The jwt has an invalid date. Perhaps the machine clocks has some differences or you may suffering an attack.

@@ -157,0 +175,0 @@ */

477

lib/jwt-utils.js

@@ -54,329 +54,330 @@ /**

var config = {};
/**
* The exported API
* @type {*}
* @return {Object}
*/
module.exports = function(configuration) {
function base64urlEscape(str) {
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
var config = {};
function base64urlUnescape(str) {
str += new Array(5 - str.length % 4).join('=');
return str.replace(/\-/g, '+').replace(/_/g, '/');
}
function base64urlEscape(str) {
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function base64urlEncode(str) {
return base64urlEscape(new Buffer(str).toString('base64'));
}
function base64urlDecode(str) {
return new Buffer(base64urlUnescape(str), 'base64');
}
function encodeBase64url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
function convertSlowBufToBuf(slowBuffer) {
var buffer = new Buffer(slowBuffer.length);
slowBuffer.copy(buffer);
return buffer;
}
function readJWTHeader(base64header) {
var headerStr = base64urlDecode(base64header);
var header = JSON.parse(headerStr);
return header;
}
function getEncryptionAndHashAlgoritms(encHeaderValue) {
var algs = encHeaderValue.split('-');
var cipherAlgorithm = cipherMap[algs[0]];
var hashAlgorithm = hashAlgorithms[algs[1]];
if (!cipherAlgorithm) {
throw errors.ALGORITHM_NOT_SUPPORTED('Invalid encryption algorithm: ' + algs[0]);
function base64urlUnescape(str) {
str += new Array(5 - str.length % 4).join('=');
return str.replace(/\-/g, '+').replace(/_/g, '/');
}
if (!hashAlgorithm) {
throw errors.ALGORITHM_NOT_SUPPORTED('Invalid hash algorithm: ' + algs[1]);
function base64urlEncode(str) {
return base64urlEscape(new Buffer(str).toString('base64'));
}
return {
cipherAlgorithm: cipherAlgorithm,
hashAlgorithm: hashAlgorithm
};
}
function encodeJWT(payload, header, key) {
if (!key) {
throw errors.MISSING_REQUIRED_KEY();
function base64urlDecode(str) {
return new Buffer(base64urlUnescape(str), 'base64');
}
if (!hashAlgorithms[header.alg]) {
throw errors.ALGORITHM_NOT_SUPPORTED();
function encodeBase64url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
// header, typ is fixed value.
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
function convertSlowBufToBuf(slowBuffer) {
var buffer = new Buffer(slowBuffer.length);
slowBuffer.copy(buffer);
return buffer;
}
// create segments, all segment should be base64 string
var segments = [];
segments.push(base64urlEncode(JSON.stringify(header)));
segments.push(base64urlEncode(JSON.stringify(payload)));
var signer = jwa(header.alg);
//If we use RS256, the key is the private Key
if (header.alg.indexOf('HS') === 0) {
key = new Buffer(key, 'hex');
function readJWTHeader(base64header) {
var headerStr = base64urlDecode(base64header);
var header = JSON.parse(headerStr);
return header;
}
segments.push(signer.sign(segments.join('.'), key));
return segments.join('.');
}
function getEncryptionAndHashAlgoritms(encHeaderValue) {
var algs = encHeaderValue.split('-');
var cipherAlgorithm = cipherMap[algs[0]];
var hashAlgorithm = hashAlgorithms[algs[1]];
function decodeJWT(token, key) {
var segments = token.split('.');
if (segments.length !== 3) {
throw errors.SEGMENTS_NUMBER_ERROR(segments.length);
if (!cipherAlgorithm) {
throw errors.ALGORITHM_NOT_SUPPORTED('Invalid encryption algorithm: ' + algs[0]);
}
if (!hashAlgorithm) {
throw errors.ALGORITHM_NOT_SUPPORTED('Invalid hash algorithm: ' + algs[1]);
}
return {
cipherAlgorithm: cipherAlgorithm,
hashAlgorithm: hashAlgorithm
};
}
var header = readJWTHeader(segments[0]);
function encodeJWT(payload, header, key) {
if (!key) {
throw errors.MISSING_REQUIRED_KEY();
}
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
if (!hashAlgorithms[header.alg]) {
throw errors.ALGORITHM_NOT_SUPPORTED();
}
var algorithm = header.alg || 'HS256';
// header, typ is fixed value.
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
if (!hashAlgorithms[header.alg]) {
throw errors.ALGORITHM_NOT_SUPPORTED();
}
// create segments, all segment should be base64 string
var segments = [];
segments.push(base64urlEncode(JSON.stringify(header)));
segments.push(base64urlEncode(JSON.stringify(payload)));
var signer = jwa(header.alg);
//If we use RS256, the key is the private Key
if (header.alg.indexOf('HS') === 0) {
key = new Buffer(key, 'hex');
}
segments.push(signer.sign(segments.join('.'), key));
var signer = jwa(algorithm);
if (algorithm.indexOf('HS') === 0) {
key = new Buffer(key, 'hex');
return segments.join('.');
}
if (!signer.verify(segments[0] + '.' + segments[1], segments[2], key)) {
throw errors.WRONG_TOKEN_SIGNATURE();
}
var payloadStr = base64urlDecode(segments[1]);
function decodeJWT(token, key) {
var segments = token.split('.');
if (segments.length !== 3) {
throw errors.SEGMENTS_NUMBER_ERROR(segments.length);
}
return {
header: header,
payload: JSON.parse(payloadStr)
};
}
var header = readJWTHeader(segments[0]);
function decryptJWT(token, encKey, hashKey) {
var segments = token.split('.');
if (segments.length !== 5) {
throw errors.SEGMENTS_NUMBER_ERROR(segments.length);
}
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
var header = readJWTHeader(segments[0]);
var algorithm = header.alg || 'HS256';
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
if (!hashAlgorithms[header.alg]) {
throw errors.ALGORITHM_NOT_SUPPORTED();
}
var algorithms = getEncryptionAndHashAlgoritms(header.enc);
var signer = jwa(algorithm);
if (algorithm.indexOf('HS') === 0) {
key = new Buffer(key, 'hex');
}
if (!signer.verify(segments[0] + '.' + segments[1], segments[2], key)) {
throw errors.WRONG_TOKEN_SIGNATURE();
}
var initializationVector = null;
if (segments[2]) {
initializationVector = base64urlDecode(segments[2]);
}
var payloadStr = base64urlDecode(segments[1]);
var cypherText = null;
if (segments[3] === null || segments[3] === '') {
throw errors.INVALID_FOURTH_SEGMENT();
} else {
cypherText = base64urlDecode(segments[3]);
return {
header: header,
payload: JSON.parse(payloadStr)
};
}
var origAuthenticationTag = null;
if (segments[4]) {
origAuthenticationTag = segments[4];
}
function decryptJWT(token, encKey, hashKey) {
var segments = token.split('.');
if (segments.length !== 5) {
throw errors.SEGMENTS_NUMBER_ERROR(segments.length);
}
var encKeyBuffer = new Buffer(encKey, 'hex');
var result, hashBuf;
try {
var decipher = crypto.createDecipheriv(algorithms.cipherAlgorithm, encKeyBuffer, initializationVector);
var header = readJWTHeader(segments[0]);
decipher.setAutoPadding(true);
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
var mainBody = decipher.update(cypherText);
var endBody = decipher.final();
result = Buffer.concat([convertSlowBufToBuf(mainBody), convertSlowBufToBuf(endBody)]).toString();
var algorithms = getEncryptionAndHashAlgoritms(header.enc);
var b64Header = new Buffer(segments[0]);
var iv = new Buffer(initializationVector);
var encryptedBody = new Buffer(cypherText);
var initializationVector = null;
if (segments[2]) {
initializationVector = base64urlDecode(segments[2]);
}
// create al vector
var al = new Buffer(4);
al.writeInt32BE(b64Header.length * 8, 0);
var cypherText = null;
if (segments[3] === null || segments[3] === '') {
throw errors.INVALID_FOURTH_SEGMENT();
} else {
cypherText = base64urlDecode(segments[3]);
}
var buf4Clear = new Buffer(4);
buf4Clear.fill(0);
var alResult = Buffer.concat([buf4Clear, al]);
var origAuthenticationTag = null;
if (segments[4]) {
origAuthenticationTag = segments[4];
}
var authTag = new Buffer(Buffer.concat([b64Header, iv, encryptedBody, alResult]));
var encKeyBuffer = new Buffer(encKey, 'hex');
var result, hashBuf;
try {
var decipher = crypto.createDecipheriv(algorithms.cipherAlgorithm, encKeyBuffer, initializationVector);
var hashKeyBuffer = new Buffer(hashKey, 'hex');
var authTagHash = crypto.createHmac(algorithms.hashAlgorithm, hashKeyBuffer).update(authTag).digest();
decipher.setAutoPadding(true);
var authTagHashBuf = convertSlowBufToBuf(authTagHash);
hashBuf = authTagHashBuf.slice(0, authenticationTagBits[algorithms.hashAlgorithm]);
} catch (err) {
debug('Error Decrypt: ', err.message);
throw errors.DECRYPTION_ERROR(': ' + err.message);
}
var mainBody = decipher.update(cypherText);
var endBody = decipher.final();
result = Buffer.concat([convertSlowBufToBuf(mainBody), convertSlowBufToBuf(endBody)]).toString();
if (base64urlEscape(hashBuf.toString('base64')) !== origAuthenticationTag) {
throw errors.DECRYPTION_ERROR('');
} else {
return {
header: header,
payload: JSON.parse(result)
};
}
}
var b64Header = new Buffer(segments[0]);
var iv = new Buffer(initializationVector);
var encryptedBody = new Buffer(cypherText);
function generateRandomInitializationVector() {
var iv = [];
for (var i = 0; i < 16; i++) {
iv.push(Math.round(Math.random() * 255));
}
// create al vector
var al = new Buffer(4);
al.writeInt32BE(b64Header.length * 8, 0);
return new Buffer(iv);
}
var buf4Clear = new Buffer(4);
buf4Clear.fill(0);
var alResult = Buffer.concat([buf4Clear, al]);
function encryptJWT(payload, header, encKey, hashKey) {
var authTag = new Buffer(Buffer.concat([b64Header, iv, encryptedBody, alResult]));
header = _.defaults(header, {
alg: 'dir',
enc: 'A256CBC-HS512'
});
var hashKeyBuffer = new Buffer(hashKey, 'hex');
var authTagHash = crypto.createHmac(algorithms.hashAlgorithm, hashKeyBuffer).update(authTag).digest();
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
var authTagHashBuf = convertSlowBufToBuf(authTagHash);
hashBuf = authTagHashBuf.slice(0, authenticationTagBits[algorithms.hashAlgorithm]);
} catch (err) {
debug('Error Decrypt: ', err.message);
throw errors.DECRYPTION_ERROR(': ' + err.message);
}
if (base64urlEscape(hashBuf.toString('base64')) !== origAuthenticationTag) {
throw errors.DECRYPTION_ERROR('');
} else {
return {
header: header,
payload: JSON.parse(result)
};
}
}
var algorithms = getEncryptionAndHashAlgoritms(header.enc);
function generateRandomInitializationVector() {
var iv = [];
for (var i = 0; i < 16; i++) {
iv.push(Math.round(Math.random() * 255));
}
var iv = generateRandomInitializationVector();
return new Buffer(iv);
}
var segments = [];
function encryptJWT(payload, header, encKey, hashKey) {
var b64Header = encodeBase64url(new Buffer(JSON.stringify(header)).toString('base64'));
segments.push(b64Header);
header = _.defaults(header, {
alg: 'dir',
enc: 'A256CBC-HS512'
});
var b64Jek = '';
segments.push(b64Jek);
if (!header.kid) {
throw errors.MISSING_REQUIRED_KID();
}
var b64IV = encodeBase64url(iv.toString('base64'));
segments.push(b64IV);
var algorithms = getEncryptionAndHashAlgoritms(header.enc);
var encKeyBuffer = new Buffer(encKey, 'hex');
var cipher = crypto.createCipheriv(algorithms.cipherAlgorithm, encKeyBuffer, iv);
var iv = generateRandomInitializationVector();
var cipherTextBegin = cipher.update(new Buffer(JSON.stringify(payload), 'utf8'));
var segments = [];
var cipherTextEnd = cipher.final();
var b64Header = encodeBase64url(new Buffer(JSON.stringify(header)).toString('base64'));
segments.push(b64Header);
var cipherTextBuf = Buffer.concat([convertSlowBufToBuf(cipherTextBegin), convertSlowBufToBuf(cipherTextEnd)]);
var b64Jek = '';
segments.push(b64Jek);
var b64CipherText = encodeBase64url(cipherTextBuf.toString('base64'));
segments.push(b64CipherText);
var b64IV = encodeBase64url(iv.toString('base64'));
segments.push(b64IV);
// Calculate AuthTag
var encKeyBuffer = new Buffer(encKey, 'hex');
var cipher = crypto.createCipheriv(algorithms.cipherAlgorithm, encKeyBuffer, iv);
var b64HeaderBuf = new Buffer(b64Header);
var cipherTextBegin = cipher.update(new Buffer(JSON.stringify(payload), 'utf8'));
// We have iv Buffer and cipherTextBuf Buffer calculated.
var cipherTextEnd = cipher.final();
var al = new Buffer(4);
al.writeInt32BE(b64HeaderBuf.length * 8, 0);
var cipherTextBuf = Buffer.concat([convertSlowBufToBuf(cipherTextBegin), convertSlowBufToBuf(cipherTextEnd)]);
var buf4Clear = new Buffer(4);
buf4Clear.fill(0);
var b64CipherText = encodeBase64url(cipherTextBuf.toString('base64'));
segments.push(b64CipherText);
var alResult = Buffer.concat([buf4Clear, al]);
// Calculate AuthTag
var authTag = new Buffer(Buffer.concat([b64HeaderBuf, iv, cipherTextBuf, alResult]));
var b64HeaderBuf = new Buffer(b64Header);
var hashKeyBuffer = new Buffer(hashKey, 'hex');
var base64str = crypto.createHmac(algorithms.hashAlgorithm, hashKeyBuffer).update(authTag).digest();
// We have iv Buffer and cipherTextBuf Buffer calculated.
var buf = convertSlowBufToBuf(base64str);
var al = new Buffer(4);
al.writeInt32BE(b64HeaderBuf.length * 8, 0);
var result = buf.slice(0, authenticationTagBits[algorithms.hashAlgorithm]);
var buf4Clear = new Buffer(4);
buf4Clear.fill(0);
var b64AuthTag = encodeBase64url(result.toString('base64'));
segments.push(b64AuthTag);
var alResult = Buffer.concat([buf4Clear, al]);
return segments.join('.');
}
var authTag = new Buffer(Buffer.concat([b64HeaderBuf, iv, cipherTextBuf, alResult]));
function makePayload(payload) {
payload.iat = payload.iat || parseInt(new Date().getTime() / 1000, 10);
payload.exp = payload.exp || payload.iat + config.expiration;
return payload;
}
var hashKeyBuffer = new Buffer(hashKey, 'hex');
var base64str = crypto.createHmac(algorithms.hashAlgorithm, hashKeyBuffer).update(authTag).digest();
function checkPayload(token, cb) {
var payload = token.payload;
if (!payload) {
return cb(errors.EMPTY_PAYLOAD());
var buf = convertSlowBufToBuf(base64str);
var result = buf.slice(0, authenticationTagBits[algorithms.hashAlgorithm]);
var b64AuthTag = encodeBase64url(result.toString('base64'));
segments.push(b64AuthTag);
return segments.join('.');
}
var currentDate = parseInt(Date.now() / 1000, 10);
if (payload.iat) {
var iatParsed = parseInt(payload.iat, 10);
if (isNaN(iatParsed) || payload.iat !== iatParsed) {
return cb(errors.INVALID_IAT());
}
function makePayload(payload) {
payload.iat = payload.iat || parseInt(new Date().getTime() / 1000, 10);
payload.exp = payload.exp || payload.iat + config.expiration;
return payload;
}
if (payload.iat > currentDate + config.futureTolerance) {
return cb(errors.FUTURE_JWT());
function checkPayload(token, cb) {
var payload = token.payload;
if (!payload) {
return cb(errors.EMPTY_PAYLOAD());
}
if (!payload.exp) {
if (!payload.hasOwnProperty('exp')) {
var expired = iatParsed + config.expiration;
var currentDate = parseInt(Date.now() / 1000, 10);
if (expired <= currentDate) {
return cb(errors.EXPIRED_JWT(), token);
}
if (payload.iat) {
var iatParsed = parseInt(payload.iat, 10);
if (isNaN(iatParsed) || payload.iat !== iatParsed) {
return cb(errors.INVALID_IAT());
}
if (payload.iat > currentDate + config.futureTolerance) {
return cb(errors.FUTURE_JWT());
}
} else {
if (payload.hasOwnProperty('iat')) {
return cb(errors.INVALID_IAT());
} else {
return cb(errors.INVALID_EXP());
return cb(errors.MISSING_IAT(), token);
}
}
} else {
if (payload.hasOwnProperty('iat')) {
return cb(errors.INVALID_IAT());
var expLimit = iatParsed + config.expiration;
if (expLimit <= currentDate) {
return cb(errors.NO_FRESH_JWT(), token);
}
}
if (payload.exp) {
var expParsed = parseInt(payload.exp, 10);
if (isNaN(expParsed) || payload.exp !== expParsed) {
return cb(errors.INVALID_EXP());
if (payload.exp) {
var expParsed = parseInt(payload.exp, 10);
if (isNaN(expParsed) || payload.exp !== expParsed) {
return cb(errors.INVALID_EXP());
}
if (payload.exp <= currentDate) {
return cb(errors.EXPIRED_JWT(), token);
}
} else {
if (payload.hasOwnProperty('exp')) {
return cb(errors.INVALID_EXP());
}
}
if (payload.exp <= currentDate) {
return cb(errors.EXPIRED_JWT(), token);
}
return cb(null, token);
}
return cb(null, token);
}
/**
* The exported API
* @type {*}
* @return {Object}
*/
module.exports = function(configuration) {
if (configuration) {

@@ -383,0 +384,0 @@ config.expiration = configuration.expiration || DEFAULT_CONFIG.expiration;

{
"name": "jwt-utils",
"description": "JSON Web Tokens (JWT) utils",
"version": "1.0.1",
"version": "1.0.3",
"license": "Apache-2.0",

@@ -6,0 +6,0 @@ "author": {

@@ -421,3 +421,3 @@ 'use strict';

var expiration = 10;
jwtUtils = jwt({expiration: expiration});
var jwtUtilsMod = jwt({expiration: expiration});

@@ -431,3 +431,3 @@ var payload = {

jwtUtils.buildJWTEncrypted(payload, {kid: kid}, key, hashKey, function(err, jwt) {
jwtUtilsMod.buildJWTEncrypted(payload, {kid: kid}, key, hashKey, function(err, jwt) {
expect(err).to.not.exist;

@@ -437,4 +437,5 @@ var clock = sinon.useFakeTimers(new Date().getTime());

jwtUtils.readJWTEncrypted(jwt, key, hashKey, function(err, token) {
expect(err).to.exist;
expect(err).to.be.apiError(errors.EXPIRED_JWT());
clock.restore();
expect(err).to.be.apiError(errors.EXPIRED_JWT());
done();

@@ -828,2 +829,24 @@ });

it('should fail when client expired the jwt without taking into account the exp field', function() {
var jwtToken = 'eyJraWQiOiJraWQiLCJhbGciOiJkaXIiLCJlbmMiOiJBMjU' +
'2Q0JDLUhTNTEyIiwiY29yciI6ImNvcnIifQ..Xuaus8N7yqmojQFFFNgxL' +
'g.UCM5odV53W9NEaVvee7fCKb31B1C7wedJn02vapNOf2v3-dmKDaZxt2G' +
'nIDYK-TQ-XkiziRkRtZkjABFRNHPYw.FFduxvzrjIhWu8dQ3MMOVH11-tE' +
'BgE6955c4M9EO3fk';
var hashKey = '796f75722d7365637265742d6b657923796f75722d7365637265742d6b657923';
var encKey = '796f75722d7365637265742d6b657923796f75722d7365637265742d6b657923';
var clock = sinon.useFakeTimers(0, 'Date');
clock.tick((1443688542000 + 1) * 1000);
var jwtUtilsMod = jwt({expiration: 1});
jwtUtilsMod.readJWTEncrypted(jwtToken, encKey, hashKey, function(err, token) {
expect(err).to.exist;
expect(err.name).to.be.equal('NO_FRESH_JWT');
clock.restore();
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc