hawk
Advanced tools
Comparing version 0.13.1 to 0.14.0
@@ -0,0 +0,0 @@ // Load modules |
@@ -19,33 +19,33 @@ /* | ||
// Generate an Authorization header for a given request | ||
hawk.client = { | ||
/* | ||
uri: 'http://example.com/resource?a=b' | ||
method: HTTP verb (e.g. 'GET', 'POST') | ||
options: { | ||
// Generate an Authorization header for a given request | ||
// Required | ||
/* | ||
uri: 'http://example.com/resource?a=b' | ||
method: HTTP verb (e.g. 'GET', 'POST') | ||
options: { | ||
// Required | ||
credentials: { | ||
id: 'dh37fgj492je', | ||
key: 'aoijedoaijsdlaksjdl', | ||
algorithm: 'sha256' // 'sha1', 'sha256' | ||
}, | ||
// Optional | ||
ext: 'application-specific', // Application specific data sent via the ext attribute | ||
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds | ||
nonce: '2334f34f', // A pre-generated nonce | ||
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided) | ||
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided) | ||
contentType: 'application/json', // Payload content-type (ignored if hash provided) | ||
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash | ||
app: '24s23423f34dx', // Oz application id | ||
dlg: '234sz34tww3sd' // Oz delegated-by application id | ||
} | ||
*/ | ||
credentials: { | ||
id: 'dh37fgj492je', | ||
key: 'aoijedoaijsdlaksjdl', | ||
algorithm: 'sha256' // 'sha1', 'sha256' | ||
}, | ||
// Optional | ||
ext: 'application-specific', // Application specific data sent via the ext attribute | ||
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds | ||
nonce: '2334f34f', // A pre-generated nonce | ||
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided) | ||
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided) | ||
contentType: 'application/json', // Payload content-type (ignored if hash provided) | ||
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash | ||
app: '24s23423f34dx', // Oz application id | ||
dlg: '234sz34tww3sd' // Oz delegated-by application id | ||
} | ||
*/ | ||
hawk.client = { | ||
header: function (uri, method, options) { | ||
@@ -216,2 +216,57 @@ | ||
return (calculatedHash === attributes.hash); | ||
}, | ||
message: function (host, port, message, options) { | ||
// Validate inputs | ||
if (!host || typeof host !== 'string' || | ||
!port || typeof port !== 'number' || | ||
message === null || message === undefined || typeof message !== 'string' || | ||
!options || typeof options !== 'object') { | ||
return null; | ||
} | ||
// Application time | ||
var timestamp = options.timestamp || Math.floor((hawk.utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
// Validate credentials | ||
var credentials = options.credentials; | ||
if (!credentials || | ||
!credentials.id || | ||
!credentials.key || | ||
!credentials.algorithm) { | ||
// Invalid credential object | ||
return null; | ||
} | ||
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) { | ||
return null; | ||
} | ||
// Calculate signature | ||
var artifacts = { | ||
ts: timestamp, | ||
nonce: options.nonce || hawk.utils.randomString(6), | ||
host: host, | ||
port: port, | ||
hash: hawk.crypto.calculatePayloadHash(message, credentials.algorithm) | ||
}; | ||
// Construct authorization | ||
var result = { | ||
id: credentials.id, | ||
ts: artifacts.ts, | ||
nonce: artifacts.nonce, | ||
hash: artifacts.hash, | ||
mac: hawk.crypto.calculateMac('message', credentials, artifacts) | ||
}; | ||
return result; | ||
} | ||
@@ -240,4 +295,4 @@ }; | ||
options.nonce + '\n' + | ||
options.method.toUpperCase() + '\n' + | ||
options.resource + '\n' + | ||
(options.method || '').toUpperCase() + '\n' + | ||
(options.resource || '') + '\n' + | ||
options.host.toLowerCase() + '\n' + | ||
@@ -244,0 +299,0 @@ options.port + '\n' + |
@@ -286,1 +286,83 @@ // Load modules | ||
// Generate an authorization string for a message | ||
/* | ||
host: 'example.com', | ||
port: 8000, | ||
message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation | ||
options: { | ||
// Required | ||
credentials: { | ||
id: 'dh37fgj492je', | ||
key: 'aoijedoaijsdlaksjdl', | ||
algorithm: 'sha256' // 'sha1', 'sha256' | ||
}, | ||
// Optional | ||
timestamp: Date.now(), // A pre-calculated timestamp | ||
nonce: '2334f34f', // A pre-generated nonce | ||
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided) | ||
} | ||
*/ | ||
exports.message = function (host, port, message, options) { | ||
// Validate inputs | ||
if (!host || typeof host !== 'string' || | ||
!port || typeof port !== 'number' || | ||
message === null || message === undefined || typeof message !== 'string' || | ||
!options || typeof options !== 'object') { | ||
return null; | ||
} | ||
// Application time | ||
var timestamp = options.timestamp || Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000) | ||
// Validate credentials | ||
var credentials = options.credentials; | ||
if (!credentials || | ||
!credentials.id || | ||
!credentials.key || | ||
!credentials.algorithm) { | ||
// Invalid credential object | ||
return null; | ||
} | ||
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) { | ||
return null; | ||
} | ||
// Calculate signature | ||
var artifacts = { | ||
ts: timestamp, | ||
nonce: options.nonce || Cryptiles.randomString(6), | ||
host: host, | ||
port: port, | ||
hash: Crypto.calculatePayloadHash(message, credentials.algorithm) | ||
}; | ||
// Construct authorization | ||
var result = { | ||
id: credentials.id, | ||
ts: artifacts.ts, | ||
nonce: artifacts.nonce, | ||
hash: artifacts.hash, | ||
mac: Crypto.calculateMac('message', credentials, artifacts) | ||
}; | ||
return result; | ||
}; | ||
@@ -60,4 +60,4 @@ // Load modules | ||
options.nonce + '\n' + | ||
options.method.toUpperCase() + '\n' + | ||
options.resource + '\n' + | ||
(options.method || '').toUpperCase() + '\n' + | ||
(options.resource || '') + '\n' + | ||
options.host.toLowerCase() + '\n' + | ||
@@ -64,0 +64,0 @@ options.port + '\n' + |
@@ -292,3 +292,3 @@ // Load modules | ||
/* | ||
* Arguments and options are the same as index.js with the exception that the only supported options are: | ||
* Arguments and options are the same as authenticate() with the exception that the only supported options are: | ||
* 'hostHeaderName', 'localtimeOffsetMsec' | ||
@@ -426,1 +426,98 @@ */ | ||
}; | ||
/* | ||
* options are the same as authenticate() with the exception that the only supported options are: | ||
* 'nonceFunc', 'timestampSkewSec', 'localtimeOffsetMsec' | ||
*/ | ||
exports.authenticateMessage = function (host, port, message, authorization, credentialsFunc, options, callback) { | ||
callback = Utils.nextTick(callback); | ||
// Default options | ||
options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation | ||
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds | ||
// Application time | ||
var now = Utils.now() + (options.localtimeOffsetMsec || 0); // 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')) | ||
} | ||
// Fetch Hawk credentials | ||
credentialsFunc(authorization.id, function (err, credentials) { | ||
if (err) { | ||
return callback(err, credentials || null); | ||
} | ||
if (!credentials) { | ||
return callback(Boom.unauthorized('Unknown credentials', 'Hawk')); | ||
} | ||
if (!credentials.key || | ||
!credentials.algorithm) { | ||
return callback(Boom.internal('Invalid credentials'), credentials); | ||
} | ||
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) { | ||
return callback(Boom.internal('Unknown algorithm'), credentials); | ||
} | ||
// Construct artifacts container | ||
var artifacts = { | ||
ts: authorization.ts, | ||
nonce: authorization.nonce, | ||
host: host, | ||
port: port, | ||
hash: authorization.hash | ||
}; | ||
// Calculate MAC | ||
var mac = Crypto.calculateMac('message', credentials, artifacts); | ||
if (!Cryptiles.fixedTimeComparison(mac, authorization.mac)) { | ||
return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials); | ||
} | ||
// Check payload hash | ||
var hash = Crypto.calculatePayloadHash(message, credentials.algorithm); | ||
if (!Cryptiles.fixedTimeComparison(hash, authorization.hash)) { | ||
return callback(Boom.unauthorized('Bad message hash', 'Hawk'), credentials); | ||
} | ||
// Check nonce | ||
options.nonceFunc(authorization.nonce, authorization.ts, function (err) { | ||
if (err) { | ||
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials); | ||
} | ||
// Check timestamp staleness | ||
if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) { | ||
return callback(Boom.unauthorized('Stale timestamp'), credentials); | ||
} | ||
// Successful authentication | ||
return callback(null, credentials); | ||
}); | ||
}); | ||
}; |
@@ -0,0 +0,0 @@ // Load modules |
{ | ||
"name": "hawk", | ||
"description": "HTTP Hawk Authentication Scheme", | ||
"version": "0.13.1", | ||
"version": "0.14.0", | ||
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)", | ||
@@ -19,3 +19,3 @@ "contributors": [], | ||
"dependencies": { | ||
"hoek": "0.8.x", | ||
"hoek": "0.9.x", | ||
"boom": "0.4.x", | ||
@@ -22,0 +22,0 @@ "cryptiles": "0.2.x", |
@@ -6,3 +6,3 @@ ![hawk Logo](https://raw.github.com/hueniverse/hawk/master/images/hawk.png) | ||
Current version: **0.13** | ||
Current version: **0.14** | ||
@@ -9,0 +9,0 @@ [![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk) |
// Load modules | ||
var Lab = require('lab'); | ||
var Hoek = require('hoek'); | ||
var Hawk = require('../lib'); | ||
@@ -683,2 +684,49 @@ var Browser = require('../lib/browser'); | ||
}); | ||
describe('#message', function () { | ||
it('should generate an authorization then successfully parse it', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials }); | ||
expect(auth).to.exist; | ||
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) { | ||
expect(err).to.not.exist; | ||
expect(credentials.user).to.equal('steve'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should fail on missing host', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var auth = Browser.client.message(null, 8080, 'some message', { credentials: credentials }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('should fail on missing credentials', function (done) { | ||
var auth = Browser.client.message('example.com', 8080, 'some message', {}); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
it('should fail on invalid algorithm', function (done) { | ||
credentialsFunc('123456', function (err, credentials) { | ||
var creds = Hoek.clone(credentials); | ||
creds.algorithm = 'blah'; | ||
var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds }); | ||
expect(auth).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -685,0 +733,0 @@ |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
@@ -0,0 +0,0 @@ // Load modules |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
283884
25
3435
- Removedhoek@0.8.5(transitive)
Updatedhoek@0.9.x