Allow built-in token middleware to run repeatedly
Add two new options: - When `enableDoublecheck` is true, the middleware will run even if a previous middleware has already set `req.accessToken` (possibly to `null` for anonymous requests) - When `overwriteExistingToken` is true (and `enableDoublecheck` too), the middleware will overwrite `req.accessToken` set by a previous middleware instances.
This commit is contained in:
parent
1e7adb21ae
commit
9e0405de9f
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue