From 7adefd6ee0534eac96ff13f25048eb344bb5fb03 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 30 Mar 2015 14:14:32 +0100 Subject: [PATCH] Add access_token expiry --- red/api/auth/index.js | 2 +- red/api/auth/strategies.js | 2 +- red/api/auth/tokens.js | 40 ++++++++++++++++++++++++-- test/red/api/auth/tokens_spec.js | 48 +++++++++++++++++++++++++++----- 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/red/api/auth/index.js b/red/api/auth/index.js index 6c0938468..9ba45aee3 100644 --- a/red/api/auth/index.js +++ b/red/api/auth/index.js @@ -38,7 +38,7 @@ function init(_settings,storage) { settings = _settings; if (settings.adminAuth) { Users.init(settings.adminAuth); - Tokens.init(storage); + Tokens.init(settings.adminAuth,storage); } } diff --git a/red/api/auth/strategies.js b/red/api/auth/strategies.js index 5f3e2ccdf..294089f54 100644 --- a/red/api/auth/strategies.js +++ b/red/api/auth/strategies.js @@ -86,7 +86,7 @@ var passwordTokenExchange = function(client, username, password, scope, done) { }); Tokens.create(username,client.id,scope).then(function(tokens) { // TODO: audit log - done(null,tokens.accessToken); + done(null,tokens.accessToken,null,{expires_in:tokens.expires_in}); }); } else { done(null,false); diff --git a/red/api/auth/tokens.js b/red/api/auth/tokens.js index 32f20a88f..38a38a7c1 100644 --- a/red/api/auth/tokens.js +++ b/red/api/auth/tokens.js @@ -27,30 +27,66 @@ function generateToken(length) { var storage; + +var sessionExpiryTime + var sessions = {}; +function expireSessions() { + var now = Date.now(); + var modified = false; + for (var t in sessions) { + if (sessions.hasOwnProperty(t)) { + var session = sessions[t]; + if (!session.hasOwnProperty("expires") || session.expires < now) { + delete sessions[t]; + modified = true; + } + } + } + if (modified) { + return storage.saveSessions(sessions); + } else { + return when.resolve(); + } +} + module.exports = { - init: function(_storage) { + init: function(adminAuthSettings, _storage) { storage = _storage; + + sessionExpiryTime = adminAuthSettings.sessionExpiryTime || 604800; // 1 week in seconds + return storage.getSessions().then(function(_sessions) { - sessions = _sessions||{}; + sessions = _sessions||{}; + return expireSessions(); }); }, get: function(token) { + if (sessions[token]) { + if (sessions[token].expires < Date.now()) { + return expireSessions().then(function() { return null }); + } + } return when.resolve(sessions[token]); }, create: function(user,client,scope) { var accessToken = generateToken(128); + + var accessTokenExpiresAt = Date.now() + (sessionExpiryTime*1000); + var session = { user:user, client:client, scope:scope, accessToken: accessToken, + expires: accessTokenExpiresAt }; sessions[accessToken] = session; return storage.saveSessions(sessions).then(function() { return { accessToken: accessToken, + expires_in: sessionExpiryTime } }); }, diff --git a/test/red/api/auth/tokens_spec.js b/test/red/api/auth/tokens_spec.js index 2be83a250..b7496cde8 100644 --- a/test/red/api/auth/tokens_spec.js +++ b/test/red/api/auth/tokens_spec.js @@ -24,7 +24,7 @@ var Tokens = require("../../../../red/api/auth/tokens"); describe("Tokens", function() { describe("#init",function() { it('loads sessions', function(done) { - Tokens.init({ + Tokens.init({},{ getSessions:function() { done(); return when.resolve(); @@ -36,9 +36,9 @@ describe("Tokens", function() { describe("#get",function() { it('returns a valid token', function(done) { - Tokens.init({ + Tokens.init({},{ getSessions:function() { - return when.resolve({"1234":{"user":"fred"}}); + return when.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); } }).then(function() { Tokens.get("1234").then(function(token) { @@ -53,7 +53,7 @@ describe("Tokens", function() { }); it('returns null for an invalid token', function(done) { - Tokens.init({ + Tokens.init({},{ getSessions:function() { return when.resolve({}); } @@ -68,12 +68,41 @@ describe("Tokens", function() { }); }); }); + it('returns null for an expired token', function(done) { + var saveSessions = sinon.stub().returns(when.resolve()); + var expiryTime = Date.now()+50; + Tokens.init({},{ + getSessions:function() { + return when.resolve({"1234":{"user":"fred","expires":expiryTime}}); + }, + saveSessions: saveSessions + }).then(function() { + Tokens.get("1234").then(function(token) { + try { + should.exist(token); + setTimeout(function() { + Tokens.get("1234").then(function(token) { + try { + should.not.exist(token); + saveSessions.calledOnce.should.be.true; + done(); + } catch(err) { + done(err); + } + }); + },100); + } catch(err) { + done(err); + } + }); + }); + }); }); describe("#create",function() { it('creates a token', function(done) { var savedSession; - Tokens.init({ + Tokens.init({sessionExpiryTime: 10},{ getSessions:function() { return when.resolve({}); }, @@ -82,6 +111,9 @@ describe("Tokens", function() { return when.resolve(); } }); + var expectedExpiryTime = Date.now()+10000; + + Tokens.create("user","client","scope").then(function(token) { try { should.exist(savedSession); @@ -92,6 +124,8 @@ describe("Tokens", function() { savedSession[sessionKeys[0]].should.have.a.property('user','user'); savedSession[sessionKeys[0]].should.have.a.property('client','client'); savedSession[sessionKeys[0]].should.have.a.property('scope','scope'); + savedSession[sessionKeys[0]].should.have.a.property('expires'); + savedSession[sessionKeys[0]].expires.should.be.within(expectedExpiryTime-200,expectedExpiryTime+200); done(); } catch(err) { done(err); @@ -103,9 +137,9 @@ describe("Tokens", function() { describe("#revoke", function() { it('revokes a token', function(done) { var savedSession; - Tokens.init({ + Tokens.init({},{ getSessions:function() { - return when.resolve({"1234":{"user":"fred"}}); + return when.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); }, saveSessions:function(sess) { savedSession = sess;