Merge pull request #2108 from benkroeger/hotfix/issue-2106
Allow built-in token middleware to run repeatedly
This commit is contained in:
commit
a9c7801380
|
@ -62,6 +62,8 @@ function escapeRegExp(str) {
|
||||||
* @property {Array} [headers] Array of header names.
|
* @property {Array} [headers] Array of header names.
|
||||||
* @property {Array} [params] Array of param names.
|
* @property {Array} [params] Array of param names.
|
||||||
* @property {Boolean} [searchDefaultTokenKeys] Use the default search locations for Token in request
|
* @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 {Function|String} [model] AccessToken model name or class to use.
|
||||||
* @property {String} [currentUserLiteral] String literal for the current user.
|
* @property {String} [currentUserLiteral] String literal for the current user.
|
||||||
* @header loopback.token([options])
|
* @header loopback.token([options])
|
||||||
|
@ -80,6 +82,9 @@ function token(options) {
|
||||||
currentUserLiteral = escapeRegExp(currentUserLiteral);
|
currentUserLiteral = escapeRegExp(currentUserLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enableDoublecheck = !!options.enableDoublecheck;
|
||||||
|
var overwriteExistingToken = !!options.overwriteExistingToken;
|
||||||
|
|
||||||
return function(req, res, next) {
|
return function(req, res, next) {
|
||||||
var app = req.app;
|
var app = req.app;
|
||||||
var registry = app.registry;
|
var registry = app.registry;
|
||||||
|
@ -97,8 +102,19 @@ function token(options) {
|
||||||
'loopback.token() middleware requires a AccessToken model');
|
'loopback.token() middleware requires a AccessToken model');
|
||||||
|
|
||||||
if (req.accessToken !== undefined) {
|
if (req.accessToken !== undefined) {
|
||||||
rewriteUserLiteral(req, currentUserLiteral);
|
if (!enableDoublecheck) {
|
||||||
return next();
|
// 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) {
|
TokenModel.findForRequest(req, options, function(err, token) {
|
||||||
req.accessToken = token || null;
|
req.accessToken = token || null;
|
||||||
|
|
|
@ -65,7 +65,7 @@ describe('loopback.token(options)', function() {
|
||||||
.end(done);
|
.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) {
|
it('parses "standalone-token"', function(done) {
|
||||||
var token = this.token.id;
|
var token = this.token.id;
|
||||||
token = 'Basic ' + new Buffer(token).toString('base64');
|
token = 'Basic ' + new Buffer(token).toString('base64');
|
||||||
|
@ -199,6 +199,90 @@ describe('loopback.token(options)', function() {
|
||||||
done();
|
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() {
|
describe('AccessToken', function() {
|
||||||
|
|
Loading…
Reference in New Issue