diff --git a/server/middleware/token.js b/server/middleware/token.js index e80eb560..146c75d1 100644 --- a/server/middleware/token.js +++ b/server/middleware/token.js @@ -62,6 +62,8 @@ function escapeRegExp(str) { * @property {Array} [headers] Array of header names. * @property {Array} [params] Array of param names. * @property {Boolean} [searchDefaultTokenKeys] Use the default search locations for Token in request + * @property {Boolean} [enableDoublecheck] Execute middleware although an instance mounted earlier in the chain didn't find a token + * @property {Boolean} [overwriteExistingToken] only has effect in combination with `enableDoublecheck`. If truthy, will allow to overwrite an existing accessToken. * @property {Function|String} [model] AccessToken model name or class to use. * @property {String} [currentUserLiteral] String literal for the current user. * @header loopback.token([options]) @@ -80,6 +82,9 @@ function token(options) { currentUserLiteral = escapeRegExp(currentUserLiteral); } + var enableDoublecheck = !!options.enableDoublecheck; + var overwriteExistingToken = !!options.overwriteExistingToken; + return function(req, res, next) { var app = req.app; var registry = app.registry; @@ -97,8 +102,19 @@ function token(options) { 'loopback.token() middleware requires a AccessToken model'); if (req.accessToken !== undefined) { - rewriteUserLiteral(req, currentUserLiteral); - return next(); + if (!enableDoublecheck) { + // req.accessToken is defined already (might also be "null" or "false") and enableDoublecheck + // has not been set --> skip searching for credentials + rewriteUserLiteral(req, currentUserLiteral); + return next(); + } + if (req.accessToken && req.accessToken.id && !overwriteExistingToken) { + // req.accessToken.id is defined, which means that some other middleware has identified a valid user. + // when overwriteExistingToken is not set to a truthy value, skip searching for credentials. + rewriteUserLiteral(req, currentUserLiteral); + return next(); + } + // continue normal operation (as if req.accessToken was undefined) } TokenModel.findForRequest(req, options, function(err, token) { req.accessToken = token || null; diff --git a/test/access-token.test.js b/test/access-token.test.js index 9e57cba2..a2ddbdb0 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -65,7 +65,7 @@ describe('loopback.token(options)', function() { .end(done); }); - describe('populating req.toen from HTTP Basic Auth formatted authorization header', function() { + describe('populating req.token from HTTP Basic Auth formatted authorization header', function() { it('parses "standalone-token"', function(done) { var token = this.token.id; token = 'Basic ' + new Buffer(token).toString('base64'); @@ -199,6 +199,90 @@ describe('loopback.token(options)', function() { done(); }); }); + + describe('loading multiple instances of token middleware', function() { + it('should skip when req.token is already present and no further options are set', + function(done) { + var tokenStub = { id: 'stub id' }; + app.use(function(req, res, next) { + req.accessToken = tokenStub; + next(); + }); + app.use(loopback.token({ model: Token })); + app.get('/', function(req, res, next) { + res.send(req.accessToken); + }); + + request(app).get('/') + .set('Authorization', this.token.id) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + expect(res.body).to.eql(tokenStub); + done(); + }); + }); + + it('should not overwrite valid existing token (has "id" property) ' + + ' when overwriteExistingToken is falsy', + function(done) { + var tokenStub = { id: 'stub id' }; + app.use(function(req, res, next) { + req.accessToken = tokenStub; + next(); + }); + app.use(loopback.token({ + model: Token, + enableDoublecheck: true, + })); + app.get('/', function(req, res, next) { + res.send(req.accessToken); + }); + + request(app).get('/') + .set('Authorization', this.token.id) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + expect(res.body).to.eql(tokenStub); + done(); + }); + }); + + it('should overwrite existing token when enableDoublecheck ' + + 'and overwriteExistingToken options are truthy', + function(done) { + var token = this.token; + var tokenStub = { id: 'stub id' }; + + app.use(function(req, res, next) { + req.accessToken = tokenStub; + next(); + }); + app.use(loopback.token({ + model: Token, + enableDoublecheck: true, + overwriteExistingToken: true, + })); + app.get('/', function(req, res, next) { + res.send(req.accessToken); + }); + + request(app).get('/') + .set('Authorization', token.id) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + expect(res.body).to.eql({ + id: token.id, + ttl: token.ttl, + userId: token.userId, + created: token.created.toJSON(), + }); + done(); + }); + }); + }); }); describe('AccessToken', function() {