New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

kitten-jwt

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kitten-jwt - npm Package Compare versions

Comparing version 0.3.1 to 1.0.0

.eslintignore

196

index.js

@@ -1,2 +0,1 @@

const crypto = require('crypto');
const path = require('path');

@@ -14,3 +13,3 @@ const exec = require('child_process').exec;

const CLIENT_CACHE_SIZE_MAX = 50;
const CLIENT_CACHE_SIZE_MAX = 200;
const clientCache = new Qlru({maxSize : CLIENT_CACHE_SIZE_MAX});

@@ -24,2 +23,4 @@ // how many time before expiration do we renew the token in millisecond

const TOKEN_COOKIE_REGEXP = /access_token\s*=([^;]+?)(?:;|$)/;
/**

@@ -124,2 +125,4 @@ * Decode base64 url if there is in the token

let _tokenString = _headerBase64 + '.' + _payloadBase64;
let _header = null;
let _payload = null;

@@ -129,29 +132,29 @@ try {

let _payloadString = base64urlDecode(_payloadBase64);
let _header = JSON.parse(_headerString);
let _payload = JSON.parse(_payloadString);
_header = JSON.parse(_headerString);
_payload = JSON.parse(_payloadString);
}
catch (e) {
return callback(new Error('Invalid JSON Web Token: ' + e.message));
}
if (!(_payload instanceof Object)) {
return callback(new Error('Invalid Payload JSON Web token. It is not an object'));
}
if (!(_payload instanceof Object)) {
return callback(new Error('Invalid Payload JSON Web token. It is not an object'));
}
if (!(_header instanceof Object)) {
return callback(new Error('Invalid Header JSON Web Token. It is not an object'));
}
if (!(_header instanceof Object)) {
return callback(new Error('Invalid Header JSON Web Token. It is not an object'));
}
if (_header.alg !== ALGORITHM_NAME) {
return callback(new Error('Algorithm not accepted for JSON Web Token. Only ' + ALGORITHM_NAME + ' is accepted'));
}
if (_header.alg !== ALGORITHM_NAME) {
return callback(new Error('Algorithm not accepted for JSON Web Token. Only ' + ALGORITHM_NAME + ' is accepted'));
}
if (_payload.exp && Date.now() > parseInt(_payload.exp, 10) * 1000) {
return callback(new Error('JSON Web Token expired'));
}
if (_payload.exp && Date.now() > parseInt(_payload.exp, 10) * 1000) {
return callback(new Error('JSON Web Token expired'));
}
if (_payload.iss === '' || _payload.iss === undefined || _payload.iss === null) {
return callback(new Error('JSON Web Token without issuer'));
}
return callback(null, _payload, _tokenString, _signature);
if (_payload.iss === '' || _payload.iss === undefined || _payload.iss === null) {
return callback(new Error('JSON Web Token without issuer'));
}
catch (e) {
return callback(new Error('Invalid JSON Web Token ' + e.toString()));
}
return callback(null, _payload, _tokenString, _signature);
}

@@ -179,3 +182,3 @@

} catch (e) {
return callback(new Error('Invalid JSON Web Token' + e.toString()));
return callback(new Error('Invalid JSON Web Token: ' + e.message));
}

@@ -207,4 +210,5 @@ return callback(null, payload);

* @param {String} privKey private key
* @param {Object} data user data
*/
function getToken (clientId, serverId, privKey) {
function getToken (clientId, serverId, privKey, data) {
let _cacheKey = clientId + '_' + serverId;

@@ -217,3 +221,3 @@

}
let _newToken = generate(clientId, serverId, DEFAULT_EXPIRE_IN, privKey);
let _newToken = generate(clientId, serverId, DEFAULT_EXPIRE_IN, privKey, data);

@@ -229,5 +233,67 @@ clientCache.set(_cacheKey, {

/**
* Verify token with a list of public keys
*
* @param {Array} publicKeys array of public keys
* @param {Object} payload
* @param {String} tokenString
* @param {String} signature
* @param {Function} callback(err)
* @param {Number} i iterator of public keys
*/
function verifyTokenForEachPublicKey (publicKeys, payload, tokenString, signature, callback, i = 0) {
verifyToken(payload, tokenString, signature, publicKeys[i], (err) => {
i++;
if (!err || i >= publicKeys.length) {
return callback(err);
}
// avoid looping without releasing NodeJS event loop
process.nextTick(() => {
verifyTokenForEachPublicKey(publicKeys, payload, tokenString, signature, callback, i);
});
});
}
/**
* Check if a token exists in Authorization header first or in cookies
*
* @param {Object} req Req from request
* @param {Function} callback(err, oken)
*/
function findToken (req, callback) {
if (!req.headers) {
return callback(new Error('JSON Web Token - No HTTP header detected'));
}
// Accept tokens in authorization header and cookies
let _token = req.headers.authorization || req.headers.Authorization || parseCookie(req.headers.cookie);
if (typeof _token !== 'string' || _token.length === 0) {
return callback(new Error('No JSON Web Token detected in Authorization header or Cookie. Format is "Authorization: jwt" or "Cookie: access_token=jwt"'));
}
// remove Bearer keyword if present
if (/^Bearer /i.test(_token) === true) {
return callback(null, _token.slice(7));
}
return callback(null, _token);
}
/**
* Parse cookie to get JWT in access_token key
*
* @param {String} cookie req.headers.cookie
* @return {String} jwt if found, null otherwise
*/
function parseCookie (cookie) {
var _token = TOKEN_COOKIE_REGEXP.exec(cookie);
if (_token instanceof Array && _token.length > 1) {
return _token[1].trim();
}
return null;
}
/**
* Generate a middleware for Express
*
*
* @param {Mixed} serverId accepted server id

@@ -239,46 +305,43 @@ * @param {String} getPublicKeyFn public key of client

return function (req, res, next) {
if (!req.headers) {
return next(new Error('JSON Web Token - No HTTP header detected'));
}
let _auth = req.headers.Authorization || req.headers.authorization;
if (typeof _auth !== 'string') {
return next(new Error('No Authorization HTTP header detected. Format is "Authorization: Bearer jwt"'));
}
if (/^Bearer /i.test(_auth) === false) {
return next(new Error('No Bearer JSON Web Token detected. Format is "Authorization: Bearer jwt"'));
}
let _token = _auth.slice(7);
// is it in cache, fast return of errors or not
let _cachedToken = serverCache.get(_token);
if (_cachedToken) {
if (_cachedToken.payload !== undefined && _cachedToken.payload.exp !== undefined && Date.now() > parseInt(_cachedToken.payload.exp, 10) * 1000) {
_cachedToken.err = new Error('JSON Web Token expired');
}
if (_cachedToken.err) {
return next(_cachedToken.err);
}
req.jwtPayload = _cachedToken.payload;
return next();
}
// otherwise, compute everything
parseToken(_token, (err, payload, tokenString, signature) => {
// Get token in authorization header or cookies
findToken(req, (err, _token) => {
if (err) {
serverCache.set(_token, { payload : payload, err : err });
return next(err);
}
if (payload && payload.aud !== serverId) {
let _err = new Error('Invalid JSON Web Token audience');
serverCache.set(_token, { payload : payload, err : _err });
return next(_err);
// is it in cache, fast return of errors or not
let _cachedToken = serverCache.get(_token);
if (_cachedToken) {
if (_cachedToken.payload !== undefined && _cachedToken.payload.exp !== undefined && Date.now() > parseInt(_cachedToken.payload.exp, 10) * 1000) {
_cachedToken.err = new Error('JSON Web Token expired');
}
if (_cachedToken.err) {
return next(_cachedToken.err);
}
req.jwtPayload = _cachedToken.payload;
return next();
}
getPublicKeyFn(req, res, payload, function (publicKey) {
verifyToken(payload, tokenString, signature, publicKey, (err) => {
// otherwise, compute everything
parseToken(_token, (err, payload, tokenString, signature) => {
if (err) {
serverCache.set(_token, { payload : payload, err : err });
if (err) {
return next(err);
}
req.jwtPayload = payload;
next();
return next(err);
}
if (payload && payload.aud !== serverId) {
let _err = new Error('Invalid JSON Web Token audience');
serverCache.set(_token, { payload : payload, err : _err });
return next(_err);
}
getPublicKeyFn(req, res, payload, function (publicKey) {
var _publicKeys = (publicKey instanceof Array) ? publicKey : [publicKey];
verifyTokenForEachPublicKey(_publicKeys, payload, tokenString, signature, (err) => {
// what ever happens, put result in cache
serverCache.set(_token, { payload : payload, err : err });
if (err) {
return next(err);
}
req.jwtPayload = payload;
next();
});
});

@@ -306,4 +369,5 @@ });

generateECDHKeys,
parseCookie,
generateAuto : getToken // deprecated
};
{
"name": "kitten-jwt",
"version": "0.3.1",
"version": "1.0.0",
"description": "Keep It Simple, Stupid, Secure and Fast JWT module",
"main": "index.js",
"scripts": {
"test": "./node_modules/mocha/bin/mocha -w"
"test": "./node_modules/mocha/bin/mocha -w -b -t 5000"
},

@@ -31,6 +31,3 @@ "repository": {

"quick-lru": "2.0.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
}
}

@@ -5,4 +5,2 @@ # Kitten JWT

Maintained by https://www.easilys.com and https://carbone.io
## Philosophy and why

@@ -21,2 +19,4 @@

This module solves this for you. It chooses a highly secured algorithm by default. If you want another algorithm, fork it.
The algorithm used (asymmetric) allow the client to generate himself a token without having to exchange a secret with the server.
Only the public key is exchanged.

@@ -45,4 +45,6 @@ To save extra bandwidth, it let you define only two parameters : a client id ("Alice", issuer), and a server id ("Bob", audience).

* On client-side
#### 1) On client-side
Using `request` module for example:
```js

@@ -53,12 +55,21 @@ var jwt = require('kitten-jwt');

// This function is very fast (uses cache), it can be called for every HTTP request
var header = jwt.getToken('client-id-1220202', 'server-app-name', 'privKeyOfTheClient');
var token = jwt.getToken('client-id-1220202', 'server-app-name', 'privKeyOfTheClient');
// Insert the token in HTTP Header, it will be parsed by jwt.verifyHTTPHeaderFn automatically
request.setHeader('Authorization', 'Bearer ' + header);
request.setHeader('Authorization', 'Bearer ' + token); // "Bearer" keyword is optional
```
* On server-side
Or, if your client is a browser, store the JWT in a `cookie` instead of `Authorization` header.
With `ExpressJS`:
```js
// let the browser send it back automatically.
// Do not forget to refresh it before the 12-hour expiration
response.cookie('access_token', token);
```
#### 2) On server-side
```js
var jwt = require('kitten-jwt');

@@ -69,3 +80,4 @@

var _clientId = payload.iss;
// do whatever you want: db query, file read
// do whatever you want: db query, file read to return the public key
// it accepts an array of public key ['pubKeyOfTheClient1', 'pubKeyOfTheClient2']
return callback('pubKeyOfTheClient');

@@ -76,2 +88,3 @@ }

// This function is very fast (uses lru-cache)
// It searches JWT in req.header.authorization, then in req.header.cookie.<access_token>
express().use(jwt.verifyHTTPHeaderFn('server-app-name', getPublicKeyFn));

@@ -130,6 +143,7 @@

Generate a function(req, req, next)<br>
Set req.jwtPayload
Generate a middleware `function(req, req, next)`<br>
Verify and set `req.jwtPayload`
- getPublicKeyFn : Function(req, res, payload, callback) which returns publicKey in callback(pubKey)
- getPublicKeyFn : Function(req, res, payload, callback) which must call the `callback(String|Array)` where
the parameter is either a string (one public key) or an array of strings (mutliple public key to test)
- serverId : JWT audience, token.aud

@@ -164,2 +178,14 @@ if the token is invalid, next(err) is called. Thus you can catch the error in another 4-parameter middlewares.

## CHANGELOG
**1.0.0**
- Possibility to verify a token with multiple public keys. `getPublicKeyFn` can return an array of public keys
- Increase key cache to 200
- Improve error output
- Accepts token in cookie `access_token`
- Accepts token without key word "Bearer"
## Notes

@@ -169,3 +195,2 @@

- blacklist IP which send too many bad token
- to save extra bandwithh: kitten-jwt accepts and generate tokens with one-letter header instead of RFCs JWT header (optional)

@@ -177,3 +202,2 @@ - make expiration a little bit random

https://github.com/volschin/node-curve25519
https://ianix.com/pub/curve25519-deployment.html
https://ianix.com/pub/curve25519-deployment.html
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