From 83d8844b7090d008d3b98c0446f7d10b750acb14 Mon Sep 17 00:00:00 2001 From: Ryan Graham Date: Wed, 14 Jan 2015 13:31:30 -0800 Subject: [PATCH 1/3] tests: fix Bearer token test --- test/access-token.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/access-token.test.js b/test/access-token.test.js index e20521a8..574a6fa1 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -34,7 +34,7 @@ describe('loopback.token(options)', function() { token = 'Bearer ' + new Buffer(token).toString('base64'); createTestAppAndRequest(this.token, done) .get('/') - .set('authorization', this.token.id) + .set('authorization', token) .expect(200) .end(done); }); From fbb091e3b3f87c5090480284d681ac9a38409a03 Mon Sep 17 00:00:00 2001 From: Ryan Graham Date: Wed, 14 Jan 2015 14:10:20 -0800 Subject: [PATCH 2/3] Extend AccessToken to parse Basic auth headers Allow convenient URLs for curl and browsers such as: - http://some-long-token@localhost:3000/ - http://token:some-long-token@localhost:3000/ Basic Auth specifies a 'Basic' scheme for the Authorization header similar to how OAuth specifies 'Bearer' as an auth scheme. Following a similar convention, extract the access token from the Authorization header when it specifies the 'Basic' scheme, assuming it is the larger of the : segments. --- common/models/access-token.js | 14 ++++++++++++ test/access-token.test.js | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/common/models/access-token.js b/common/models/access-token.js index 4e01a33d..9c5b3285 100644 --- a/common/models/access-token.js +++ b/common/models/access-token.js @@ -189,6 +189,20 @@ module.exports = function(AccessToken) { // Decode from base64 var buf = new Buffer(id, 'base64'); id = buf.toString('utf8'); + } else if (/^Basic /i.test(id)) { + id = id.substring(6); + id = (new Buffer(id, 'base64')).toString('utf8'); + // The spec says the string is user:pass, so if we see both parts + // we will assume the longer of the two is the token, so we will + // extract "a2b2c3" from: + // "a2b2c3" + // "a2b2c3:" (curl http://a2b2c3@localhost:3000/) + // "token:a2b2c3" (curl http://token:a2b2c3@localhost:3000/) + // ":a2b2c3" + var parts = /^([^:]*):(.*)$/.exec(id); + if (parts) { + id = parts[2].length > parts[1].length ? parts[2] : parts[1]; + } } return id; } diff --git a/test/access-token.test.js b/test/access-token.test.js index 574a6fa1..2f3c535f 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -39,6 +39,48 @@ describe('loopback.token(options)', function() { .end(done); }); + describe('populating req.toen 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'); + createTestAppAndRequest(this.token, done) + .get('/') + .set('authorization', this.token.id) + .expect(200) + .end(done); + }); + + it('parses "token-and-empty-password:"', function(done) { + var token = this.token.id + ':'; + token = 'Basic ' + new Buffer(token).toString('base64'); + createTestAppAndRequest(this.token, done) + .get('/') + .set('authorization', this.token.id) + .expect(200) + .end(done); + }); + + it('parses "ignored-user:token-is-password"', function(done) { + var token = 'username:' + this.token.id; + token = 'Basic ' + new Buffer(token).toString('base64'); + createTestAppAndRequest(this.token, done) + .get('/') + .set('authorization', this.token.id) + .expect(200) + .end(done); + }); + + it('parses "token-is-username:ignored-password"', function(done) { + var token = this.token.id + ':password'; + token = 'Basic ' + new Buffer(token).toString('base64'); + createTestAppAndRequest(this.token, done) + .get('/') + .set('authorization', this.token.id) + .expect(200) + .end(done); + }); + }); + it('should populate req.token from a secure cookie', function(done) { var app = createTestApp(this.token, done); From 389b8c0e83727bebd6e006b91f3d166b4badee6f Mon Sep 17 00:00:00 2001 From: Ryan Graham Date: Thu, 15 Jan 2015 22:51:53 -0800 Subject: [PATCH 3/3] test: use 127.0.0.1 instead of localhost More portable for dual-stack environments where localhost may mean ::1, which is the IPv6 equivalent of 127.0.0.1, but not actually the same. --- test/e2e/remote-connector.e2e.js | 2 +- test/e2e/replication.e2e.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/remote-connector.e2e.js b/test/e2e/remote-connector.e2e.js index a1f3332c..1a342471 100644 --- a/test/e2e/remote-connector.e2e.js +++ b/test/e2e/remote-connector.e2e.js @@ -8,7 +8,7 @@ describe('RemoteConnector', function() { before(function() { // setup the remote connector var ds = loopback.createDataSource({ - url: 'http://localhost:3000/api', + url: 'http://127.0.0.1:3000/api', connector: loopback.Remote }); TestModel.attachTo(ds); diff --git a/test/e2e/replication.e2e.js b/test/e2e/replication.e2e.js index 502059a7..24f6967e 100644 --- a/test/e2e/replication.e2e.js +++ b/test/e2e/replication.e2e.js @@ -11,7 +11,7 @@ describe('Replication', function() { before(function() { // setup the remote connector var ds = loopback.createDataSource({ - url: 'http://localhost:3000/api', + url: 'http://127.0.0.1:3000/api', connector: loopback.Remote }); TestModel.attachTo(ds);