From f0ab195d846e5eece174066ae4c7efef71613b9e Mon Sep 17 00:00:00 2001 From: Damian Fortuna Date: Fri, 25 Feb 2022 20:26:16 -0300 Subject: [PATCH] fix: some request properties are not being passed to hawk in Node16 Due to nodejs/node#36550 (it was reverted in Node 14 but it is back in Node 16). req.headers and other properties are not own properties of requests so they are not cloned. Node documentation (see discussion on nodejs/node#36550) advices against clonning the req (or any stream object). This PR fixes that without modifying the original request object by creating a new object which prototype is the original request object instead of trying to clone it. --- .gitignore | 4 +++- lib/strategy.js | 5 ++--- package.json | 3 +-- test/bewit.tests.js | 48 +++++++++++++++++++++++++++++++++++--------- test/header.tests.js | 23 +++++++++++---------- test/reqMock.js | 9 +++++++++ 6 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 test/reqMock.js diff --git a/.gitignore b/.gitignore index dbf0821..f119d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules/* \ No newline at end of file +node_modules/* +package-lock.json +.vscode/* \ No newline at end of file diff --git a/lib/strategy.js b/lib/strategy.js index 621650b..6df924e 100644 --- a/lib/strategy.js +++ b/lib/strategy.js @@ -5,8 +5,6 @@ var passport = require('passport'), util = require('util'), hawk = require('hawk'); -var xtend = require('xtend'); - /** * `Strategy` constructor. * @@ -65,7 +63,8 @@ util.inherits(Strategy, passport.Strategy); Strategy.prototype.authenticate = function(req, opts) { //express change req.url when mounting with app.use //this creates a new request object with url = originalUrl - req = xtend({}, req, { url: req.originalUrl || req.url }); + req = Object.create(req); + req.url = req.originalUrl || req.url; var authenticate = this.bewit ? 'authenticateBewit' : 'authenticate'; hawk.server[authenticate](req, this.verify, opts || {}, function(err, credentials, ext) { diff --git a/package.json b/package.json index 7cf8fb6..84a79e3 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,7 @@ "license": "MIT", "dependencies": { "hawk": "jfromaniello/hawk", - "passport": "^0.3.0", - "xtend": "^4.0.1" + "passport": "^0.3.0" }, "devDependencies": { "mocha": "^2.3.3", diff --git a/test/bewit.tests.js b/test/bewit.tests.js index c3cb3e9..0ac8be2 100644 --- a/test/bewit.tests.js +++ b/test/bewit.tests.js @@ -1,6 +1,7 @@ var HawkStrategy = require('../lib/strategy'), Hawk = require('hawk'), - should = require('should'); + should = require('should'), + buildRequest = require('./reqMock').buildRequest; var credentials = { key: 'abcd', @@ -22,13 +23,13 @@ describe('passport-hawk with bewit', function() { credentials: credentials, ttlSec: 60 * 5 }); - var req = { + var req = buildRequest({ headers: { host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a&bewit=' + bewit - }; + }); strategy.success = function(user) { user.should.eql('tito'); @@ -41,18 +42,47 @@ describe('passport-hawk with bewit', function() { strategy.authenticate(req); }); + it('does not modifies req.url when is available', function (testDone) { + var bewit = Hawk.uri.getBewit( + 'http://example.com:8080/resource/4?filter=a', + { + credentials: credentials, + ttlSec: 60 * 5, + } + ); + var req = buildRequest({ + headers: { + host: 'example.com:8080', + }, + method: 'GET', + url: '/abc', + originalUrl: '/resource/4?filter=a&bewit=' + bewit, + }); + + strategy.success = function (user) { + req.url.should.eql('/abc'); + + testDone(); + }; + + strategy.error = function () { + testDone(new Error(arguments)); + }; + strategy.authenticate(req); + }); + it('should properly fail with correct challenge code when using different url', function(testDone) { var bewit = Hawk.uri.getBewit('http://example.com:8080/resource/4?filter=a' + bewit, { credentials: credentials, ttlSec: 60 * 5 }); - var req = { + var req = buildRequest({ headers: { host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a&bewit=' + bewit - }; + }); strategy.error = function(challenge) { challenge.message.should.eql('Bad mac'); testDone(); @@ -70,13 +100,13 @@ describe('passport-hawk with bewit', function() { ttlSec: 60 * 5 }); - var req = { + var req = buildRequest({ headers: { host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a&bewit=' + bewit - }; + }); strategy.error = function(challenge) { challenge.message.should.eql('Unknown credentials'); @@ -87,13 +117,13 @@ describe('passport-hawk with bewit', function() { it('should call fail when url doesnt have a bewit', function(testDone) { - var req = { + var req = buildRequest({ headers: { host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); strategy.fail = function(failure) { failure.should.eql('Missing authentication tokens'); diff --git a/test/header.tests.js b/test/header.tests.js index 61c2382..39fc8f2 100644 --- a/test/header.tests.js +++ b/test/header.tests.js @@ -1,6 +1,7 @@ var HawkStrategy = require('../lib/strategy'), Hawk = require('hawk'), - should = require('should'); + should = require('should'), + buildRequest = require('./reqMock').buildRequest;; var credentials = { key: 'abcd', @@ -17,14 +18,14 @@ var strategy = new HawkStrategy(function(id, done) { describe('passport-hawk', function() { it('can authenticate a request with a correct header', function(testDone) { var header = Hawk.client.header('http://example.com:8080/resource/4?filter=a', 'GET', { credentials: credentials }); - var req = { + var req = buildRequest({ headers: { authorization: header.field, host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); strategy.success = function(user) { user.should.eql('tito'); testDone(); @@ -34,14 +35,14 @@ describe('passport-hawk', function() { it('should properly fail with correct challenge code when using different url', function(testDone) { var header = Hawk.client.header('http://example.com:8080/resource/4?filter=a', 'GET', { credentials: credentials }); - var req = { + var req = buildRequest({ headers: { authorization: header.field, host: 'example.com:9090' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); strategy.error = function(challenge) { challenge.message.should.eql('Bad mac'); testDone(); @@ -56,14 +57,14 @@ describe('passport-hawk', function() { algorithm: 'sha256' } var authHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', 'POST', { credentials: testCredentials }); - var req = { + var req = buildRequest({ headers: { authorization: authHeader.field, host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); strategy.error = function(challenge) { challenge.message.should.eql('Unknown credentials'); @@ -74,14 +75,14 @@ describe('passport-hawk', function() { it('should fail with a stale request', function(testDone) { var fixedHeader = 'Hawk id="dasd123", ts="1366220539", nonce="xVO62D", mac="9x+7TGN6VLRH8zX5PpwewpIzvf+mTt8m7PDQQW2NU/U="'; - var req = { + var req = buildRequest({ headers: { authorization: fixedHeader, host: 'example.com:8080' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); strategy.error = function(challenge) { challenge.message.should.eql('Stale timestamp'); testDone(); @@ -91,14 +92,14 @@ describe('passport-hawk', function() { it('can authenticate a request with options', function(testDone) { var header = Hawk.client.header('https://example.com/resource/4?filter=a', 'GET', { credentials: credentials }); - var req = { + var req = buildRequest({ headers: { authorization: header.field, host: 'example.com:3000' }, method: 'GET', url: '/resource/4?filter=a' - }; + }); var opts = { port: 443 }; strategy.success = function(user) { user.should.eql('tito'); diff --git a/test/reqMock.js b/test/reqMock.js new file mode 100644 index 0000000..d50dde2 --- /dev/null +++ b/test/reqMock.js @@ -0,0 +1,9 @@ +exports.buildRequest = function buildRequest(reqProps) { + const obj = Object.create({ headers: reqProps.headers }); + + return Object.assign(obj, { + method: reqProps.method, + url: reqProps.url, + originalUrl: reqProps.originalUrl, + }); + }; \ No newline at end of file