diff --git a/server/middleware/token.js b/server/middleware/token.js index 2ceb701d..58ca056f 100644 --- a/server/middleware/token.js +++ b/server/middleware/token.js @@ -7,6 +7,8 @@ * Module dependencies. */ +'use strict'; +var g = require('strong-globalize')(); var loopback = require('../../lib/loopback'); var assert = require('assert'); var debug = require('debug')('loopback:middleware:token'); @@ -20,18 +22,33 @@ module.exports = token; /* * Rewrite the url to replace current user literal with the logged in user id */ -function rewriteUserLiteral(req, currentUserLiteral) { - if (req.accessToken && req.accessToken.userId && currentUserLiteral) { +function rewriteUserLiteral(req, currentUserLiteral, next) { + if (!currentUserLiteral) return next(); + var literalRegExp = new RegExp('/' + currentUserLiteral + '(/|$|\\?)', 'g'); + + if (req.accessToken && req.accessToken.userId) { // Replace /me/ with /current-user-id/ var urlBeforeRewrite = req.url; - req.url = req.url.replace( - new RegExp('/' + currentUserLiteral + '(/|$|\\?)', 'g'), + req.url = req.url.replace(literalRegExp, '/' + req.accessToken.userId + '$1'); + if (req.url !== urlBeforeRewrite) { debug('req.url has been rewritten from %s to %s', urlBeforeRewrite, req.url); } + } else if (!req.accessToken && literalRegExp.test(req.url)) { + debug( + 'URL %s matches current-user literal %s,' + + ' but no (valid) access token was provided.', + req.url, currentUserLiteral); + + var e = new Error(g.f('Authorization Required')); + e.status = e.statusCode = 401; + e.code = 'AUTHORIZATION_REQUIRED'; + return next(e); } + + next(); } function escapeRegExp(str) { @@ -110,23 +127,24 @@ function token(options) { 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(); + return rewriteUserLiteral(req, currentUserLiteral, 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(); + return rewriteUserLiteral(req, currentUserLiteral, next); } // continue normal operation (as if req.accessToken was undefined) } + TokenModel.findForRequest(req, options, function(err, token) { req.accessToken = token || null; - rewriteUserLiteral(req, currentUserLiteral); + var ctx = req.loopbackContext; if (ctx && ctx.active) ctx.set('accessToken', token); - next(err); + + if (err) return next(err); + rewriteUserLiteral(req, currentUserLiteral, next); }); }; } diff --git a/test/access-token.test.js b/test/access-token.test.js index 9bf2d0b8..7e4363eb 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -190,6 +190,26 @@ describe('loopback.token(options)', function() { }); }); + it('should generate a 401 on a current user literal route without an authToken', + function(done) { + var app = createTestApp(null, done); + request(app) + .get('/users/me') + .set('authorization', null) + .expect(401) + .end(done); + }); + + it('should generate a 401 on a current user literal route with invalid authToken', + function(done) { + var app = createTestApp(this.token, done); + request(app) + .get('/users/me') + .set('Authorization', 'invald-token-id') + .expect(401) + .end(done); + }); + it('should skip when req.token is already present', function(done) { var tokenStub = { id: 'stub id' }; app.use(function(req, res, next) {