Skip to content

Commit

Permalink
fix: some request properties are not being passed to hawk in Node16
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dafortune committed Feb 25, 2022
1 parent e979df1 commit de33706
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 132 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/*
node_modules/*
package-lock.json
44 changes: 27 additions & 17 deletions lib/strategy.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Module dependencies.
*/
var passport = require('passport'),
util = require('util'),
hawk = require('hawk');
var passport = require("passport"),
util = require("util"),
hawk = require("hawk");

var xtend = require('xtend');
var xtend = require("xtend");

/**
* `Strategy` constructor.
Expand Down Expand Up @@ -36,15 +36,18 @@ var xtend = require('xtend');
* @api public
*/
function Strategy(bewit, verify) {
if (typeof bewit == 'function') {
if (typeof bewit == "function") {
verify = bewit;
bewit = false;
}
if (typeof bewit == 'object') {
if (typeof bewit == "object") {
bewit = bewit.bewit;
}

if (!verify) throw new Error('HTTP Hawk authentication strategy requires a verify function');
if (!verify)
throw new Error(
"HTTP Hawk authentication strategy requires a verify function"
);
this.verify = verify;
this.bewit = bewit;
passport.Strategy.call(this);
Expand All @@ -62,19 +65,26 @@ util.inherits(Strategy, passport.Strategy);
* @param {Object} req
* @api protected
*/
Strategy.prototype.authenticate = function(req, opts) {
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 });

var authenticate = this.bewit ? 'authenticateBewit' : 'authenticate';
hawk.server[authenticate](req, this.verify, opts || {}, function(err, credentials, ext) {
if (err) {
if (err.isMissing) return this.fail('Missing authentication tokens');
return this.error(err); // Return hawk error
}
return this.success(credentials.user, ext);
}.bind(this));
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) {
if (err) {
if (err.isMissing) return this.fail("Missing authentication tokens");
return this.error(err); // Return hawk error
}
return this.success(credentials.user, ext);
}.bind(this)
);
};

/**
Expand Down
145 changes: 89 additions & 56 deletions test/bewit.tests.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,135 @@
var HawkStrategy = require('../lib/strategy'),
Hawk = require('hawk'),
should = require('should');
var HawkStrategy = require("../lib/strategy"),
Hawk = require("hawk"),
should = require("should"),
buildRequest = require("./reqMock").buildRequest;

var credentials = {
key: 'abcd',
algorithm: 'sha256',
user: 'tito',
id: 'dasd123'
key: "abcd",
algorithm: "sha256",
user: "tito",
id: "dasd123",
};

var strategy = new HawkStrategy({ bewit: true }, function(id, done) {
var strategy = new HawkStrategy({ bewit: true }, function (id, done) {
if (id === credentials.id) return done(null, credentials);
return done(null, null);
});

describe("passport-hawk with bewit", function () {
it("can authenticate a request with a correct header", 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: "/resource/4?filter=a&bewit=" + bewit,
});

describe('passport-hawk with bewit', function() {
strategy.success = function (user) {
user.should.eql("tito");
testDone();
};

it('can authenticate a request with a correct header', function(testDone) {
var bewit = Hawk.uri.getBewit('http://example.com:8080/resource/4?filter=a', {
credentials: credentials,
ttlSec: 60 * 5
});
var req = {
strategy.error = function () {
testDone(new Error(arguments));
};
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'
host: "example.com:8080",
},
method: 'GET',
url: '/resource/4?filter=a&bewit=' + bewit
};
method: "GET",
url: "/abc",
originalUrl: "/resource/4?filter=a&bewit=" + bewit,
});

strategy.success = function (user) {
req.url.should.eql("/abc");

strategy.success = function(user) {
user.should.eql('tito');
testDone();
};

strategy.error = function() {
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 = {
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 = buildRequest({
headers: {
host: 'example.com:8080'
host: "example.com:8080",
},
method: 'GET',
url: '/resource/4?filter=a&bewit=' + bewit
};
strategy.error = function(challenge) {
challenge.message.should.eql('Bad mac');
method: "GET",
url: "/resource/4?filter=a&bewit=" + bewit,
});
strategy.error = function (challenge) {
challenge.message.should.eql("Bad mac");
testDone();
};
strategy.authenticate(req);
});

it('should call done with false when the id doesnt exist', function(testDone) {
var bewit = Hawk.uri.getBewit('http://example.com:8080/foobar', {
it("should call done with false when the id doesnt exist", function (testDone) {
var bewit = Hawk.uri.getBewit("http://example.com:8080/foobar", {
credentials: {
id: '321321',
key: 'dsa',
algorithm: 'sha256'
id: "321321",
key: "dsa",
algorithm: "sha256",
},
ttlSec: 60 * 5
ttlSec: 60 * 5,
});

var req = {
var req = buildRequest({
headers: {
host: 'example.com:8080'
host: "example.com:8080",
},
method: 'GET',
url: '/resource/4?filter=a&bewit=' + bewit
};
method: "GET",
url: "/resource/4?filter=a&bewit=" + bewit,
});

strategy.error = function(challenge) {
challenge.message.should.eql('Unknown credentials');
strategy.error = function (challenge) {
challenge.message.should.eql("Unknown credentials");
testDone();
};
strategy.authenticate(req);
});

it('should call fail when url doesnt have a bewit', function(testDone) {

var req = {
it("should call fail when url doesnt have a bewit", function (testDone) {
var req = buildRequest({
headers: {
host: 'example.com:8080'
host: "example.com:8080",
},
method: 'GET',
url: '/resource/4?filter=a'
};
method: "GET",
url: "/resource/4?filter=a",
});

strategy.fail = function(failure) {
failure.should.eql('Missing authentication tokens');
strategy.fail = function (failure) {
failure.should.eql("Missing authentication tokens");
testDone();
};
strategy.authenticate(req);
Expand Down
Loading

0 comments on commit de33706

Please sign in to comment.