зеркало из https://github.com/mozilla/hawk.git
Merge pull request #141 from jcwilson/master
adding support for receiving credentials.key during server-side nonce verification
This commit is contained in:
Коммит
1e44abb0c3
|
@ -16,7 +16,7 @@ var internals = {};
|
|||
|
||||
/*
|
||||
req: node's HTTP request object or an object as follows:
|
||||
|
||||
|
||||
var request = {
|
||||
method: 'GET',
|
||||
url: '/resource/4?a=1&b=2',
|
||||
|
@ -24,21 +24,21 @@ var internals = {};
|
|||
port: 8080,
|
||||
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="'
|
||||
};
|
||||
|
||||
|
||||
credentialsFunc: required function to lookup the set of Hawk credentials based on the provided credentials id.
|
||||
The credentials include the MAC key, MAC algorithm, and other attributes (such as username)
|
||||
needed by the application. This function is the equivalent of verifying the username and
|
||||
password in Basic authentication.
|
||||
|
||||
|
||||
var credentialsFunc = function (id, callback) {
|
||||
|
||||
|
||||
// Lookup credentials in database
|
||||
db.lookup(id, function (err, item) {
|
||||
|
||||
|
||||
if (err || !item) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
var credentials = {
|
||||
// Required
|
||||
key: item.key,
|
||||
|
@ -46,27 +46,27 @@ var internals = {};
|
|||
// Application specific
|
||||
user: item.user
|
||||
};
|
||||
|
||||
|
||||
return callback(null, credentials);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
options: {
|
||||
|
||||
hostHeaderName: optional header field name, used to override the default 'Host' header when used
|
||||
behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving
|
||||
the original (which is what the module must verify) in the 'x-forwarded-host' header field.
|
||||
Only used when passed a node Http.ServerRequest object.
|
||||
|
||||
nonceFunc: optional nonce validation function. The function signature is function(nonce, ts, callback)
|
||||
|
||||
nonceFunc: optional nonce validation function. The function signature is function(key, nonce, ts, callback)
|
||||
where 'callback' must be called using the signature function(err).
|
||||
|
||||
|
||||
timestampSkewSec: optional number of seconds of permitted clock skew for incoming timestamps. Defaults to 60 seconds.
|
||||
Provides a +/- skew which means actual allowed window is double the number of seconds.
|
||||
|
||||
|
||||
localtimeOffsetMsec: optional local clock time offset express in a number of milliseconds (positive or negative).
|
||||
Defaults to 0.
|
||||
|
||||
|
||||
payload: optional payload for validation. The client calculates the hash value and includes it via the 'hash'
|
||||
header attribute. The server always ensures the value provided has been included in the request
|
||||
MAC. When this option is provided, it validates the hash value itself. Validation is done by calculating
|
||||
|
@ -85,10 +85,10 @@ var internals = {};
|
|||
exports.authenticate = function (req, credentialsFunc, options, callback) {
|
||||
|
||||
callback = Hoek.nextTick(callback);
|
||||
|
||||
|
||||
// Default options
|
||||
|
||||
options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
|
||||
options.nonceFunc = options.nonceFunc || function (key, nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
|
||||
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
|
||||
|
||||
// Application time
|
||||
|
@ -182,7 +182,7 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
|
|||
|
||||
// Check nonce
|
||||
|
||||
options.nonceFunc(attributes.nonce, attributes.ts, function (err) {
|
||||
options.nonceFunc(credentials.key, attributes.nonce, attributes.ts, function (err) {
|
||||
|
||||
if (err) {
|
||||
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials, artifacts);
|
||||
|
@ -325,7 +325,7 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
|
|||
|
||||
// Extract bewit
|
||||
|
||||
// 1 2 3 4
|
||||
// 1 2 3 4
|
||||
var resource = request.url.match(/^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/);
|
||||
if (!resource) {
|
||||
return callback(Boom.unauthorized(null, 'Hawk'));
|
||||
|
@ -445,10 +445,10 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
|
|||
exports.authenticateMessage = function (host, port, message, authorization, credentialsFunc, options, callback) {
|
||||
|
||||
callback = Hoek.nextTick(callback);
|
||||
|
||||
|
||||
// Default options
|
||||
|
||||
options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
|
||||
options.nonceFunc = options.nonceFunc || function (key, nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
|
||||
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
|
||||
|
||||
// Application time
|
||||
|
@ -456,13 +456,13 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
|
|||
var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
|
||||
|
||||
// Validate authorization
|
||||
|
||||
|
||||
if (!authorization.id ||
|
||||
!authorization.ts ||
|
||||
!authorization.nonce ||
|
||||
!authorization.hash ||
|
||||
!authorization.mac) {
|
||||
|
||||
|
||||
return callback(Boom.badRequest('Invalid authorization'))
|
||||
}
|
||||
|
||||
|
@ -514,7 +514,7 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
|
|||
|
||||
// Check nonce
|
||||
|
||||
options.nonceFunc(authorization.nonce, authorization.ts, function (err) {
|
||||
options.nonceFunc(credentials.key, authorization.nonce, authorization.ts, function (err) {
|
||||
|
||||
if (err) {
|
||||
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials);
|
||||
|
|
|
@ -137,7 +137,7 @@ describe('Hawk', function () {
|
|||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
|
||||
expect(auth).to.exist();
|
||||
|
||||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { nonceFunc: function (nonce, ts, callback) { callback (new Error('kaboom')); } }, function (err, credentials) {
|
||||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { nonceFunc: function (key, nonce, ts, callback) { callback (new Error('kaboom')); } }, function (err, credentials) {
|
||||
|
||||
expect(err).to.exist();
|
||||
expect(err.message).to.equal('Invalid nonce');
|
||||
|
|
|
@ -190,13 +190,13 @@ describe('Hawk', function () {
|
|||
var memoryCache = {};
|
||||
var options = {
|
||||
localtimeOffsetMsec: 1353788437000 - Hawk.utils.now(),
|
||||
nonceFunc: function (nonce, ts, callback) {
|
||||
nonceFunc: function (key, nonce, ts, callback) {
|
||||
|
||||
if (memoryCache[nonce]) {
|
||||
if (memoryCache[key + nonce]) {
|
||||
return callback(new Error());
|
||||
}
|
||||
|
||||
memoryCache[nonce] = true;
|
||||
memoryCache[key + nonce] = true;
|
||||
return callback();
|
||||
}
|
||||
};
|
||||
|
@ -215,6 +215,72 @@ describe('Hawk', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not error on nonce collision if keys differ', function (done) {
|
||||
|
||||
var reqSteve = {
|
||||
method: 'GET',
|
||||
url: '/resource/4?filter=a',
|
||||
host: 'example.com',
|
||||
port: 8080,
|
||||
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="bXx7a7p1h9QYQNZ8x7QhvDQym8ACgab4m3lVSFn4DBw=", ext="hello"'
|
||||
};
|
||||
|
||||
var reqBob = {
|
||||
method: 'GET',
|
||||
url: '/resource/4?filter=a',
|
||||
host: 'example.com',
|
||||
port: 8080,
|
||||
authorization: 'Hawk id="456", ts="1353788437", nonce="k3j4h2", mac="LXfmTnRzrLd9TD7yfH+4se46Bx6AHyhpM94hLCiNia4=", ext="hello"'
|
||||
};
|
||||
|
||||
var credentialsFunc = function (id, callback) {
|
||||
|
||||
var credentials = {
|
||||
'123': {
|
||||
id: id,
|
||||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
|
||||
algorithm: (id === '1' ? 'sha1' : 'sha256'),
|
||||
user: 'steve'
|
||||
},
|
||||
'456': {
|
||||
id: id,
|
||||
key: 'xrunpaw3489ruxnpa98w4rxnwerxhqb98rpaxn39848',
|
||||
algorithm: (id === '1' ? 'sha1' : 'sha256'),
|
||||
user: 'bob'
|
||||
}
|
||||
};
|
||||
|
||||
return callback(null, credentials[id]);
|
||||
};
|
||||
|
||||
var memoryCache = {};
|
||||
var options = {
|
||||
localtimeOffsetMsec: 1353788437000 - Hawk.utils.now(),
|
||||
nonceFunc: function (key, nonce, ts, callback) {
|
||||
|
||||
if (memoryCache[key + nonce]) {
|
||||
return callback(new Error());
|
||||
}
|
||||
|
||||
memoryCache[key + nonce] = true;
|
||||
return callback();
|
||||
}
|
||||
};
|
||||
|
||||
Hawk.server.authenticate(reqSteve, credentialsFunc, options, function (err, credentials, artifacts) {
|
||||
|
||||
expect(err).to.not.exist();
|
||||
expect(credentials.user).to.equal('steve');
|
||||
|
||||
Hawk.server.authenticate(reqBob, credentialsFunc, options, function (err, credentials, artifacts) {
|
||||
|
||||
expect(err).to.not.exist();
|
||||
expect(credentials.user).to.equal('bob');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('errors on an invalid authentication header: wrong scheme', function (done) {
|
||||
|
||||
var req = {
|
||||
|
@ -970,6 +1036,21 @@ describe('Hawk', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('errors on nonce collision', function (done) {
|
||||
|
||||
credentialsFunc('123456', function (err, credentials) {
|
||||
|
||||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
|
||||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {nonceFunc: function (key, nonce, ts, nonceCallback) { nonceCallback(true); }}, function (err, credentials) {
|
||||
|
||||
expect(err).to.exist();
|
||||
expect(err.message).to.equal('Invalid nonce');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#authenticatePayloadHash', function () {
|
||||
|
|
Загрузка…
Ссылка в новой задаче