Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}
36 changes: 36 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
"browser": true, // Standard browser globals e.g. `window`, `document`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
"curly": false, // Require {} for every new block or scope.
"eqeqeq": true, // Require triple equals i.e. `===`.
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef": true, // Prohibit variable use before definition.
"newcap": true, // Require capitalization of all constructor functions e.g. `new F()`.
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"quotmark": "single", // Define quotes to string values.
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
"undef": true, // Require all non-global variables be declared before they are used.
"unused": true, // Warn unused variables.
"strict": true, // Require `use strict` pragma in every file.
"trailing": true, // Prohibit trailing whitespaces.
"smarttabs": false, // Suppresses warnings about mixed tabs and spaces
"globals": { // Globals variables.
"_": true,
"$": true
},
"predef": [ // Extra globals.
"require",
"exports",
"module",
"describe",
"it",
"after",
"before"
],
"indent": 4, // Specify indentation spacing
"devel": true, // Allow development statements e.g. `console.log();`.
"noempty": true // Prohibit use of empty blocks.
}
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ This module supports a few initialization parameters that can be used to support
* **maxAge** - The maximum age (in seconds) of a session.

```
var JWTRedisSession = require("jwt-redis-session"),
express = require("express"),
redis = require("redis");
const JWTRedisSession = require('jwt-redis-session'),
express = require('express'),
redis = require('redis');

var redisClient = redis.createClient(),
const redisClient = redis.createClient(),
secret = generateSecretKeySomehow(),
app = express();

app.use(JWTRedisSession({
client: redisClient,
secret: secret,
keyspace: "sess:",
keyspace: 'sess:',
maxAge: 86400,
algorithm: "HS256",
requestKey: "jwtSession",
requestArg: "jwtToken"
algorithm: 'HS256',
requestKey: 'jwtSession',
requestArg: 'jwtToken'
}));
```

Expand All @@ -57,15 +57,15 @@ Create a new JSON Web Token from the provided claims and store any relevant data

