Comparing version 2.1.3 to 2.2.0
/* | ||
HTTP Hawk Authentication Scheme | ||
Copyright (c) 2012-2013, Eran Hammer <eran@hueniverse.com> | ||
MIT Licensed | ||
Copyright (c) 2012-2014, Eran Hammer <eran@hammer.io> | ||
BSD Licensed | ||
*/ | ||
@@ -10,11 +10,7 @@ | ||
var hawk = {}; | ||
var hawk = { | ||
internals: {} | ||
}; | ||
// Export if used as a module | ||
if (typeof module !== "undefined" && module.exports) { | ||
module.exports = hawk; | ||
} | ||
hawk.client = { | ||
@@ -25,3 +21,3 @@ | ||
/* | ||
uri: 'http://example.com/resource?a=b' | ||
uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri() | ||
method: HTTP verb (e.g. 'GET', 'POST') | ||
@@ -71,3 +67,3 @@ options: { | ||
var timestamp = options.timestamp || Math.floor((hawk.utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec); | ||
@@ -82,3 +78,3 @@ // Validate credentials | ||
result.err = 'Invalid credential object'; | ||
result.err = 'Invalid credentials object'; | ||
return result; | ||
@@ -118,3 +114,3 @@ } | ||
if (!artifacts.hash && | ||
options.hasOwnProperty('payload')) { | ||
(options.payload || options.payload === '')) { | ||
@@ -151,3 +147,3 @@ artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
request: object created via 'new XMLHttpRequest()' after response received | ||
artifacts: object recieved from header().artifacts | ||
artifacts: object received from header().artifacts | ||
options: { | ||
@@ -168,7 +164,8 @@ payload: optional payload received | ||
if (getHeader('www-authenticate')) { | ||
var wwwAuthenticate = getHeader('www-authenticate'); | ||
if (wwwAuthenticate) { | ||
// Parse HTTP WWW-Authenticate header | ||
var attributes = hawk.utils.parseAuthorizationHeader(getHeader('www-authenticate'), ['ts', 'tsm', 'error']); | ||
var attributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']); | ||
if (!attributes) { | ||
@@ -190,3 +187,4 @@ return false; | ||
if (!getHeader('server-authorization') && | ||
var serverAuthorization = getHeader('server-authorization'); | ||
if (!serverAuthorization && | ||
!options.required) { | ||
@@ -197,3 +195,3 @@ | ||
var attributes = hawk.utils.parseAuthorizationHeader(getHeader('server-authorization'), ['mac', 'ext', 'hash']); | ||
var attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']); | ||
if (!attributes) { | ||
@@ -221,3 +219,5 @@ return false; | ||
if (!options.hasOwnProperty('payload')) { | ||
if (!options.payload && | ||
options.payload !== '') { | ||
return true; | ||
@@ -248,3 +248,3 @@ } | ||
var timestamp = options.timestamp || Math.floor((hawk.utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec); | ||
@@ -350,3 +350,3 @@ // Validate credentials | ||
hash.update(hawk.utils.parseContentType(contentType) + '\n'); | ||
hash.update(payload || ''); | ||
hash.update(payload); | ||
hash.update('\n'); | ||
@@ -364,21 +364,50 @@ return hash.finalize().toString(CryptoJS.enc.Base64); | ||
hawk.utils = { | ||
// localStorage compatible interface | ||
storage: { // localStorage compatible interface | ||
_cache: {}, | ||
setItem: function (key, value) { | ||
hawk.internals.LocalStorage = function () { | ||
hawk.utils.storage._cache[key] = value; | ||
}, | ||
getItem: function (key) { | ||
this._cache = {}; | ||
this.length = 0; | ||
return hawk.utils.storage._cache[key]; | ||
} | ||
}, | ||
this.getItem = function (key) { | ||
return this._cache.hasOwnProperty(key) ? String(this._cache[key]) : null; | ||
}; | ||
this.setItem = function (key, value) { | ||
this._cache[key] = String(value); | ||
this.length = Object.keys(this._cache).length; | ||
}; | ||
this.removeItem = function (key) { | ||
delete this._cache[key]; | ||
this.length = Object.keys(this._cache).length; | ||
}; | ||
this.clear = function () { | ||
this._cache = {}; | ||
this.length = 0; | ||
}; | ||
this.key = function (i) { | ||
return Object.keys(this._cache)[i || 0]; | ||
}; | ||
}; | ||
hawk.utils = { | ||
storage: new hawk.internals.LocalStorage(), | ||
setStorage: function (storage) { | ||
var ntpOffset = hawk.utils.getNtpOffset() || 0; | ||
var ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset'); | ||
hawk.utils.storage = storage; | ||
hawk.utils.setNtpOffset(ntpOffset); | ||
if (ntpOffset) { | ||
hawk.utils.setNtpOffset(ntpOffset); | ||
} | ||
}, | ||
@@ -399,8 +428,13 @@ | ||
return parseInt(hawk.utils.storage.getItem('hawk_ntp_offset') || '0', 10); | ||
var offset = hawk.utils.storage.getItem('hawk_ntp_offset'); | ||
if (!offset) { | ||
return 0; | ||
} | ||
return parseInt(offset, 10); | ||
}, | ||
now: function () { | ||
now: function (localtimeOffsetMsec) { | ||
return (new Date()).getTime() + hawk.utils.getNtpOffset(); | ||
return Math.floor(((new Date()).getTime() + (localtimeOffsetMsec || 0)) / 1000) + hawk.utils.getNtpOffset(); | ||
}, | ||
@@ -498,13 +532,10 @@ | ||
var uriRegex = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?)(?:#(.*))?)/; | ||
var uriByNumber = uriRegex.exec(input); | ||
var uriByNumber = input.match(uriRegex); | ||
var uri = {}; | ||
var i = 15; | ||
while (i--) { | ||
for (var i = 0, il = keys.length; i < il; ++i) { | ||
uri[keys[i]] = uriByNumber[i] || ''; | ||
} | ||
if (uri.port === null || | ||
uri.port === '') { | ||
if (uri.port === '') { | ||
uri.port = (uri.protocol.toLowerCase() === 'http' ? '80' : (uri.protocol.toLowerCase() === 'https' ? '443' : '')); | ||
@@ -518,2 +549,4 @@ } | ||
// $lab:coverage:off$ | ||
// Based on: Crypto-JS v3.1.2 | ||
@@ -530,2 +563,11 @@ // Copyright (c) 2009-2013, Jeff Mott. All rights reserved. | ||
hawk.crypto.internals = CryptoJS; | ||
hawk.crypto.internals = CryptoJS; | ||
// Export if used as a module | ||
if (module && module.exports) { | ||
module.exports = hawk; | ||
} | ||
// $lab:coverage:on$ |
@@ -63,3 +63,3 @@ // Load modules | ||
var timestamp = options.timestamp || Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec); | ||
@@ -109,3 +109,3 @@ // Validate credentials | ||
if (!artifacts.hash && | ||
options.hasOwnProperty('payload')) { | ||
(options.payload || options.payload === '')) { | ||
@@ -124,3 +124,3 @@ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
(artifacts.hash ? '", hash="' + artifacts.hash : '') + | ||
(hasExt ? '", ext="' + Utils.escapeHeaderAttribute(artifacts.ext) : '') + | ||
(hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') + | ||
'", mac="' + mac + '"'; | ||
@@ -143,3 +143,3 @@ | ||
res: node's response object | ||
artifacts: object recieved from header().artifacts | ||
artifacts: object received from header().artifacts | ||
options: { | ||
@@ -196,3 +196,5 @@ payload: optional payload received | ||
if (!options.hasOwnProperty('payload')) { | ||
if (!options.payload && | ||
options.payload !== '') { | ||
return true; | ||
@@ -253,3 +255,3 @@ } | ||
var now = Utils.now() + (options.localtimeOffsetMsec || 0); | ||
var now = Utils.now(options.localtimeOffsetMsec); | ||
@@ -293,3 +295,3 @@ // Validate credentials | ||
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext; | ||
return Utils.base64urlEncode(bewit); | ||
return Hoek.base64urlEncode(bewit); | ||
}; | ||
@@ -336,3 +338,3 @@ | ||
var timestamp = options.timestamp || Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec); | ||
@@ -339,0 +341,0 @@ // Validate credentials |
@@ -115,5 +115,5 @@ // Load modules | ||
var now = Math.floor((Utils.now() + (localtimeOffsetMsec || 0)) / 1000); | ||
var now = Utils.nowSecs(localtimeOffsetMsec); | ||
var tsm = exports.calculateTsMac(now, credentials); | ||
return { ts: now, tsm: tsm }; | ||
}; |
@@ -87,3 +87,3 @@ // Load modules | ||
callback = Utils.nextTick(callback); | ||
callback = Hoek.nextTick(callback); | ||
@@ -97,3 +97,3 @@ // Default options | ||
var now = Utils.now() + (options.localtimeOffsetMsec || 0); // Measure now before any other processing | ||
var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing | ||
@@ -172,4 +172,4 @@ // Convert node Http request object to a request configuration object | ||
if (options.payload !== null && | ||
options.payload !== undefined) { // '' is valid | ||
if (options.payload || | ||
options.payload === '') { | ||
@@ -286,3 +286,3 @@ if (!attributes.hash) { | ||
if (!artifacts.hash && | ||
options.hasOwnProperty('payload')) { | ||
(options.payload || options.payload === '')) { | ||
@@ -303,3 +303,3 @@ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
header += ', ext="' + Utils.escapeHeaderAttribute(artifacts.ext) + '"'; | ||
header += ', ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) + '"'; | ||
} | ||
@@ -318,7 +318,7 @@ | ||
callback = Utils.nextTick(callback); | ||
callback = Hoek.nextTick(callback); | ||
// Application time | ||
var now = Utils.now() + (options.localtimeOffsetMsec || 0); | ||
var now = Utils.now(options.localtimeOffsetMsec); | ||
@@ -362,3 +362,3 @@ // Convert node Http request object to a request configuration object | ||
var bewitString = Utils.base64urlDecode(resource[3]); | ||
var bewitString = Hoek.base64urlDecode(resource[3]); | ||
if (bewitString instanceof Error) { | ||
@@ -371,5 +371,3 @@ return callback(Boom.badRequest('Invalid bewit encoding')); | ||
var bewitParts = bewitString.split('\\'); | ||
if (!bewitParts || | ||
bewitParts.length !== 4) { | ||
if (bewitParts.length !== 4) { | ||
return callback(Boom.badRequest('Invalid bewit structure')); | ||
@@ -457,3 +455,3 @@ } | ||
callback = Utils.nextTick(callback); | ||
callback = Hoek.nextTick(callback); | ||
@@ -467,3 +465,3 @@ // Default options | ||
var now = Utils.now() + (options.localtimeOffsetMsec || 0); // Measure now before any other processing | ||
var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing | ||
@@ -470,0 +468,0 @@ // Validate authorization |
// Load modules | ||
var Hoek = require('hoek'); | ||
var Sntp = require('sntp'); | ||
@@ -13,26 +12,14 @@ var Boom = require('boom'); | ||
// Import Hoek Utilities | ||
exports.version = function () { | ||
internals.import = function () { | ||
for (var i in Hoek) { | ||
if (Hoek.hasOwnProperty(i)) { | ||
exports[i] = Hoek[i]; | ||
} | ||
} | ||
return require('../package.json').version; | ||
}; | ||
internals.import(); | ||
// Extract host and port from request | ||
// Hawk version | ||
// $1 $2 | ||
internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6) | ||
exports.version = function () { | ||
return exports.loadPackage(__dirname + '/..').version; | ||
}; | ||
// Extract host and port from request | ||
exports.parseHost = function (req, hostHeaderName) { | ||
@@ -46,16 +33,4 @@ | ||
var hostHeaderRegex; | ||
if (hostHeader[0] === '[') { | ||
hostHeaderRegex = /^(?:(?:\r\n)?\s)*(\[[^\]]+\])(?::(\d+))?(?:(?:\r\n)?\s)*$/; // IPv6 | ||
} | ||
else { | ||
hostHeaderRegex = /^(?:(?:\r\n)?\s)*([^:]+)(?::(\d+))?(?:(?:\r\n)?\s)*$/; // IPv4, hostname | ||
} | ||
var hostParts = hostHeader.match(hostHeaderRegex); | ||
if (!hostParts || | ||
hostParts.length !== 3 || | ||
!hostParts[1]) { | ||
var hostParts = hostHeader.match(internals.hostHeaderRegex); | ||
if (!hostParts) { | ||
return null; | ||
@@ -113,8 +88,14 @@ } | ||
exports.now = function () { | ||
exports.now = function (localtimeOffsetMsec) { | ||
return Sntp.now(); | ||
return Sntp.now() + (localtimeOffsetMsec || 0); | ||
}; | ||
exports.nowSecs = function (localtimeOffsetMsec) { | ||
return Math.floor(exports.now(localtimeOffsetMsec) / 1000); | ||
}; | ||
// Parse Hawk HTTP Authorization header | ||
@@ -121,0 +102,0 @@ |
{ | ||
"name": "hawk", | ||
"description": "HTTP Hawk Authentication Scheme", | ||
"version": "2.1.3", | ||
"version": "2.2.0", | ||
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)", | ||
@@ -20,3 +20,3 @@ "contributors": [], | ||
"dependencies": { | ||
"hoek": "1.x.x", | ||
"hoek": "2.x.x", | ||
"boom": "2.x.x", | ||
@@ -27,4 +27,3 @@ "cryptiles": "2.x.x", | ||
"devDependencies": { | ||
"lab": "1.x.x", | ||
"localStorage": "1.0.x" | ||
"lab": "3.x.x" | ||
}, | ||
@@ -31,0 +30,0 @@ "scripts": { |
@@ -6,5 +6,5 @@ ![hawk Logo](https://raw.github.com/hueniverse/hawk/master/images/hawk.png) | ||
Current version: **2.0** | ||
Current version: **2.2** | ||
Note: 2.0 is the same exact protocol as 1.1. The version increment reflects a change in the internal error format | ||
Note: 2.x is the same exact protocol as 1.1. The version increment reflects a change in the internal error format | ||
used by the module and used by the node API. | ||
@@ -11,0 +11,0 @@ |
// Load modules | ||
var Url = require('url'); | ||
var Lab = require('lab'); | ||
@@ -7,3 +8,2 @@ var Hoek = require('hoek'); | ||
var Browser = require('../lib/browser'); | ||
var LocalStorage = require('localStorage'); | ||
@@ -39,3 +39,3 @@ | ||
it('should generate a header then successfully parse it (configuration)', function (done) { | ||
it('generates a header then successfully parse it (configuration)', function (done) { | ||
@@ -64,3 +64,3 @@ var req = { | ||
it('should generate a header then successfully parse it (node request)', function (done) { | ||
it('generates a header then successfully parse it (node request)', function (done) { | ||
@@ -109,3 +109,3 @@ var req = { | ||
it('should generate a header then successfully parse it (browserify)', function (done) { | ||
it('generates a header then successfully parse it (browserify)', function (done) { | ||
@@ -154,5 +154,29 @@ var req = { | ||
it('should generate a header then successfully parse it (no server header options)', function (done) { | ||
it('generates a header then successfully parse it (time offset)', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?filter=a', | ||
host: 'example.com', | ||
port: 8080 | ||
}; | ||
credentialsFunc('123456', function (err, credentials) { | ||
req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', localtimeOffsetMsec: 100000 }).field; | ||
expect(req.authorization).to.exist; | ||
Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 100000 }, function (err, credentials, artifacts) { | ||
expect(err).to.not.exist; | ||
expect(credentials.user).to.equal('steve'); | ||
expect(artifacts.ext).to.equal('some-app-data'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('generates a header then successfully parse it (no server header options)', function (done) { | ||
var req = { | ||
method: 'POST', | ||
@@ -199,3 +223,3 @@ url: '/resource/4?filter=a', | ||
it('should generate a header then successfully parse it (no server header)', function (done) { | ||
it('generates a header then successfully parse it (no server header)', function (done) { | ||
@@ -241,3 +265,3 @@ var req = { | ||
it('should generate a header with stale ts and successfully authenticate on second call', function (done) { | ||
it('generates a header with stale ts and successfully authenticate on second call', function (done) { | ||
@@ -291,3 +315,3 @@ var req = { | ||
it('should generate a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) { | ||
it('generates a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) { | ||
@@ -302,4 +326,7 @@ var req = { | ||
credentialsFunc('123456', function (err, credentials) { | ||
Browser.utils.setStorage(LocalStorage) | ||
var localStorage = new Browser.internals.LocalStorage(); | ||
Browser.utils.setStorage(localStorage) | ||
Browser.utils.setNtpOffset(60 * 60 * 1000); | ||
@@ -325,7 +352,7 @@ var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }); | ||
expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000); | ||
expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000); | ||
expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000); | ||
expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true); | ||
expect(Browser.utils.getNtpOffset()).to.equal(0); | ||
expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(0); | ||
expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(0); | ||
@@ -346,3 +373,3 @@ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field; | ||
it('should generate a header then fails to parse it (missing server header hash)', function (done) { | ||
it('generates a header then fails to parse it (missing server header hash)', function (done) { | ||
@@ -391,3 +418,3 @@ var req = { | ||
it('should generate a header then successfully parse it (with hash)', function (done) { | ||
it('generates a header then successfully parse it (with hash)', function (done) { | ||
@@ -414,3 +441,3 @@ var req = { | ||
it('should generate a header then successfully parse it then validate payload', function (done) { | ||
it('generates a header then successfully parse it then validate payload', function (done) { | ||
@@ -439,3 +466,3 @@ var req = { | ||
it('should generate a header then successfully parse it (app)', function (done) { | ||
it('generates a header then successfully parse it (app)', function (done) { | ||
@@ -463,3 +490,3 @@ var req = { | ||
it('should generate a header then successfully parse it (app, dlg)', function (done) { | ||
it('generates a header then successfully parse it (app, dlg)', function (done) { | ||
@@ -488,3 +515,3 @@ var req = { | ||
it('should generate a header then fail authentication due to bad hash', function (done) { | ||
it('generates a header then fail authentication due to bad hash', function (done) { | ||
@@ -510,3 +537,3 @@ var req = { | ||
it('should generate a header for one resource then fail to authenticate another', function (done) { | ||
it('generates a header for one resource then fail to authenticate another', function (done) { | ||
@@ -538,3 +565,3 @@ var req = { | ||
it('should return a valid authorization header (sha1)', function (done) { | ||
it('returns a valid authorization header (sha1)', function (done) { | ||
@@ -552,3 +579,3 @@ var credentials = { | ||
it('should return a valid authorization header (sha256)', function (done) { | ||
it('returns a valid authorization header (sha256)', function (done) { | ||
@@ -566,3 +593,3 @@ var credentials = { | ||
it('should return a valid authorization header (no ext)', function (done) { | ||
it('returns a valid authorization header (empty payload)', function (done) { | ||
@@ -572,2 +599,15 @@ var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: '' }).field; | ||
expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"404ghL7K+hfyhByKKejFBRGgTjU=\", ext=\"Bazinga!\", mac=\"Bh1sj1DOfFRWOdi3ww52nLCJdBE=\"'); | ||
done(); | ||
}); | ||
it('returns a valid authorization header (no ext)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
@@ -581,12 +621,19 @@ }; | ||
it('should return an empty authorization header on missing options', function (done) { | ||
it('returns a valid authorization header (null ext)', function (done) { | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST').field; | ||
expect(header).to.equal(''); | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field; | ||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="'); | ||
done(); | ||
}); | ||
it('should return an empty authorization header on invalid credentials', function (done) { | ||
it('returns a valid authorization header (uri object)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
@@ -596,19 +643,134 @@ algorithm: 'sha256' | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }).field; | ||
expect(header).to.equal(''); | ||
var uri = Browser.utils.parseUri('https://example.net/somewhere/over/the/rainbow'); | ||
var header = Browser.client.header(uri, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field; | ||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="'); | ||
done(); | ||
}); | ||
it('should return an empty authorization header on invalid algorithm', function (done) { | ||
it('errors on missing options', function (done) { | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST'); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on empty uri', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on invalid uri', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header(4, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on missing method', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', '', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on invalid method', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 5, { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on missing credentials', function (done) { | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credentials object'); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (id)', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credentials object'); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (key)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credentials object'); | ||
done(); | ||
}); | ||
it('errors on invalid algorithm', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'hmac-sha-0' | ||
}; | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }).field; | ||
expect(header).to.equal(''); | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Unknown algorithm'); | ||
done(); | ||
}); | ||
it('uses a pre-calculated payload hash', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var options = { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }; | ||
options.hash = Browser.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field; | ||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="'); | ||
done(); | ||
}); | ||
}); | ||
@@ -618,6 +780,39 @@ | ||
it('should return false on invalid header', function (done) { | ||
it('skips tsm validation when missing ts', function (done) { | ||
var res = { | ||
headers: { | ||
'www-authenticate': 'Hawk error="Stale timestamp"' | ||
}, | ||
getResponseHeader: function (header) { | ||
return res.headers[header.toLowerCase()]; | ||
} | ||
}; | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
ts: 1402135580, | ||
nonce: 'iBRB6t', | ||
method: 'GET', | ||
resource: '/resource/4?filter=a', | ||
host: 'example.com', | ||
port: '8080', | ||
ext: 'some-app-data' | ||
}; | ||
expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true); | ||
done(); | ||
}); | ||
it('returns false on invalid header', function (done) { | ||
var res = { | ||
headers: { | ||
'server-authorization': 'Hawk mac="abc", bad="xyz"' | ||
@@ -635,3 +830,3 @@ }, | ||
it('should return false on invalid mac', function (done) { | ||
it('returns false on invalid mac', function (done) { | ||
@@ -675,3 +870,3 @@ var res = { | ||
it('should return true on ignoring hash', function (done) { | ||
it('returns true on ignoring hash', function (done) { | ||
@@ -715,3 +910,3 @@ var res = { | ||
it('should fail on invalid WWW-Authenticate header format', function (done) { | ||
it('errors on invalid WWW-Authenticate header format', function (done) { | ||
@@ -732,3 +927,3 @@ var res = { | ||
it('should fail on invalid WWW-Authenticate header format', function (done) { | ||
it('errors on invalid WWW-Authenticate header format', function (done) { | ||
@@ -758,4 +953,5 @@ var credentials = { | ||
describe('#message', function () { | ||
it('should generate an authorization then successfully parse it', function (done) { | ||
it('generates an authorization then successfully parse it', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
@@ -775,6 +971,18 @@ | ||
it('should fail on missing host', function (done) { | ||
it('generates an authorization using custom nonce/timestamp', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials, nonce: 'abc123', timestamp: 1398536270957 }); | ||
expect(auth).to.exist; | ||
expect(auth.nonce).to.equal('abc123'); | ||
expect(auth.ts).to.equal(1398536270957); | ||
done(); | ||
}); | ||
}); | ||
it('errors on missing host', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message(null, 8080, 'some message', { credentials: credentials }); | ||
@@ -786,4 +994,64 @@ expect(auth).to.not.exist; | ||
it('should fail on missing credentials', function (done) { | ||
it('errors on invalid host', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message(5, 8080, 'some message', { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on missing port', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 0, 'some message', { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on invalid port', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 'a', 'some message', { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on missing message', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 8080, undefined, { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on null message', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 8080, null, { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on invalid message', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 8080, 5, { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on missing credentials', function (done) { | ||
var auth = Browser.client.message('example.com', 8080, 'some message', {}); | ||
@@ -794,7 +1062,38 @@ expect(auth).to.not.exist; | ||
it('should fail on invalid algorithm', function (done) { | ||
it('errors on missing options', function (done) { | ||
var auth = Browser.client.message('example.com', 8080, 'some message'); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on invalid credentials (id)', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var creds = Hoek.clone(credentials); | ||
delete creds.id; | ||
var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on invalid credentials (key)', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var creds = Hoek.clone(credentials); | ||
delete creds.key; | ||
var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('errors on invalid algorithm', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var creds = Hoek.clone(credentials); | ||
creds.algorithm = 'blah'; | ||
@@ -810,3 +1109,3 @@ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds }); | ||
it('should validate a timestamp', function (done) { | ||
it('validates a timestamp', function (done) { | ||
@@ -821,6 +1120,18 @@ credentialsFunc('123456', function (err, credentials) { | ||
it('should detect a bad timestamp', function (done) { | ||
it('validates a timestamp without updating local time', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var offset = Browser.utils.getNtpOffset(); | ||
var tsm = Hawk.crypto.timestampMessage(credentials, 10000); | ||
expect(Browser.client.authenticateTimestamp(tsm, credentials, false)).to.equal(true); | ||
expect(offset).to.equal(Browser.utils.getNtpOffset()); | ||
done(); | ||
}); | ||
}); | ||
it('detects a bad timestamp', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var tsm = Hawk.crypto.timestampMessage(credentials); | ||
@@ -835,93 +1146,131 @@ tsm.ts = 4; | ||
describe('#parseAuthorizationHeader', function (done) { | ||
describe('internals', function () { | ||
it('returns null on missing header', function (done) { | ||
describe('LocalStorage', function () { | ||
expect(Browser.utils.parseAuthorizationHeader()).to.equal(null); | ||
done(); | ||
it('goes through the full lifecycle', function (done) { | ||
var storage = new Browser.internals.LocalStorage(); | ||
expect(storage.length).to.equal(0); | ||
expect(storage.getItem('a')).to.equal(null); | ||
storage.setItem('a', 5); | ||
expect(storage.length).to.equal(1); | ||
expect(storage.key()).to.equal('a'); | ||
expect(storage.key(0)).to.equal('a'); | ||
expect(storage.getItem('a')).to.equal('5'); | ||
storage.setItem('b', 'test'); | ||
expect(storage.key()).to.equal('a'); | ||
expect(storage.key(0)).to.equal('a'); | ||
expect(storage.key(1)).to.equal('b'); | ||
expect(storage.length).to.equal(2); | ||
expect(storage.getItem('b')).to.equal('test'); | ||
storage.removeItem('a'); | ||
expect(storage.length).to.equal(1); | ||
expect(storage.getItem('a')).to.equal(null); | ||
expect(storage.getItem('b')).to.equal('test'); | ||
storage.clear(); | ||
expect(storage.length).to.equal(0); | ||
expect(storage.getItem('a')).to.equal(null); | ||
expect(storage.getItem('b')).to.equal(null); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('returns null on bad header syntax (structure)', function (done) { | ||
describe('utils', function () { | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null); | ||
done(); | ||
}); | ||
describe('#setStorage', function () { | ||
it('returns null on bad header syntax (parts)', function (done) { | ||
it('sets storage for the first time', function (done) { | ||
expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null); | ||
done(); | ||
Browser.utils.storage = new Browser.internals.LocalStorage(); // Reset state | ||
expect(Browser.utils.storage.getItem('hawk_ntp_offset')).to.not.exist; | ||
Browser.utils.storage.setItem('test', '1'); | ||
Browser.utils.setStorage(new Browser.internals.LocalStorage()); | ||
expect(Browser.utils.storage.getItem('test')).to.not.exist; | ||
Browser.utils.storage.setItem('test', '2'); | ||
expect(Browser.utils.storage.getItem('test')).to.equal('2'); | ||
done(); | ||
}); | ||
}); | ||
it('returns null on bad scheme name', function (done) { | ||
describe('#setNtpOffset', function (done) { | ||
expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null); | ||
done(); | ||
}); | ||
it('catches localStorage errors', function (done) { | ||
it('returns null on bad attribute value', function (done) { | ||
var orig = Browser.utils.storage.setItem; | ||
var error = console.error; | ||
var count = 0; | ||
console.error = function () { if (count++ === 2) { console.error = error; } }; | ||
Browser.utils.storage.setItem = function () { | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null); | ||
done(); | ||
}); | ||
Browser.utils.storage.setItem = orig; | ||
throw new Error() | ||
}; | ||
it('returns null on duplicated attribute', function (done) { | ||
expect(function () { | ||
Browser.utils.setNtpOffset(100); | ||
}).not.to.throw(); | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null); | ||
done(); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#setNtpOffset', function (done) { | ||
describe('#parseAuthorizationHeader', function (done) { | ||
it('catches localStorage errors', function (done) { | ||
it('returns null on missing header', function (done) { | ||
var orig = Browser.utils.storage.setItem; | ||
var error = console.error; | ||
var count = 0; | ||
console.error = function () { if (count++ === 2) { console.error = error; } }; | ||
Browser.utils.storage.setItem = function () { | ||
expect(Browser.utils.parseAuthorizationHeader()).to.equal(null); | ||
done(); | ||
}); | ||
Browser.utils.storage.setItem = orig; | ||
throw new Error() | ||
}; | ||
it('returns null on bad header syntax (structure)', function (done) { | ||
expect(function () { | ||
Browser.utils.setNtpOffset(100); | ||
}).not.to.throw(); | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
it('returns null on bad header syntax (parts)', function (done) { | ||
describe('#parseUri', function () { | ||
expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null); | ||
done(); | ||
}); | ||
it('returns empty port when unknown scheme', function (done) { | ||
it('returns null on bad scheme name', function (done) { | ||
var uri = Browser.utils.parseUri('ftp://domain'); | ||
expect(uri.port).to.equal(''); | ||
done(); | ||
expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null); | ||
done(); | ||
}); | ||
it('returns null on bad attribute value', function (done) { | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null); | ||
done(); | ||
}); | ||
it('returns null on duplicated attribute', function (done) { | ||
expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('CryptoJS', function () { | ||
describe('#parseUri', function () { | ||
it('fills in coverage', function (done) { | ||
it('returns empty port when unknown scheme', function (done) { | ||
Browser.crypto.internals.lib.WordArray.random(1); | ||
var hmac = Browser.crypto.internals.HmacSHA1('a', 'b'); | ||
hmac.toString(Browser.crypto.internals.enc.Hex); | ||
hmac.toString(Browser.crypto.internals.enc.Latin1); | ||
Browser.crypto.internals.enc.Hex.parse('00'); | ||
Browser.crypto.internals.enc.Utf8.stringify('00'); | ||
var uri = Browser.utils.parseUri('ftp://domain'); | ||
expect(uri.port).to.equal(''); | ||
done(); | ||
}); | ||
var hash = Browser.crypto.internals.algo.SHA1.create(); | ||
hash.update('x'); | ||
hash.finalize(); | ||
Browser.crypto.internals.lib.BufferedBlockAlgorithm.clone.call(hash); | ||
Browser.crypto.internals.lib.BufferedBlockAlgorithm._process.call(hash, 1); | ||
it('returns default port when missing', function (done) { | ||
done(); | ||
}) | ||
var uri = Browser.utils.parseUri('http://'); | ||
expect(uri.port).to.equal('80'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -28,3 +28,3 @@ // Load modules | ||
it('should return a valid authorization header (sha1)', function (done) { | ||
it('returns a valid authorization header (sha1)', function (done) { | ||
@@ -42,3 +42,3 @@ var credentials = { | ||
it('should return a valid authorization header (sha256)', function (done) { | ||
it('returns a valid authorization header (sha256)', function (done) { | ||
@@ -56,3 +56,3 @@ var credentials = { | ||
it('should return a valid authorization header (no ext)', function (done) { | ||
it('returns a valid authorization header (no ext)', function (done) { | ||
@@ -70,12 +70,19 @@ var credentials = { | ||
it('should return an empty authorization header on missing options', function (done) { | ||
it('returns a valid authorization header (null ext)', function (done) { | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST').field; | ||
expect(header).to.equal(''); | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field; | ||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="'); | ||
done(); | ||
}); | ||
it('should return an empty authorization header on invalid credentials', function (done) { | ||
it('returns a valid authorization header (empty payload)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
@@ -85,8 +92,8 @@ algorithm: 'sha256' | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }).field; | ||
expect(header).to.equal(''); | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' }).field; | ||
expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", mac=\"U5k16YEzn3UnBHKeBzsDXn067Gu3R4YaY6xOt9PYRZM=\"'); | ||
done(); | ||
}); | ||
it('should return an empty authorization header on invalid algorithm', function (done) { | ||
it('returns a valid authorization header (pre hashed payload)', function (done) { | ||
@@ -96,7 +103,97 @@ var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var options = { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }; | ||
options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field; | ||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="'); | ||
done(); | ||
}); | ||
it('errors on missing uri', function (done) { | ||
var header = Hawk.client.header('', 'POST'); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on invalid uri', function (done) { | ||
var header = Hawk.client.header(4, 'POST'); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on missing method', function (done) { | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', ''); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on invalid method', function (done) { | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 5); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on missing options', function (done) { | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST'); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid argument type'); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (id)', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credential object'); | ||
done(); | ||
}); | ||
it('errors on missing credentials', function (done) { | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credential object'); | ||
done(); | ||
}); | ||
it('errors on invalid credentials', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
algorithm: 'sha256' | ||
}; | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Invalid credential object'); | ||
done(); | ||
}); | ||
it('errors on invalid algorithm', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'hmac-sha-0' | ||
}; | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }).field; | ||
expect(header).to.equal(''); | ||
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }); | ||
expect(header.field).to.equal(''); | ||
expect(header.err).to.equal('Unknown algorithm'); | ||
done(); | ||
@@ -108,3 +205,3 @@ }); | ||
it('should return false on invalid header', function (done) { | ||
it('returns false on invalid header', function (done) { | ||
@@ -121,3 +218,3 @@ var res = { | ||
it('should return false on invalid mac', function (done) { | ||
it('returns false on invalid mac', function (done) { | ||
@@ -157,3 +254,3 @@ var res = { | ||
it('should return true on ignoring hash', function (done) { | ||
it('returns true on ignoring hash', function (done) { | ||
@@ -193,3 +290,3 @@ var res = { | ||
it('should fail on invalid WWW-Authenticate header format', function (done) { | ||
it('fails on invalid WWW-Authenticate header format', function (done) { | ||
@@ -201,3 +298,3 @@ var header = 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"'; | ||
it('should fail on invalid WWW-Authenticate header format', function (done) { | ||
it('fails on invalid WWW-Authenticate header format', function (done) { | ||
@@ -215,4 +312,144 @@ var credentials = { | ||
}); | ||
it('skips tsm validation when missing ts', function (done) { | ||
var header = 'Hawk error="Stale timestamp"'; | ||
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(true); | ||
done(); | ||
}); | ||
}); | ||
describe('#message', function () { | ||
it('generates authorization', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.exist; | ||
expect(auth.ts).to.equal(1353809207); | ||
expect(auth.nonce).to.equal('abc123'); | ||
done(); | ||
}); | ||
it('errors on invalid host', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message(5, 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on invalid port', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on missing host', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 0, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on null message', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, null, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on missing message', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, undefined, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on invalid message', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, 5, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on missing options', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman'); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on invalid credentials (id)', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('errors on invalid credentials (key)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
algorithm: 'sha1' | ||
}; | ||
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -36,3 +36,3 @@ // Load modules | ||
it('should generate a header then successfully parse it (configuration)', function (done) { | ||
it('generates a header then successfully parse it (configuration)', function (done) { | ||
@@ -61,3 +61,3 @@ var req = { | ||
it('should generate a header then successfully parse it (node request)', function (done) { | ||
it('generates a header then successfully parse it (node request)', function (done) { | ||
@@ -102,3 +102,3 @@ var req = { | ||
it('should generate a header then successfully parse it (no server header options)', function (done) { | ||
it('generates a header then successfully parse it (no server header options)', function (done) { | ||
@@ -143,3 +143,3 @@ var req = { | ||
it('should generate a header then fails to parse it (missing server header hash)', function (done) { | ||
it('generates a header then fails to parse it (missing server header hash)', function (done) { | ||
@@ -184,3 +184,3 @@ var req = { | ||
it('should generate a header then successfully parse it (with hash)', function (done) { | ||
it('generates a header then successfully parse it (with hash)', function (done) { | ||
@@ -207,3 +207,3 @@ var req = { | ||
it('should generate a header then successfully parse it then validate payload', function (done) { | ||
it('generates a header then successfully parse it then validate payload', function (done) { | ||
@@ -232,3 +232,3 @@ var req = { | ||
it('should generate a header then successfully parse it (app)', function (done) { | ||
it('generates a header then successfully parses and validates payload', function (done) { | ||
@@ -244,2 +244,24 @@ var req = { | ||
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field; | ||
Hawk.server.authenticate(req, credentialsFunc, { payload: 'hola!' }, function (err, credentials, artifacts) { | ||
expect(err).to.not.exist; | ||
expect(credentials.user).to.equal('steve'); | ||
expect(artifacts.ext).to.equal('some-app-data'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('generates a header then successfully parse it (app)', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?filter=a', | ||
host: 'example.com', | ||
port: 8080 | ||
}; | ||
credentialsFunc('123456', function (err, credentials) { | ||
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased' }).field; | ||
@@ -257,3 +279,3 @@ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) { | ||
it('should generate a header then successfully parse it (app, dlg)', function (done) { | ||
it('generates a header then successfully parse it (app, dlg)', function (done) { | ||
@@ -282,3 +304,3 @@ var req = { | ||
it('should generate a header then fail authentication due to bad hash', function (done) { | ||
it('generates a header then fail authentication due to bad hash', function (done) { | ||
@@ -304,3 +326,3 @@ var req = { | ||
it('should generate a header for one resource then fail to authenticate another', function (done) { | ||
it('generates a header for one resource then fail to authenticate another', function (done) { | ||
@@ -307,0 +329,0 @@ var req = { |
@@ -85,2 +85,17 @@ // Load modules | ||
it('overrides timestampSkewSec', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials, localtimeOffsetMsec: 100000 }); | ||
expect(auth).to.exist; | ||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { timestampSkewSec: 500 }, function (err, credentials) { | ||
expect(err).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should fail authorization on invalid authorization', function (done) { | ||
@@ -87,0 +102,0 @@ |
@@ -40,3 +40,3 @@ // Load modules | ||
it('should parse a valid authentication header (sha1)', function (done) { | ||
it('parses a valid authentication header (sha1)', function (done) { | ||
@@ -59,3 +59,3 @@ var req = { | ||
it('should parse a valid authentication header (sha256)', function (done) { | ||
it('parses a valid authentication header (sha256)', function (done) { | ||
@@ -78,3 +78,3 @@ var req = { | ||
it('should parse a valid authentication header (host override)', function (done) { | ||
it('parses a valid authentication header (host override)', function (done) { | ||
@@ -98,3 +98,3 @@ var req = { | ||
it('should parse a valid authentication header (host port override)', function (done) { | ||
it('parses a valid authentication header (host port override)', function (done) { | ||
@@ -118,3 +118,3 @@ var req = { | ||
it('should parse a valid authentication header (POST with payload)', function (done) { | ||
it('parses a valid authentication header (POST with payload)', function (done) { | ||
@@ -137,3 +137,3 @@ var req = { | ||
it('should fail on missing hash', function (done) { | ||
it('errors on missing hash', function (done) { | ||
@@ -156,3 +156,3 @@ var req = { | ||
it('should fail on a stale timestamp', function (done) { | ||
it('errors on a stale timestamp', function (done) { | ||
@@ -187,3 +187,3 @@ var req = { | ||
it('should fail on a replay', function (done) { | ||
it('errors on a replay', function (done) { | ||
@@ -226,3 +226,3 @@ var req = { | ||
it('should fail on an invalid authentication header: wrong scheme', function (done) { | ||
it('errors on an invalid authentication header: wrong scheme', function (done) { | ||
@@ -245,3 +245,3 @@ var req = { | ||
it('should fail on an invalid authentication header: no scheme', function (done) { | ||
it('errors on an invalid authentication header: no scheme', function (done) { | ||
@@ -264,3 +264,3 @@ var req = { | ||
it('should fail on an missing authorization header', function (done) { | ||
it('errors on an missing authorization header', function (done) { | ||
@@ -282,3 +282,3 @@ var req = { | ||
it('should fail on an missing host header', function (done) { | ||
it('errors on an missing host header', function (done) { | ||
@@ -301,3 +301,3 @@ var req = { | ||
it('should fail on an missing authorization attribute (id)', function (done) { | ||
it('errors on an missing authorization attribute (id)', function (done) { | ||
@@ -320,3 +320,3 @@ var req = { | ||
it('should fail on an missing authorization attribute (ts)', function (done) { | ||
it('errors on an missing authorization attribute (ts)', function (done) { | ||
@@ -339,3 +339,3 @@ var req = { | ||
it('should fail on an missing authorization attribute (nonce)', function (done) { | ||
it('errors on an missing authorization attribute (nonce)', function (done) { | ||
@@ -358,3 +358,3 @@ var req = { | ||
it('should fail on an missing authorization attribute (mac)', function (done) { | ||
it('errors on an missing authorization attribute (mac)', function (done) { | ||
@@ -377,3 +377,3 @@ var req = { | ||
it('should fail on an unknown authorization attribute', function (done) { | ||
it('errors on an unknown authorization attribute', function (done) { | ||
@@ -396,3 +396,3 @@ var req = { | ||
it('should fail on an bad authorization header format', function (done) { | ||
it('errors on an bad authorization header format', function (done) { | ||
@@ -415,3 +415,3 @@ var req = { | ||
it('should fail on an bad authorization attribute value', function (done) { | ||
it('errors on an bad authorization attribute value', function (done) { | ||
@@ -434,3 +434,3 @@ var req = { | ||
it('should fail on an empty authorization attribute value', function (done) { | ||
it('errors on an empty authorization attribute value', function (done) { | ||
@@ -453,3 +453,3 @@ var req = { | ||
it('should fail on duplicated authorization attribute key', function (done) { | ||
it('errors on duplicated authorization attribute key', function (done) { | ||
@@ -472,3 +472,3 @@ var req = { | ||
it('should fail on an invalid authorization header format', function (done) { | ||
it('errors on an invalid authorization header format', function (done) { | ||
@@ -491,3 +491,3 @@ var req = { | ||
it('should fail on an bad host header (missing host)', function (done) { | ||
it('errors on an bad host header (missing host)', function (done) { | ||
@@ -511,3 +511,3 @@ var req = { | ||
it('should fail on an bad host header (pad port)', function (done) { | ||
it('errors on an bad host header (pad port)', function (done) { | ||
@@ -531,3 +531,3 @@ var req = { | ||
it('should fail on credentialsFunc error', function (done) { | ||
it('errors on credentialsFunc error', function (done) { | ||
@@ -555,3 +555,3 @@ var req = { | ||
it('should fail on missing credentials', function (done) { | ||
it('errors on credentialsFunc error (with credentials)', function (done) { | ||
@@ -568,2 +568,26 @@ var req = { | ||
return callback(new Error('Unknown user'), { some: 'value' }); | ||
}; | ||
Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('Unknown user'); | ||
expect(credentials.some).to.equal('value'); | ||
done(); | ||
}); | ||
}); | ||
it('errors on missing credentials', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?filter=a', | ||
host: 'example.com', | ||
port: 8080, | ||
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"' | ||
}; | ||
var credentialsFunc = function (id, callback) { | ||
return callback(null, null); | ||
@@ -580,3 +604,3 @@ }; | ||
it('should fail on invalid credentials', function (done) { | ||
it('errors on invalid credentials (id)', function (done) { | ||
@@ -610,3 +634,3 @@ var req = { | ||
it('should fail on unknown credentials algorithm', function (done) { | ||
it('errors on invalid credentials (key)', function (done) { | ||
@@ -624,2 +648,31 @@ var req = { | ||
var credentials = { | ||
id: '23434d3q4d5345d', | ||
user: 'steve' | ||
}; | ||
return callback(null, credentials); | ||
}; | ||
Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('Invalid credentials'); | ||
expect(err.output.payload.message).to.equal('An internal server error occurred'); | ||
done(); | ||
}); | ||
}); | ||
it('errors on unknown credentials algorithm', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?filter=a', | ||
host: 'example.com', | ||
port: 8080, | ||
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"' | ||
}; | ||
var credentialsFunc = function (id, callback) { | ||
var credentials = { | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
@@ -642,3 +695,3 @@ algorithm: 'hmac-sha-0', | ||
it('should fail on unknown bad mac', function (done) { | ||
it('errors on unknown bad mac', function (done) { | ||
@@ -675,12 +728,121 @@ var req = { | ||
it('should return an empty authorization header on missing options', function (done) { | ||
it('generates header', function (done) { | ||
var header = Hawk.server.header(); | ||
expect(header).to.equal(''); | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal('Hawk mac=\"n14wVJK4cOxAytPUMc5bPezQzuJGl5n7MYXhFQgEKsE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"'); | ||
done(); | ||
}); | ||
it('should return an empty authorization header on missing credentials', function (done) { | ||
it('generates header (empty payload)', function (done) { | ||
var header = Hawk.server.header(null, {}); | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts, { payload: '', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal('Hawk mac=\"i8/kUBDx0QF+PpCtW860kkV/fa9dbwEoe/FpGUXowf0=\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", ext=\"response-specific\"'); | ||
done(); | ||
}); | ||
it('generates header (pre calculated hash)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var options = { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }; | ||
options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType); | ||
var header = Hawk.server.header(credentials, artifacts, options); | ||
expect(header).to.equal('Hawk mac=\"n14wVJK4cOxAytPUMc5bPezQzuJGl5n7MYXhFQgEKsE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"'); | ||
done(); | ||
}); | ||
it('generates header (null ext)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: null }); | ||
expect(header).to.equal('Hawk mac=\"6PrybJTJs20jsgBw5eilXpcytD8kUbaIKNYXL+6g0ns=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\"'); | ||
done(); | ||
}); | ||
it('errors on missing artifacts', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var header = Hawk.server.header(credentials, null, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal(''); | ||
@@ -690,9 +852,12 @@ done(); | ||
it('should return an empty authorization header on invalid credentials', function (done) { | ||
it('errors on invalid artifacts', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q' | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var header = Hawk.server.header(credentials); | ||
var header = Hawk.server.header(credentials, 5, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal(''); | ||
@@ -702,19 +867,143 @@ done(); | ||
it('should return an empty authorization header on invalid algorithm', function (done) { | ||
it('errors on missing credentials', function (done) { | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(null, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (key)', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'hmac-sha-0' | ||
id: '123456', | ||
algorithm: 'sha256', | ||
user: 'steve' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts); | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid algorithm', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', | ||
algorithm: 'x', | ||
user: 'steve' | ||
}; | ||
var artifacts = { | ||
method: 'POST', | ||
host: 'example.com', | ||
port: '8080', | ||
resource: '/resource/4?filter=a', | ||
ts: '1398546787', | ||
nonce: 'xUwusx', | ||
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=', | ||
ext: 'some-app-data', | ||
mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=', | ||
id: '123456' | ||
}; | ||
var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' }); | ||
expect(header).to.equal(''); | ||
done(); | ||
}); | ||
}); | ||
describe('#authenticateMessage', function () { | ||
it('errors on invalid authorization (ts)', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials }); | ||
delete auth.ts; | ||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('Invalid authorization'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('errors on invalid authorization (nonce)', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials }); | ||
delete auth.nonce; | ||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('Invalid authorization'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('errors on invalid authorization (hash)', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials }); | ||
delete auth.hash; | ||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('Invalid authorization'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('errors with credentials', 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, function (id, callback) { callback(new Error('something'), { some: 'value' }); }, {}, function (err, credentials) { | ||
expect(err).to.exist; | ||
expect(err.message).to.equal('something'); | ||
expect(credentials.some).to.equal('value'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('#authenticatePayloadHash', function () { | ||
@@ -721,0 +1010,0 @@ |
144
test/uri.js
// Load modules | ||
var Http = require('http'); | ||
var Url = require('url'); | ||
var Hoek = require('hoek'); | ||
var Lab = require('lab'); | ||
@@ -182,3 +184,3 @@ var Hawk = require('../lib'); | ||
req.url += '&bewit=' + Hawk.utils.base64urlEncode(bewit); | ||
req.url += '&bewit=' + Hoek.base64urlEncode(bewit); | ||
@@ -300,2 +302,19 @@ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) { | ||
it('should fail on missing bewit id attribute', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?bewit=XDQ1NTIxNDc2MjJcK0JFbFhQMXhuWjcvd1Nrbm1ldGhlZm5vUTNHVjZNSlFVRHk4NWpTZVJ4VT1cc29tZS1hcHAtZGF0YQ', | ||
host: 'example.com', | ||
port: 8080 | ||
}; | ||
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) { | ||
expect(err).to.exist; | ||
expect(err.output.payload.message).to.equal('Missing bewit attributes'); | ||
done(); | ||
}); | ||
}); | ||
it('should fail on expired access', function (done) { | ||
@@ -335,2 +354,20 @@ | ||
it('should fail on credentials function error with credentials', function (done) { | ||
var req = { | ||
method: 'GET', | ||
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ', | ||
host: 'example.com', | ||
port: 8080 | ||
}; | ||
Hawk.uri.authenticate(req, function (id, callback) { callback(Hawk.error.badRequest('Boom'), { some: 'value' }); }, {}, function (err, credentials, attributes) { | ||
expect(err).to.exist; | ||
expect(err.output.payload.message).to.equal('Boom'); | ||
expect(credentials.some).to.equal('value'); | ||
done(); | ||
}); | ||
}); | ||
it('should fail on null credentials function response', function (done) { | ||
@@ -407,3 +444,3 @@ | ||
it('should return a valid bewit value', function (done) { | ||
it('returns a valid bewit value', function (done) { | ||
@@ -421,5 +458,6 @@ var credentials = { | ||
it('should return an empty bewit on invalid credentials', function (done) { | ||
it('returns a valid bewit value (explicit port)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
@@ -429,2 +467,79 @@ algorithm: 'sha256' | ||
var bewit = Hawk.uri.getBewit('https://example.com:8080/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' }); | ||
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6'); | ||
done(); | ||
}); | ||
it('returns a valid bewit value (null ext)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null }); | ||
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c'); | ||
done(); | ||
}); | ||
it('returns a valid bewit value (parsed uri)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit(Url.parse('https://example.com/somewhere/over/the/rainbow'), { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' }); | ||
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6'); | ||
done(); | ||
}); | ||
it('errors on invalid options', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', 4); | ||
expect(bewit).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on missing uri', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit('', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' }); | ||
expect(bewit).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid uri', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit(5, { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' }); | ||
expect(bewit).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (id)', function (done) { | ||
var credentials = { | ||
key: '2983d45yun89q', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' }); | ||
@@ -435,6 +550,25 @@ expect(bewit).to.equal(''); | ||
it('should return an empty bewit on invalid algorithm', function (done) { | ||
it('errors on missing credentials', function (done) { | ||
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' }); | ||
expect(bewit).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid credentials (key)', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
algorithm: 'sha256' | ||
}; | ||
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' }); | ||
expect(bewit).to.equal(''); | ||
done(); | ||
}); | ||
it('errors on invalid algorithm', function (done) { | ||
var credentials = { | ||
id: '123456', | ||
key: '2983d45yun89q', | ||
@@ -449,3 +583,3 @@ algorithm: 'hmac-sha-0' | ||
it('should return an empty bewit on missing options', function (done) { | ||
it('errors on missing options', function (done) { | ||
@@ -452,0 +586,0 @@ var credentials = { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
333808
1
4481
- Removedhoek@1.5.2(transitive)
Updatedhoek@2.x.x