```
var handleRequest = function(req, res){
User.login(req.param("username"), req.param("password"), function(error, user){
User.login(req.param('username'), req.param('password'), function(error, user){

// this will be stored in redis
req.jwtSession.user = user.toJSON();

// this will be attached to the JWT
var claims = {
iss: "my application name",
aud: "myapplication.com"
iss: 'my application name',
aud: 'myapplication.com'
};

req.jwtSession.create(claims, function(error, token){
Expand All @@ -82,9 +82,9 @@ var handleRequest = function(req, res){
The session's UUID, JWT claims, and the JWT itself are all available on the jwtSession object as well. Any of these properties can be used to test for the existence of a valid JWT and session.

```
var handleRequest = function(req, res){
const handleRequest = function(req, res){

console.log("Request JWT session data: ",
console.log('Request JWT session data: ',
req.jwtSession.id,
req.jwtSession.claims,
req.jwtSession.jwt
Expand All @@ -100,18 +100,18 @@ var handleRequest = function(req, res){
Any modifications to the jwtSession will be reflected in redis.

```
var handleRequest = function(req, res){
const handleRequest = function(req, res){

if(req.jwtSession.id){

req.jwtSession.foo = "bar";
req.jwtSession.foo = 'bar';

req.jwtSession.update(function(error){
res.json(req.jwtSession.toJSON());
});

}else{
res.redirect("/login");
res.redirect('/login');
}
};
```
Expand All @@ -121,7 +121,7 @@ var handleRequest = function(req, res){
Force a reload of the session data from redis.

```
var handleRequest = function(req, res){
const handleRequest = function(req, res){

setTimeout(function(){

Expand All @@ -137,7 +137,7 @@ var handleRequest = function(req, res){
## Refresh the TTL on a Session

```
var handleRequest = function(req, res){
const handleRequest = function(req, res){

req.jwtSession.touch(function(error){
res.json(req.jwtSession.toJSON());
Expand All @@ -151,10 +151,10 @@ var handleRequest = function(req, res){
Remove the session data from redis. The user's JWT may still be valid within its expiration window, but the backing data in redis will no longer exist. This module will not recognize the JWT when this is the case.

```
var handleRequest = function(req, res){
const handleRequest = function(req, res){

req.jwtSession.destroy(function(error){
res.redirect("/login");
res.redirect('/login');
});

};
Expand Down
5 changes: 3 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';

var path = require("path");
const path = require('path');

module.exports = require(path.join(__dirname, "lib/jwtRedisSession"));
module.exports = require(path.join(__dirname, 'lib/jwtRedisSession'));
92 changes: 47 additions & 45 deletions lib/jwtRedisSession.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,68 @@
'use strict';

const jwt = require('jsonwebtoken'),
{ promisify } = require('util'),
utils = require('./utils'),
JWTVerify = promisify(jwt.verify).bind(jwt);

var _ = require("lodash"),
jwt = require("jsonwebtoken"),
utils = require("./utils");

module.exports = function(options){

if(!options.client || !options.secret)
throw new Error("Redis client and secret required for jwtRedisSession!");
module.exports = (options) => {
if (!options.client || !options.secret) {
throw new Error('Redis client and secret required for jwtRedisSession!');
}

options = {
client: options.client,
secret: options.secret,
algorithm: options.algorithm || "HS256",
keyspace: options.keyspace || "sess:",
algorithm: options.algorithm || 'HS256',
keyspace: options.keyspace || 'sess:',
maxAge: options.maxAge || 86400,
requestKey: options.requestKey || "session",
requestArg: options.requestArg || "accessToken"
requestKey: options.requestKey || 'session',
requestArg: options.requestArg || 'accessToken'
};

var sessionMethods = utils(options);
const sessionMethods = utils(options);
const RedisGet = promisify(options.client.get).bind(options.client);

var requestHeader = _.reduce(options.requestArg.split(""), function(memo, ch){
return memo + (ch.toUpperCase() === ch ? "-" + ch.toLowerCase() : ch);
}, "x" + (options.requestArg.charAt(0) === options.requestArg.charAt(0).toUpperCase() ? "" : "-"));
const requestHeader = options.requestArg.split('').reduce((memo, ch) => {
return memo + (ch.toUpperCase() === ch ? '-' + ch.toLowerCase() : ch);
}, 'x' + (options.requestArg.charAt(0) === options.requestArg.charAt(0).toUpperCase() ? '' : '-'));

return function(req, res, next){
return async (req, res, next) => {

req[options.requestKey] = {};

_.extend(req[options.requestKey], sessionMethods);
req[options.requestKey] = Object.assign(sessionMethods);

var token = req.param ? req.param(options.requestArg) : false;
let token = req.params ? req.params[options.requestArg] : false;
token = token || (req.body ? req.body[options.requestArg] : false);
token = token || (req.query ? req.query[options.requestArg] : false);
token = token || req.headers[requestHeader];
token = token || (req.cookies ? req.cookies[requestHeader] : false);

if(token){
jwt.verify(token, options.secret, function(error, decoded){
if(error || !decoded.jti)
return next();

options.client.get(options.keyspace + decoded.jti, function(err, session){
if(err || !session)
return next();

try{
session = JSON.parse(session);
}catch(e){
return next();
}

_.extend(req[options.requestKey], session);
req[options.requestKey].claims = decoded;
req[options.requestKey].id = decoded.jti;
req[options.requestKey].jwt = token;
req[options.requestKey].touch(); // update the TTL
next();
});
});
}else{
next();
if (token) {
try {
const decoded = await JWTVerify(token, options.secret);
if (!(decoded && decoded.jti)) {
throw '';
}

let session = await RedisGet(options.keyspace + decoded.jti);
if (!session) {
throw '';
}
session = JSON.parse(session);

req[options.requestKey] = Object.assign(req[options.requestKey], session);
req[options.requestKey].claims = decoded;
req[options.requestKey].id = decoded.jti;
req[options.requestKey].jwt = token;
req[options.requestKey].touch(); // update the TTL
return next();
} catch (error) {
return next();
}
} else {
return next();
}
};

};
Loading