This commit is contained in:
Eran Hammer 2017-10-26 14:23:40 -07:00
Родитель d251edf711
Коммит f72cdeafd1
18 изменённых файлов: 1355 добавлений и 2411 удалений

Просмотреть файл

@ -3,9 +3,9 @@
<img align="right" src="https://raw.github.com/hueniverse/hawk/master/images/logo.png" /> **Hawk** is an HTTP authentication scheme using a message authentication code (MAC) algorithm to provide partial
HTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).
Current version: **6.x**
Current version: **7.x**
Note: 6.x, 5.x, 4.x, 3.x, and 2.x are the same exact protocol as 1.1. The version increments reflect changes in the node API.
Note: the protocol has not changed since version 1.1. The version increments reflect changes in the node API.
[![Build Status](https://travis-ci.org/hueniverse/hawk.svg?branch=master)](https://travis-ci.org/hueniverse/hawk)
@ -105,7 +105,7 @@ const Hawk = require('hawk');
// Credentials lookup function
const credentialsFunc = function (id, callback) {
const credentialsFunc = function (id) {
const credentials = {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
@ -113,32 +113,31 @@ const credentialsFunc = function (id, callback) {
user: 'Steve'
};
return callback(null, credentials);
return credentials;
};
// Create HTTP server
const handler = function (req, res) {
const handler = async function (req, res) {
// Authenticate incoming request
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials, artifacts) => {
const ( credentials, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
// Prepare response
// Prepare response
const payload = (!err ? `Hello ${credentials.user} ${artifacts.ext}` : 'Shoosh!');
const headers = { 'Content-Type': 'text/plain' };
const payload = (!err ? `Hello ${credentials.user} ${artifacts.ext}` : 'Shoosh!');
const headers = { 'Content-Type': 'text/plain' };
// Generate Server-Authorization response header
// Generate Server-Authorization response header
const header = Hawk.server.header(credentials, artifacts, { payload, contentType: headers['Content-Type'] });
headers['Server-Authorization'] = header;
const header = Hawk.server.header(credentials, artifacts, { payload, contentType: headers['Content-Type'] });
headers['Server-Authorization'] = header;
// Send the response back
// Send the response back
res.writeHead(!err ? 200 : 401, headers);
res.end(payload);
});
res.writeHead(!err ? 200 : 401, headers);
res.end(payload);
};
// Start server
@ -171,8 +170,8 @@ const requestOptions = {
// Generate Authorization request header
const header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });
requestOptions.headers.Authorization = header.field;
const { header } = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });
requestOptions.headers.Authorization = header;
// Send authenticated request
@ -385,25 +384,29 @@ const Hawk = require('hawk');
// Credentials lookup function
const credentialsFunc = function (id, callback) {
const credentialsFunc = function (id) {
const credentials = {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
};
return callback(null, credentials);
return credentials;
};
// Create HTTP server
const handler = function (req, res) {
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
res.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' });
res.end(!err ? 'Access granted' : 'Shoosh!');
});
try {
const { credentials, attributes } = await Hawk.uri.authenticate(req, credentialsFunc);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Access granted');
}
catch (err) {
res.writeHead(401, { 'Content-Type': 'text/plain' });
res.end('Shoosh!');
}
};
Http.createServer(handler).listen(8000, 'example.com');

18
dist/browser.js поставляемый Normal file → Executable file
Просмотреть файл

@ -2,7 +2,7 @@
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
Copyright (c) 2012-2017, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
@ -22,14 +22,14 @@ hawk.client = {
uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
// 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)
@ -125,15 +125,15 @@ hawk.client = {
/*
uri: 'http://example.com/resource?a=b'
options: {
// Required
credentials: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/

Просмотреть файл

@ -23,27 +23,38 @@ const internals = {
// Credentials lookup function
const credentialsFunc = function (id, callback) {
const credentialsFunc = function (id) {
return callback(null, internals.credentials[id]);
return internals.credentials[id];
};
// Create HTTP server
const handler = function (req, res) {
const handler = async function (req, res) {
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials, artifacts) => {
try {
const { credentials, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
const payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
const payload = 'Hello ' + credentials.user + ' ' + artifacts.ext;
const headers = {
'Content-Type': 'text/plain',
'Server-Authorization': Hawk.server.header(credentials, artifacts, { payload, contentType: 'text/plain' })
};
res.writeHead(!err ? 200 : 401, headers);
res.writeHead(200, headers);
res.end(payload);
});
}
catch (err) {
const payload = 'Shoosh!';
const headers = {
'Content-Type': 'text/plain',
'Server-Authorization': Hawk.server.header(err.credentials, err.artifacts, { payload, contentType: 'text/plain' })
};
res.writeHead(401, headers);
res.end(payload);
}
};
Http.createServer(handler).listen(8000, '127.0.0.1');
@ -63,30 +74,23 @@ Request('http://127.0.0.1:8000/resource/1?b=1&a=2', (err, response, body) => {
// Send authenticated request
credentialsFunc('dh37fgj492je', (err, credentials) => {
const credentials = credentialsFunc('dh37fgj492je');
const header = Hawk.client.header('http://127.0.0.1:8000/resource/1?b=1&a=2', 'GET', { credentials, ext: 'and welcome!' });
const options = {
uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
method: 'GET',
headers: {
authorization: header.header
}
};
Request(options, (err, response, body) => {
if (err) {
process.exit(1);
}
const header = Hawk.client.header('http://127.0.0.1:8000/resource/1?b=1&a=2', 'GET', { credentials, ext: 'and welcome!' });
const options = {
uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
method: 'GET',
headers: {
authorization: header.field
}
};
Request(options, (err, response, body) => {
if (err) {
process.exit(1);
}
const isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
process.exit(0);
});
const isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
process.exit(0);
});

Просмотреть файл

@ -2,7 +2,7 @@
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
Copyright (c) 2012-2017, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
@ -47,19 +47,13 @@ hawk.client = {
header: function (uri, method, options) {
const result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
!method || typeof method !== 'string' ||
!options || typeof options !== 'object') {
result.err = 'Invalid argument type';
return result;
throw new Error('Invalid argument type');
}
// Application time
@ -74,13 +68,11 @@ hawk.client = {
!credentials.key ||
!credentials.algorithm) {
result.err = 'Invalid credentials object';
return result;
throw new Error('Invalid credentials');
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
throw new Error('Unknown algorithm');
}
// Parse URI
@ -104,8 +96,6 @@ hawk.client = {
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash &&
@ -131,9 +121,7 @@ hawk.client = {
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
return { artifacts, header };
},
// Generate a bewit value for a given URI
@ -168,7 +156,7 @@ hawk.client = {
typeof options !== 'object' ||
!options.ttlSec) {
return '';
throw new Error('Invalid inputs');
}
options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
@ -185,11 +173,11 @@ hawk.client = {
!credentials.key ||
!credentials.algorithm) {
return '';
throw new Error('Invalid credentials');
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
throw new Error('Unknown algorithm');
}
// Parse URI
@ -318,7 +306,7 @@ hawk.client = {
message === null || message === undefined || typeof message !== 'string' ||
!options || typeof options !== 'object') {
return null;
throw new Error('Invalid inputs');
}
// Application time
@ -333,12 +321,11 @@ hawk.client = {
!credentials.key ||
!credentials.algorithm) {
// Invalid credential object
return null;
throw new Error('Invalid credentials');
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
throw new Error('Unknown algorithm');
}
// Calculate signature

Просмотреть файл

@ -3,7 +3,9 @@
// Load modules
const Url = require('url');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
@ -45,19 +47,13 @@ const internals = {};
exports.header = function (uri, method, options) {
const result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
!method || typeof method !== 'string' ||
!options || typeof options !== 'object') {
result.err = 'Invalid argument type';
return result;
throw new Error('Invalid argument type');
}
// Application time
@ -72,13 +68,11 @@ exports.header = function (uri, method, options) {
!credentials.key ||
!credentials.algorithm) {
result.err = 'Invalid credential object';
return result;
throw new Error('Invalid credentials');
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
throw new Error('Unknown algorithm');
}
// Parse URI
@ -102,8 +96,6 @@ exports.header = function (uri, method, options) {
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash &&
@ -118,20 +110,18 @@ exports.header = function (uri, method, options) {
const hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
let header = 'Hawk id="' + credentials.id +
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
(hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') +
'", mac="' + mac + '"';
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
(hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') +
'", mac="' + mac + '"';
if (artifacts.app) {
header = header + ', app="' + artifacts.app +
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
return { header, artifacts };
};
@ -146,44 +136,31 @@ exports.header = function (uri, method, options) {
}
*/
exports.authenticate = function (res, credentials, artifacts, options, callback) {
exports.authenticate = function (res, credentials, artifacts, options = {}) {
artifacts = Hoek.clone(artifacts);
options = options || {};
let wwwAttributes = null;
let serverAuthAttributes = null;
const finalize = function (err) {
if (callback) {
const headers = {
'www-authenticate': wwwAttributes,
'server-authorization': serverAuthAttributes
};
return callback(err, headers);
}
return !err;
};
const result = { headers: {} };
if (res.headers['www-authenticate']) {
// Parse HTTP WWW-Authenticate header
wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
if (wwwAttributes instanceof Error) {
wwwAttributes = null;
return finalize(new Error('Invalid WWW-Authenticate header'));
try {
var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
}
catch (err) {
throw new Error('Invalid WWW-Authenticate header');
}
result.headers['www-authenticate'] = wwwAttributes;
// Validate server timestamp (not used to update clock since it is done via the SNPT client)
if (wwwAttributes.ts) {
const tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return finalize(new Error('Invalid server timestamp hash'));
throw Object.assign(new Error('Invalid server timestamp hash'), result);
}
}
}
@ -193,39 +170,42 @@ exports.authenticate = function (res, credentials, artifacts, options, callback)
if (!res.headers['server-authorization'] &&
!options.required) {
return finalize();
return result;
}
serverAuthAttributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (serverAuthAttributes instanceof Error) {
serverAuthAttributes = null;
return finalize(new Error('Invalid Server-Authorization header'));
try {
var serverAuthAttributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
}
catch (err) {
throw Object.assign(new Error('Invalid Server-Authorization header'), result);
}
result.headers['server-authorization'] = serverAuthAttributes;
artifacts.ext = serverAuthAttributes.ext;
artifacts.hash = serverAuthAttributes.hash;
const mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== serverAuthAttributes.mac) {
return finalize(new Error('Bad response mac'));
throw Object.assign(new Error('Bad response mac'), result);
}
if (!options.payload &&
options.payload !== '') {
return finalize();
return result;
}
if (!serverAuthAttributes.hash) {
return finalize(new Error('Missing response hash attribute'));
throw Object.assign(new Error('Missing response hash attribute'), result);
}
const calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
if (calculatedHash !== serverAuthAttributes.hash) {
return finalize(new Error('Bad response payload mac'));
throw Object.assign(new Error('Bad response payload mac'), result);
}
return finalize();
return result;
};
@ -261,7 +241,7 @@ exports.getBewit = function (uri, options) {
typeof options !== 'object' ||
!options.ttlSec) {
return '';
throw new Error('Invalid inputs');
}
options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
@ -278,11 +258,11 @@ exports.getBewit = function (uri, options) {
!credentials.key ||
!credentials.algorithm) {
return '';
throw new Error('Invalid credentials');
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
throw new Error('Unknown algorithm');
}
// Parse URI
@ -335,16 +315,16 @@ exports.getBewit = function (uri, options) {
}
*/
exports.message = function (host, port, message, options) {
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') {
typeof options !== 'object') {
return null;
throw new Error('Invalid inputs');
}
// Application time
@ -359,12 +339,11 @@ exports.message = function (host, port, message, options) {
!credentials.key ||
!credentials.algorithm) {
// Invalid credential object
return null;
throw new Error('Invalid credentials');
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
throw new Error('Unknown algorithm');
}
// Calculate signature

Просмотреть файл

@ -4,6 +4,7 @@
const Crypto = require('crypto');
const Url = require('url');
const Utils = require('./utils');
@ -126,3 +127,14 @@ exports.timestampMessage = function (credentials, localtimeOffsetMsec) {
const tsm = exports.calculateTsMac(now, credentials);
return { ts: now, tsm };
};
exports.fixedTimeComparison = function (a, b) {
try {
return Crypto.timingSafeEqual(new Buffer(a), new Buffer(b));
}
catch (err) {
return false;
}
};

Просмотреть файл

@ -2,7 +2,6 @@
// Export sub-modules
exports.error = exports.Error = require('boom');
exports.sntp = require('sntp');
exports.server = require('./server');
@ -14,4 +13,3 @@ exports.uri = {
authenticate: exports.server.authenticateBewit,
getBewit: exports.client.getBewit
};

Просмотреть файл

@ -4,7 +4,7 @@
const Boom = require('boom');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
@ -32,25 +32,24 @@ const internals = {};
needed by the application. This function is the equivalent of verifying the username and
password in Basic authentication.
const credentialsFunc = function (id, callback) {
const credentialsFunc = async function (id) {
// Lookup credentials in database
db.lookup(id, function (err, item) {
if (err || !item) {
return callback(err);
}
const item = await db.lookup(id); // Can throw errors
if (!item) {
return null;
}
const credentials = {
// Required
key: item.key,
algorithm: item.algorithm,
// Application specific
user: item.user
};
const credentials = {
// Required
key: item.key,
algorithm: item.algorithm,
// Application specific
user: item.user
};
return callback(null, credentials);
});
return credentials;
};
options: {
@ -60,8 +59,8 @@ const internals = {};
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(key, nonce, ts, callback)
where 'callback' must be called using the signature function(err).
nonceFunc: optional nonce validation function. The function signature is `async function(key, nonce, ts)`
and it must return no value for success or throw an error for invalid state.
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.
@ -75,22 +74,19 @@ const internals = {};
a hash value over the entire payload (assuming it has already be normalized to the same format and
encoding used by the client to calculate the hash on request). If the payload is not available at the time
of authentication, the authenticatePayload() method can be used by passing it the credentials and
attributes.hash returned in the authenticate callback.
attributes.hash returned from authenticate().
host: optional host name override. Only used when passed a node request object.
port: optional port override. Only used when passed a node request object.
}
callback: function (err, credentials, artifacts) { }
Return value: { credentials, artifacts } or throws an error.
*/
exports.authenticate = function (req, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
exports.authenticate = async function (req, credentialsFunc, options = {}) {
// Default options
options.nonceFunc = options.nonceFunc || internals.nonceFunc;
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
// Application time
@ -100,16 +96,10 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
// Convert node Http request object to a request configuration object
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
// Parse HTTP Authorization header
const attributes = Utils.parseAuthorizationHeader(request.authorization);
if (attributes instanceof Error) {
return callback(attributes);
}
// Construct artifacts container
@ -135,73 +125,77 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
!attributes.nonce ||
!attributes.mac) {
return callback(Boom.badRequest('Missing attributes'), null, artifacts);
throw Object.assign(Boom.badRequest('Missing attributes'), { artifacts });
}
// Fetch Hawk credentials
credentialsFunc(attributes.id, (err, credentials) => {
try {
var credentials = await credentialsFunc(attributes.id);
}
catch (err) {
throw Object.assign(err, { artifacts });
}
if (err) {
return callback(err, credentials || null, artifacts);
if (!credentials) {
throw Object.assign(Utils.unauthorized('Unknown credentials'), { artifacts });
}
const result = { credentials, artifacts };
if (!credentials.key ||
!credentials.algorithm) {
throw Object.assign(Boom.internal('Invalid credentials'), result);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
throw Object.assign(Boom.internal('Unknown algorithm'), result);
}
// Calculate MAC
const mac = Crypto.calculateMac('header', credentials, artifacts);
if (!Crypto.fixedTimeComparison(mac, attributes.mac)) {
throw Object.assign(Utils.unauthorized('Bad mac'), result);
}
// Check payload hash
if (options.payload ||
options.payload === '') {
if (!attributes.hash) {
throw Object.assign(Utils.unauthorized('Missing required payload hash'), result);
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'), null, artifacts);
const hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
if (!Crypto.fixedTimeComparison(hash, attributes.hash)) {
throw Object.assign(Utils.unauthorized('Bad payload hash'), result);
}
}
if (!credentials.key ||
!credentials.algorithm) {
// Check nonce
return callback(Boom.internal('Invalid credentials'), credentials, artifacts);
if (options.nonceFunc) {
try {
await options.nonceFunc(credentials.key, attributes.nonce, attributes.ts);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials, artifacts);
catch (err) {
throw Object.assign(Utils.unauthorized('Invalid nonce'), result);
}
}
// Calculate MAC
// Check timestamp staleness
const mac = Crypto.calculateMac('header', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials, artifacts);
}
if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
const tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
throw Object.assign(Utils.unauthorized('Stale timestamp', tsm), result);
}
// Check payload hash
// Successful authentication
if (options.payload ||
options.payload === '') {
if (!attributes.hash) {
return callback(Utils.unauthorized('Missing required payload hash'), credentials, artifacts);
}
const hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
if (!Cryptiles.fixedTimeComparison(hash, attributes.hash)) {
return callback(Utils.unauthorized('Bad payload hash'), credentials, artifacts);
}
}
// Check nonce
options.nonceFunc(credentials.key, attributes.nonce, attributes.ts, (err) => {
if (err) {
return callback(Utils.unauthorized('Invalid nonce'), credentials, artifacts);
}
// Check timestamp staleness
if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
const tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
return callback(Utils.unauthorized('Stale timestamp', tsm), credentials, artifacts);
}
// Successful authentication
return callback(null, credentials, artifacts);
});
});
return result;
};
@ -209,15 +203,19 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
/*
payload: raw request payload
credentials: from authenticate callback
artifacts: from authenticate callback
credentials: from authenticate()
artifacts: from authenticate()
contentType: req.headers['content-type']
Return value: { credentials, artifacts } or throws an error.
*/
exports.authenticatePayload = function (payload, credentials, artifacts, contentType) {
const calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);
return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
if (!Crypto.fixedTimeComparison(calculatedHash, artifacts.hash)) {
throw Object.assign(Utils.unauthorized('Bad payload hash'), { credentials, artifacts });
}
};
@ -225,12 +223,16 @@ exports.authenticatePayload = function (payload, credentials, artifacts, content
/*
calculatedHash: the payload hash calculated using Crypto.calculatePayloadHash()
artifacts: from authenticate callback
artifacts: from authenticate()
Return value: { artifacts } or throws an error.
*/
exports.authenticatePayloadHash = function (calculatedHash, artifacts) {
return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
if (!Crypto.fixedTimeComparison(calculatedHash, artifacts.hash)) {
throw Object.assign(Utils.unauthorized('Bad payload hash'), { artifacts });
}
};
@ -292,7 +294,7 @@ exports.header = function (credentials, artifacts, options) {
// Construct header
let header = 'Hawk mac="' + mac + '"' +
(artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');
(artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');
if (artifacts.ext !== null &&
artifacts.ext !== undefined &&
@ -315,9 +317,7 @@ exports.header = function (credentials, artifacts, options) {
internals.bewitRegex = /^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/;
exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
exports.authenticateBewit = async function (req, credentialsFunc, options = {}) {
// Application time
@ -326,25 +326,22 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Convert node Http request object to a request configuration object
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
// Extract bewit
if (request.url.length > Utils.limits.maxMatchLength) {
return callback(Boom.badRequest('Resource path exceeds max length'));
throw Boom.badRequest('Resource path exceeds max length');
}
const resource = request.url.match(internals.bewitRegex);
if (!resource) {
return callback(Utils.unauthorized());
throw Utils.unauthorized();
}
// Bewit not empty
if (!resource[3]) {
return callback(Utils.unauthorized('Empty bewit'));
throw Utils.unauthorized('Empty bewit');
}
// Verify method is GET
@ -352,27 +349,29 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
if (request.method !== 'GET' &&
request.method !== 'HEAD') {
return callback(Utils.unauthorized('Invalid method'));
throw Utils.unauthorized('Invalid method');
}
// No other authentication
if (request.authorization) {
return callback(Boom.badRequest('Multiple authentications'));
throw Boom.badRequest('Multiple authentications');
}
// Parse bewit
const bewitString = Hoek.base64urlDecode(resource[3]);
if (bewitString instanceof Error) {
return callback(Boom.badRequest('Invalid bewit encoding'));
try {
var bewitString = Hoek.base64urlDecode(resource[3]);
}
catch (err) {
throw Boom.badRequest('Invalid bewit encoding');
}
// Bewit format: id\exp\mac\ext ('\' is used because it is a reserved header attribute character)
const bewitParts = bewitString.split('\\');
if (bewitParts.length !== 4) {
return callback(Boom.badRequest('Invalid bewit structure'));
throw Boom.badRequest('Invalid bewit structure');
}
const bewit = {
@ -386,7 +385,7 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
!bewit.exp ||
!bewit.mac) {
return callback(Boom.badRequest('Missing bewit attributes'));
throw Boom.badRequest('Missing bewit attributes');
}
// Construct URL without bewit
@ -399,51 +398,53 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Check expiration
if (bewit.exp * 1000 <= now) {
return callback(Utils.unauthorized('Access expired'), null, bewit);
throw Object.assign(Utils.unauthorized('Access expired'), { bewit });
}
// Fetch Hawk credentials
credentialsFunc(bewit.id, (err, credentials) => {
try {
var credentials = await credentialsFunc(bewit.id);
}
catch (err) {
throw Object.assign(err, { bewit });
}
if (err) {
return callback(err, credentials || null, bewit.ext);
}
if (!credentials) {
throw Object.assign(Utils.unauthorized('Unknown credentials'), { bewit });
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'), null, bewit);
}
const result = { credentials, attributes: bewit };
if (!credentials.key ||
!credentials.algorithm) {
if (!credentials.key ||
!credentials.algorithm) {
return callback(Boom.internal('Invalid credentials'), credentials, bewit);
}
throw Object.assign(Boom.internal('Invalid credentials'), result);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials, bewit);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
throw Object.assign(Boom.internal('Unknown algorithm'), result);
}
// Calculate MAC
// Calculate MAC
const mac = Crypto.calculateMac('bewit', credentials, {
ts: bewit.exp,
nonce: '',
method: 'GET',
resource: url,
host: request.host,
port: request.port,
ext: bewit.ext
});
if (!Cryptiles.fixedTimeComparison(mac, bewit.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials, bewit);
}
// Successful authentication
return callback(null, credentials, bewit);
const mac = Crypto.calculateMac('bewit', credentials, {
ts: bewit.exp,
nonce: '',
method: 'GET',
resource: url,
host: request.host,
port: request.port,
ext: bewit.ext
});
if (!Crypto.fixedTimeComparison(mac, bewit.mac)) {
throw Object.assign(Utils.unauthorized('Bad mac'), result);
}
// Successful authentication
return result;
};
@ -452,13 +453,10 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
* 'nonceFunc', 'timestampSkewSec', 'localtimeOffsetMsec'
*/
exports.authenticateMessage = function (host, port, message, authorization, credentialsFunc, options, callback) {
callback = Hoek.nextTick(callback);
exports.authenticateMessage = async function (host, port, message, authorization, credentialsFunc, options = {}) {
// Default options
options.nonceFunc = options.nonceFunc || internals.nonceFunc;
options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
// Application time
@ -473,78 +471,70 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
!authorization.hash ||
!authorization.mac) {
return callback(Boom.badRequest('Invalid authorization'));
throw Boom.badRequest('Invalid authorization');
}
// Fetch Hawk credentials
credentialsFunc(authorization.id, (err, credentials) => {
const credentials = await credentialsFunc(authorization.id);
if (!credentials) {
throw Utils.unauthorized('Unknown credentials');
}
if (err) {
return callback(err, credentials || null);
const result = { credentials };
if (!credentials.key ||
!credentials.algorithm) {
throw Object.assign(Boom.internal('Invalid credentials'), result);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
throw Object.assign(Boom.internal('Unknown algorithm'), result);
}
// Construct artifacts container
const artifacts = {
ts: authorization.ts,
nonce: authorization.nonce,
host,
port,
hash: authorization.hash
};
// Calculate MAC
const mac = Crypto.calculateMac('message', credentials, artifacts);
if (!Crypto.fixedTimeComparison(mac, authorization.mac)) {
throw Object.assign(Utils.unauthorized('Bad mac'), result);
}
// Check payload hash
const hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
if (!Crypto.fixedTimeComparison(hash, authorization.hash)) {
throw Object.assign(Utils.unauthorized('Bad message hash'), result);
}
// Check nonce
if (options.nonceFunc) {
try {
await options.nonceFunc(credentials.key, authorization.nonce, authorization.ts);
}
if (!credentials) {
return callback(Utils.unauthorized('Unknown credentials'));
catch (err) {
throw Object.assign(Utils.unauthorized('Invalid nonce'), result);
}
}
if (!credentials.key ||
!credentials.algorithm) {
// Check timestamp staleness
return callback(Boom.internal('Invalid credentials'), credentials);
}
if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
throw Object.assign(Utils.unauthorized('Stale timestamp'), result);
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return callback(Boom.internal('Unknown algorithm'), credentials);
}
// Successful authentication
// Construct artifacts container
const artifacts = {
ts: authorization.ts,
nonce: authorization.nonce,
host,
port,
hash: authorization.hash
};
// Calculate MAC
const mac = Crypto.calculateMac('message', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, authorization.mac)) {
return callback(Utils.unauthorized('Bad mac'), credentials);
}
// Check payload hash
const hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
if (!Cryptiles.fixedTimeComparison(hash, authorization.hash)) {
return callback(Utils.unauthorized('Bad message hash'), credentials);
}
// Check nonce
options.nonceFunc(credentials.key, authorization.nonce, authorization.ts, (err) => {
if (err) {
return callback(Utils.unauthorized('Invalid nonce'), credentials);
}
// Check timestamp staleness
if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
return callback(Utils.unauthorized('Stale timestamp'), credentials);
}
// Successful authentication
return callback(null, credentials);
});
});
};
internals.nonceFunc = function (key, nonce, ts, nonceCallback) {
return nonceCallback(); // No validation
return result;
};

Просмотреть файл

@ -2,8 +2,8 @@
// Load modules
const Sntp = require('sntp');
const Boom = require('boom');
const Sntp = require('sntp');
// Declare internals
@ -80,7 +80,7 @@ exports.parseRequest = function (req, options) {
host = exports.parseHost(req, options.hostHeaderName);
if (!host) {
return new Error('Invalid Host header');
throw Boom.badRequest('Invalid Host header');
}
}
@ -120,26 +120,26 @@ exports.parseAuthorizationHeader = function (header, keys) {
keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
if (!header) {
return Boom.unauthorized(null, 'Hawk');
throw Boom.unauthorized(null, 'Hawk');
}
if (header.length > exports.limits.maxMatchLength) {
return Boom.badRequest('Header length too long');
throw Boom.badRequest('Header length too long');
}
const headerParts = header.match(internals.authHeaderRegex);
if (!headerParts) {
return Boom.badRequest('Invalid header syntax');
throw Boom.badRequest('Invalid header syntax');
}
const scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return Boom.unauthorized(null, 'Hawk');
throw Boom.unauthorized(null, 'Hawk');
}
const attributesString = headerParts[2];
if (!attributesString) {
return Boom.badRequest('Invalid header syntax');
throw Boom.badRequest('Invalid header syntax');
}
const attributes = {};
@ -172,7 +172,7 @@ exports.parseAuthorizationHeader = function (header, keys) {
});
if (verify !== '') {
return Boom.badRequest(errorMessage || 'Bad header format');
throw Boom.badRequest(errorMessage || 'Bad header format');
}
return attributes;

Просмотреть файл

@ -1,7 +1,7 @@
{
"name": "hawk",
"description": "HTTP Hawk Authentication Scheme",
"version": "6.0.2",
"version": "7.0.0",
"author": "Eran Hammer <eran@hammer.io> (http://hueniverse.com)",
"repository": "git://github.com/hueniverse/hawk",
"main": "lib/index.js",
@ -13,19 +13,19 @@
"hawk"
],
"engines": {
"node": ">=4.5.0"
"node": ">=8.8.0"
},
"dependencies": {
"hoek": "4.x.x",
"boom": "4.x.x",
"cryptiles": "3.x.x",
"sntp": "2.x.x"
"hoek": "5.x.x",
"boom": "6.x.x",
"cryptiles": "4.x.x",
"sntp": "3.x.x"
},
"devDependencies": {
"babel-cli": "^6.1.2",
"babel-preset-es2015": "^6.1.2",
"code": "4.x.x",
"lab": "14.x.x"
"code": "5.x.x",
"lab": "15.x.x"
},
"babel": {
"presets": [
@ -34,7 +34,7 @@
},
"scripts": {
"build-client": "mkdir -p dist; babel lib/browser.js --out-file dist/browser.js",
"prepublish": "npm run-script build-client",
"prepare": "npm run-script build-client",
"test": "lab -a code -t 100 -L",
"test-cov-html": "lab -a code -r html -o coverage.html"
},

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -14,9 +14,7 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
@ -24,7 +22,7 @@ describe('Client', () => {
describe('header()', () => {
it('returns a valid authorization header (sha1)', (done) => {
it('returns a valid authorization header (sha1)', () => {
const credentials = {
id: '123456',
@ -32,12 +30,11 @@ describe('Client', () => {
algorithm: 'sha1'
};
const header = Hawk.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;
const { header } = Hawk.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' });
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
done();
});
it('returns a valid authorization header (sha256)', (done) => {
it('returns a valid authorization header (sha256)', () => {
const credentials = {
id: '123456',
@ -45,12 +42,11 @@ describe('Client', () => {
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
const { header } = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
done();
});
it('returns a valid authorization header (no ext)', (done) => {
it('returns a valid authorization header (no ext)', () => {
const credentials = {
id: '123456',
@ -58,12 +54,11 @@ describe('Client', () => {
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
const { header } = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('returns a valid authorization header (null ext)', (done) => {
it('returns a valid authorization header (null ext)', () => {
const credentials = {
id: '123456',
@ -71,12 +66,11 @@ describe('Client', () => {
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field;
const { header } = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null });
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('returns a valid authorization header (empty payload)', (done) => {
it('returns a valid authorization header (empty payload)', () => {
const credentials = {
id: '123456',
@ -84,12 +78,11 @@ describe('Client', () => {
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' }).field;
const { header } = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' });
expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", mac=\"U5k16YEzn3UnBHKeBzsDXn067Gu3R4YaY6xOt9PYRZM=\"');
done();
});
it('returns a valid authorization header (pre hashed payload)', (done) => {
it('returns a valid authorization header (pre hashed payload)', () => {
const credentials = {
id: '123456',
@ -99,86 +92,61 @@ describe('Client', () => {
const options = { credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };
options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;
const { header } = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options);
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('errors on missing uri', (done) => {
it('errors on missing uri', () => {
const header = Hawk.client.header('', 'POST');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
expect(() => Hawk.client.header('', 'POST')).to.throw('Invalid argument type');
});
it('errors on invalid uri', (done) => {
it('errors on invalid uri', () => {
const header = Hawk.client.header(4, 'POST');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
expect(() => Hawk.client.header(4, 'POST')).to.throw('Invalid argument type');
});
it('errors on missing method', (done) => {
it('errors on missing method', () => {
const 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();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', '')).to.throw('Invalid argument type');
});
it('errors on invalid method', (done) => {
it('errors on invalid method', () => {
const 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();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 5)).to.throw('Invalid argument type');
});
it('errors on missing options', (done) => {
it('errors on missing options', () => {
const 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();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST')).to.throw('Invalid argument type');
});
it('errors on invalid credentials (id)', (done) => {
it('errors on invalid credentials (id)', () => {
const credentials = {
key: '2983d45yun89q',
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid credential object');
done();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207 })).to.throw('Invalid credentials');
});
it('errors on missing credentials', (done) => {
it('errors on missing credentials', () => {
const 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();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 })).to.throw('Invalid credentials');
});
it('errors on invalid credentials', (done) => {
it('errors on invalid credentials', () => {
const credentials = {
id: '123456',
algorithm: 'sha256'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid credential object');
done();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, ext: 'Bazinga!', timestamp: 1353809207 })).to.throw('Invalid credentials');
});
it('errors on invalid algorithm', (done) => {
it('errors on invalid algorithm', () => {
const credentials = {
id: '123456',
@ -186,16 +154,13 @@ describe('Client', () => {
algorithm: 'hmac-sha-0'
};
const header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Unknown algorithm');
done();
expect(() => Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 })).to.throw('Unknown algorithm');
});
});
describe('authenticate()', () => {
it('returns false on invalid header', (done) => {
it('rejects on invalid header', () => {
const res = {
headers: {
@ -203,27 +168,10 @@ describe('Client', () => {
}
};
expect(Hawk.client.authenticate(res, {})).to.equal(false);
done();
expect(() => Hawk.client.authenticate(res)).to.throw('Invalid Server-Authorization header');
});
it('returns false on invalid header (callback)', (done) => {
const res = {
headers: {
'server-authorization': 'Hawk mac="abc", bad="xyz"'
}
};
Hawk.client.authenticate(res, {}, null, null, (err) => {
expect(err).to.exist();
expect(err.message).to.equal('Invalid Server-Authorization header');
done();
});
});
it('returns false on invalid mac', (done) => {
it('rejects on invalid mac', () => {
const res = {
headers: {
@ -254,11 +202,10 @@ describe('Client', () => {
user: 'steve'
};
expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(false);
done();
expect(() => Hawk.client.authenticate(res, credentials, artifacts)).to.throw('Bad response mac');
});
it('returns true on ignoring hash', (done) => {
it('returns headers on ignoring hash', () => {
const res = {
headers: {
@ -289,96 +236,60 @@ describe('Client', () => {
user: 'steve'
};
expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
done();
});
it('validates response payload', (done) => {
const payload = 'some reply';
const res = {
headers: {
'content-type': 'text/plain',
'server-authorization': 'Hawk mac="odsVGUq0rCoITaiNagW22REIpqkwP9zt5FyqqOW9Zj8=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
const { headers } = Hawk.client.authenticate(res, credentials, artifacts);
expect(headers).to.equal({
'server-authorization': {
mac: 'XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=',
hash: 'f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=',
ext: 'response-specific'
}
};
const credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
const artifacts = {
method: 'POST',
host: 'example.com',
port: '8080',
resource: '/resource/4?filter=a',
ts: '1453070933',
nonce: '3hOHpR',
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
ext: 'some-app-data',
app: undefined,
dlg: undefined,
mac: '/DitzeD66F2f7O535SERbX9p+oh9ZnNLqSNHG+c7/vs=',
id: '123456'
};
expect(Hawk.client.authenticate(res, credentials, artifacts, { payload })).to.equal(true);
done();
});
it('validates response payload (callback)', (done) => {
const payload = 'some reply';
const res = {
headers: {
'content-type': 'text/plain',
'server-authorization': 'Hawk mac="odsVGUq0rCoITaiNagW22REIpqkwP9zt5FyqqOW9Zj8=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
}
};
const credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
const artifacts = {
method: 'POST',
host: 'example.com',
port: '8080',
resource: '/resource/4?filter=a',
ts: '1453070933',
nonce: '3hOHpR',
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
ext: 'some-app-data',
app: undefined,
dlg: undefined,
mac: '/DitzeD66F2f7O535SERbX9p+oh9ZnNLqSNHG+c7/vs=',
id: '123456'
};
Hawk.client.authenticate(res, credentials, artifacts, { payload }, (err, headers) => {
expect(err).to.not.exist();
expect(headers).to.equal({
'www-authenticate': null,
'server-authorization': {
mac: 'odsVGUq0rCoITaiNagW22REIpqkwP9zt5FyqqOW9Zj8=',
hash: 'f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=',
ext: 'response-specific'
}
});
done();
});
});
it('errors on invalid response payload', (done) => {
it('validates response payload', () => {
const payload = 'some reply';
const res = {
headers: {
'content-type': 'text/plain',
'server-authorization': 'Hawk mac="odsVGUq0rCoITaiNagW22REIpqkwP9zt5FyqqOW9Zj8=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
}
};
const credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
const artifacts = {
method: 'POST',
host: 'example.com',
port: '8080',
resource: '/resource/4?filter=a',
ts: '1453070933',
nonce: '3hOHpR',
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
ext: 'some-app-data',
app: undefined,
dlg: undefined,
mac: '/DitzeD66F2f7O535SERbX9p+oh9ZnNLqSNHG+c7/vs=',
id: '123456'
};
const { headers } = Hawk.client.authenticate(res, credentials, artifacts, { payload });
expect(headers).to.equal({
'server-authorization': {
mac: 'odsVGUq0rCoITaiNagW22REIpqkwP9zt5FyqqOW9Zj8=',
hash: 'f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=',
ext: 'response-specific'
}
});
});
it('errors on invalid response payload', () => {
const payload = 'wrong reply';
@ -411,18 +322,16 @@ describe('Client', () => {
id: '123456'
};
expect(Hawk.client.authenticate(res, credentials, artifacts, { payload })).to.equal(false);
done();
expect(() => Hawk.client.authenticate(res, credentials, artifacts, { payload })).to.throw('Bad response payload mac');
});
it('fails on invalid WWW-Authenticate header format', (done) => {
it('fails on invalid WWW-Authenticate header format', () => {
const header = 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(false);
done();
expect(() => Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.throw('Invalid WWW-Authenticate header');
});
it('fails on invalid WWW-Authenticate header format', (done) => {
it('fails on invalid WWW-Authenticate header format', () => {
const credentials = {
id: '123456',
@ -432,21 +341,20 @@ describe('Client', () => {
};
const header = 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.equal(false);
done();
expect(() => Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.throw('Invalid server timestamp hash');
});
it('skips tsm validation when missing ts', (done) => {
it('skips tsm validation when missing ts', () => {
const header = 'Hawk error="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(true);
done();
const { headers } = Hawk.client.authenticate({ headers: { 'www-authenticate': header } });
expect(headers).to.equal({ 'www-authenticate': { error: 'Stale timestamp' } });
});
});
describe('message()', () => {
it('generates authorization', (done) => {
it('generates authorization', () => {
const credentials = {
id: '123456',
@ -458,10 +366,9 @@ describe('Client', () => {
expect(auth).to.exist();
expect(auth.ts).to.equal(1353809207);
expect(auth.nonce).to.equal('abc123');
done();
});
it('errors on invalid host', (done) => {
it('errors on invalid host', () => {
const credentials = {
id: '123456',
@ -469,12 +376,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message(5, 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message(5, 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on invalid port', (done) => {
it('errors on invalid port', () => {
const credentials = {
id: '123456',
@ -482,12 +387,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on missing host', (done) => {
it('errors on missing host', () => {
const credentials = {
id: '123456',
@ -495,12 +398,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message(undefined, 0, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message(undefined, 0, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on missing port', (done) => {
it('errors on missing port', () => {
const credentials = {
id: '123456',
@ -508,12 +409,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', undefined, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', undefined, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on null message', (done) => {
it('errors on null message', () => {
const credentials = {
id: '123456',
@ -521,12 +420,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', 80, null, { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, null, { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on missing message', (done) => {
it('errors on missing message', () => {
const credentials = {
id: '123456',
@ -534,12 +431,10 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', 80, undefined, { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, undefined, { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on invalid message', (done) => {
it('errors on invalid message', () => {
const credentials = {
id: '123456',
@ -547,40 +442,32 @@ describe('Client', () => {
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', 80, 5, { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, 5, { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid inputs');
});
it('errors on missing options', (done) => {
it('errors on missing options', () => {
const auth = Hawk.client.message('example.com', 80, 'I am the boodyman');
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, 'I am the boodyman')).to.throw('Invalid credentials');
});
it('errors on invalid credentials (id)', (done) => {
it('errors on invalid credentials (id)', () => {
const credentials = {
key: '2983d45yun89q',
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid credentials');
});
it('errors on invalid credentials (key)', (done) => {
it('errors on invalid credentials (key)', () => {
const credentials = {
id: '123456',
algorithm: 'sha1'
};
const auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
expect(() => Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials, timestamp: 1353809207, nonce: 'abc123' })).to.throw('Invalid credentials');
});
});
});

Просмотреть файл

@ -14,9 +14,7 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
@ -24,7 +22,7 @@ describe('Crypto', () => {
describe('generateNormalizedString()', () => {
it('should return a valid normalized string', (done) => {
it('should return a valid normalized string', () => {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
@ -34,11 +32,9 @@ describe('Crypto', () => {
host: 'example.com',
port: 8080
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\n\n');
done();
});
it('should return a valid normalized string (ext)', (done) => {
it('should return a valid normalized string (ext)', () => {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
@ -49,11 +45,9 @@ describe('Crypto', () => {
port: 8080,
ext: 'this is some app data'
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\nthis is some app data\n');
done();
});
it('should return a valid normalized string (payload + ext)', (done) => {
it('should return a valid normalized string (payload + ext)', () => {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
@ -65,8 +59,6 @@ describe('Crypto', () => {
hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=',
ext: 'this is some app data'
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\nU4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=\nthis is some app data\n');
done();
});
});
});

Просмотреть файл

@ -3,6 +3,7 @@
// Load modules
const Url = require('url');
const Code = require('code');
const Hawk = require('../lib');
const Lab = require('lab');
@ -15,27 +16,23 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
describe('Hawk', () => {
const credentialsFunc = function (id, callback) {
const credentialsFunc = function (id) {
const credentials = {
return {
id,
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: (id === '1' ? 'sha1' : 'sha256'),
user: 'steve'
};
return callback(null, credentials);
};
it('generates a header then successfully parse it (configuration)', (done) => {
it('generates a header then successfully parse it (configuration)', async () => {
const req = {
method: 'GET',
@ -44,24 +41,17 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header(Url.parse('http://example.com:8080/resource/4?filter=a'), req.method, { credentials: credentials1, ext: 'some-app-data' }).header;
expect(req.authorization).to.exist();
req.authorization = Hawk.client.header(Url.parse('http://example.com:8080/resource/4?filter=a'), req.method, { credentials: credentials1, ext: 'some-app-data' }).field;
expect(req.authorization).to.exist();
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
});
it('generates a header then successfully parse it (node request)', (done) => {
it('generates a header then successfully parse it (node request)', async () => {
const req = {
method: 'POST',
@ -74,36 +64,29 @@ describe('Hawk', () => {
const payload = 'some not so random text';
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.header;
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(() => Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.not.throw();
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
const res = {
headers: {
'content-type': 'text/plain'
}
};
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
const res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);
done();
});
});
expect(() => Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.not.throw();
});
it('generates a header then successfully parse it (absolute request uri)', (done) => {
it('generates a header then successfully parse it (absolute request uri)', async () => {
const req = {
method: 'POST',
@ -116,36 +99,29 @@ describe('Hawk', () => {
const payload = 'some not so random text';
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.header;
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(() => Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.not.throw();
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
const res = {
headers: {
'content-type': 'text/plain'
}
};
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
const res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);
done();
});
});
expect(() => Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.not.throw();
});
it('generates a header then successfully parse it (no server header options)', (done) => {
it('generates a header then successfully parse it (no server header options)', async () => {
const req = {
method: 'POST',
@ -158,36 +134,29 @@ describe('Hawk', () => {
const payload = 'some not so random text';
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.header;
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(() => Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.not.throw();
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
const res = {
headers: {
'content-type': 'text/plain'
}
};
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
const res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts)).to.equal(true);
done();
});
});
expect(() => Hawk.client.authenticate(res, credentials2, artifacts)).to.not.throw();
});
it('generates a header then fails to parse it (missing server header hash)', (done) => {
it('generates a header then fails to parse it (missing server header hash)', async () => {
const req = {
method: 'POST',
@ -200,36 +169,29 @@ describe('Hawk', () => {
const payload = 'some not so random text';
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.header;
const reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(() => Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.not.throw();
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
const res = {
headers: {
'content-type': 'text/plain'
}
};
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
const res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(false);
done();
});
});
expect(() => Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.throw('Missing response hash attribute');
});
it('generates a header then successfully parse it (with hash)', (done) => {
it('generates a header then successfully parse it (with hash)', async () => {
const req = {
method: 'GET',
@ -238,22 +200,15 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).header;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
});
it('generates a header then successfully parse it then validate payload', (done) => {
it('generates a header then successfully parse it then validate payload', async () => {
const req = {
method: 'GET',
@ -262,24 +217,17 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload('hola!', credentials2, artifacts)).to.be.true();
expect(Hawk.server.authenticatePayload('hello!', credentials2, artifacts)).to.be.false();
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).header;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(() => Hawk.server.authenticatePayload('hola!', credentials2, artifacts)).to.not.throw();
expect(() => Hawk.server.authenticatePayload('hello!', credentials2, artifacts)).to.throw('Bad payload hash');
});
it('generates a header then successfully parses and validates payload', (done) => {
it('generates a header then successfully parses and validates payload', async () => {
const req = {
method: 'GET',
@ -288,22 +236,15 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, { payload: 'hola!' }, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).header;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc, { payload: 'hola!' });
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
});
it('generates a header then successfully parse it (app)', (done) => {
it('generates a header then successfully parse it (app)', async () => {
const req = {
method: 'GET',
@ -312,23 +253,16 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased' }).header;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
});
it('generates a header then successfully parse it (app, dlg)', (done) => {
it('generates a header then successfully parse it (app, dlg)', async () => {
const req = {
method: 'GET',
@ -337,24 +271,17 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
expect(artifacts.dlg).to.equal('23434szr3q4d');
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).header;
const { credentials: credentials2, artifacts } = await Hawk.server.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
expect(artifacts.dlg).to.equal('23434szr3q4d');
});
it('generates a header then fail authentication due to bad hash', (done) => {
it('generates a header then fail authentication due to bad hash', async () => {
const req = {
method: 'GET',
@ -363,21 +290,13 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, (err, credentials2, artifacts) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Bad payload hash');
done();
});
});
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials, payload: 'hola!', ext: 'some-app-data' }).header;
await expect(Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' })).to.reject('Bad payload hash');
});
it('generates a header for one resource then fail to authenticate another', (done) => {
it('generates a header for one resource then fail to authenticate another', async () => {
const req = {
method: 'GET',
@ -386,19 +305,12 @@ describe('Hawk', () => {
port: 8080
};
credentialsFunc('123456', (err, credentials1) => {
const credentials = credentialsFunc('123456');
expect(err).to.not.exist();
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials, ext: 'some-app-data' }).header;
req.url = '/something/else';
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' }).field;
req.url = '/something/else';
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials2, artifacts) => {
expect(err).to.exist();
expect(credentials2).to.exist();
done();
});
});
const err = await expect(Hawk.server.authenticate(req, credentialsFunc)).to.reject();
expect(err.credentials).to.exist();
});
});

Просмотреть файл

@ -15,9 +15,7 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
@ -38,15 +36,13 @@ describe('README', () => {
ext: 'some-app-ext-data'
};
it('should generate a header protocol example', (done) => {
const header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', options).field;
it('should generate a header protocol example', () => {
const { header } = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', options);
expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="');
done();
});
it('should generate a normalized string protocol example', (done) => {
it('should generate a normalized string protocol example', () => {
const normalized = Hawk.crypto.generateNormalizedString('header', {
credentials,
@ -60,22 +56,19 @@ describe('README', () => {
});
expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nGET\n/resource?a=1&b=2\nexample.com\n8000\n\nsome-app-ext-data\n');
done();
});
const payloadOptions = Hoek.clone(options);
payloadOptions.payload = 'Thank you for flying Hawk';
payloadOptions.contentType = 'text/plain';
it('should generate a header protocol example (with payload)', (done) => {
const header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'POST', payloadOptions).field;
it('should generate a header protocol example (with payload)', () => {
const { header } = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'POST', payloadOptions);
expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="');
done();
});
it('should generate a normalized string protocol example (with payload)', (done) => {
it('should generate a normalized string protocol example (with payload)', () => {
const normalized = Hawk.crypto.generateNormalizedString('header', {
credentials,
@ -90,7 +83,6 @@ describe('README', () => {
});
expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nPOST\n/resource?a=1&b=2\nexample.com\n8000\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\nsome-app-ext-data\n');
done();
});
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -3,6 +3,8 @@
// Load modules
const Url = require('url');
const Boom = require('boom');
const Code = require('code');
const Hawk = require('../lib');
const Hoek = require('hoek');
@ -16,27 +18,23 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
describe('Uri', () => {
const credentialsFunc = function (id, callback) {
const credentialsFunc = function (id) {
const credentials = {
return {
id,
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: (id === '1' ? 'sha1' : 'sha256'),
user: 'steve'
};
return callback(null, credentials);
};
it('should generate a bewit then successfully authenticate it', (done) => {
it('should generate a bewit then successfully authenticate it', async () => {
const req = {
method: 'GET',
@ -45,24 +43,16 @@ describe('Uri', () => {
port: 80
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
const bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });
req.url += '&bewit=' + bewit;
expect(err).to.not.exist();
const bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });
req.url += '&bewit=' + bewit;
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials2, attributes) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
});
const { credentials: credentials2, attributes } = await Hawk.uri.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
});
it('should generate a bewit then successfully authenticate it (no ext)', (done) => {
it('should generate a bewit then successfully authenticate it (no ext)', async () => {
const req = {
method: 'GET',
@ -71,23 +61,15 @@ describe('Uri', () => {
port: 80
};
credentialsFunc('123456', (err, credentials1) => {
const credentials1 = credentialsFunc('123456');
const bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100 });
req.url += '&bewit=' + bewit;
expect(err).to.not.exist();
const bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100 });
req.url += '&bewit=' + bewit;
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials2, attributes) => {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
done();
});
});
const { credentials: credentials2 } = await Hawk.uri.authenticate(req, credentialsFunc);
expect(credentials2.user).to.equal('steve');
});
it('should successfully authenticate a request (last param)', (done) => {
it('should successfully authenticate a request (last param)', async () => {
const req = {
method: 'GET',
@ -96,16 +78,12 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
const { credentials, attributes } = await Hawk.uri.authenticate(req, credentialsFunc);
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
});
it('should successfully authenticate a request (first param)', (done) => {
it('should successfully authenticate a request (first param)', async () => {
const req = {
method: 'GET',
@ -114,16 +92,12 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
const { credentials, attributes } = await Hawk.uri.authenticate(req, credentialsFunc);
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
});
it('should successfully authenticate a request (only param)', (done) => {
it('should successfully authenticate a request (only param)', async () => {
const req = {
method: 'GET',
@ -132,16 +106,12 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
const { credentials, attributes } = await Hawk.uri.authenticate(req, credentialsFunc);
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
});
it('should fail on multiple authentication', (done) => {
it('should fail on multiple authentication', async () => {
const req = {
method: 'GET',
@ -151,53 +121,40 @@ describe('Uri', () => {
authorization: 'Basic asdasdasdasd'
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Multiple authentications');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Multiple authentications');
});
it('should fail on method other than GET', (done) => {
it('should fail on method other than GET', async () => {
credentialsFunc('123456', (err, credentials1) => {
const credentials = credentialsFunc('123456');
expect(err).to.not.exist();
const req = {
method: 'POST',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
const req = {
method: 'POST',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
const exp = Math.floor(Hawk.utils.now() / 1000) + 60;
const ext = 'some-app-data';
const mac = Hawk.crypto.calculateMac('bewit', credentials1, {
ts: exp,
nonce: '',
method: req.method,
resource: req.url,
host: req.host,
port: req.port,
ext
});
const bewit = credentials1.id + '\\' + exp + '\\' + mac + '\\' + ext;
req.url += '&bewit=' + Hoek.base64urlEncode(bewit);
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials2, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid method');
done();
});
const exp = Math.floor(Hawk.utils.now() / 1000) + 60;
const ext = 'some-app-data';
const mac = Hawk.crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: req.method,
resource: req.url,
host: req.host,
port: req.port,
ext
});
const bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + ext;
req.url += '&bewit=' + Hoek.base64urlEncode(bewit);
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Invalid method');
});
it('should fail on invalid host header', (done) => {
it('should fail on invalid host header', async () => {
const req = {
method: 'GET',
@ -207,15 +164,10 @@ describe('Uri', () => {
}
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid Host header');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Invalid Host header');
});
it('should fail on empty bewit', (done) => {
it('should fail on empty bewit', async () => {
const req = {
method: 'GET',
@ -224,16 +176,11 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Empty bewit');
expect(err.isMissing).to.not.exist();
done();
});
const err = await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Empty bewit');
expect(err.isMissing).to.not.exist();
});
it('should fail on invalid bewit', (done) => {
it('should fail on invalid bewit', async () => {
const req = {
method: 'GET',
@ -242,16 +189,11 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid bewit encoding');
expect(err.isMissing).to.not.exist();
done();
});
const err = await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Invalid bewit encoding');
expect(err.isMissing).to.not.exist();
});
it('should fail on missing bewit', (done) => {
it('should fail on missing bewit', async () => {
const req = {
method: 'GET',
@ -260,16 +202,11 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.not.exist();
expect(err.isMissing).to.equal(true);
done();
});
const err = await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Unauthorized');
expect(err.isMissing).to.equal(true);
});
it('should fail on invalid bewit structure', (done) => {
it('should fail on invalid bewit structure', async () => {
const req = {
method: 'GET',
@ -278,15 +215,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid bewit structure');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Invalid bewit structure');
});
it('should fail on empty bewit attribute', (done) => {
it('should fail on empty bewit attribute', async () => {
const req = {
method: 'GET',
@ -295,15 +227,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Missing bewit attributes');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Missing bewit attributes');
});
it('should fail on missing bewit id attribute', (done) => {
it('should fail on missing bewit id attribute', async () => {
const req = {
method: 'GET',
@ -312,15 +239,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Missing bewit attributes');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Missing bewit attributes');
});
it('should fail on expired access', (done) => {
it('should fail on expired access', async () => {
const req = {
method: 'GET',
@ -329,15 +251,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Access expired');
done();
});
await expect(Hawk.uri.authenticate(req, credentialsFunc)).to.reject('Access expired');
});
it('should fail on credentials function error', (done) => {
it('should fail on credentials function error', async () => {
const req = {
method: 'GET',
@ -346,18 +263,13 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
await expect(Hawk.uri.authenticate(req, (id) => {
callback(Hawk.error.badRequest('Boom'));
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Boom');
done();
});
throw Boom.badRequest('Boom');
})).to.reject('Boom');
});
it('should fail on credentials function error with credentials', (done) => {
it('should fail on credentials function error with credentials', async () => {
const req = {
method: 'GET',
@ -366,19 +278,16 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
const err = await expect(Hawk.uri.authenticate(req, (id, callback) => {
callback(Hawk.error.badRequest('Boom'), { some: 'value' });
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Boom');
expect(credentials.some).to.equal('value');
done();
});
const error = Boom.badRequest('Boom');
error.credentials = { some: 'value' };
throw error;
})).to.reject('Boom');
expect(err.credentials.some).to.equal('value');
});
it('should fail on null credentials function response', (done) => {
it('should fail on null credentials function response', async () => {
const req = {
method: 'GET',
@ -387,18 +296,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
callback(null, null);
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Unknown credentials');
done();
});
await expect(Hawk.uri.authenticate(req, (id) => null)).to.reject('Unknown credentials');
});
it('should fail on invalid credentials function response', (done) => {
it('should fail on invalid credentials function response', async () => {
const req = {
method: 'GET',
@ -407,18 +308,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
callback(null, {});
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.message).to.equal('Invalid credentials');
done();
});
await expect(Hawk.uri.authenticate(req, (id) => ({}))).to.reject('Invalid credentials');
});
it('should fail on invalid credentials function response (unknown algorithm)', (done) => {
it('should fail on invalid credentials function response (unknown algorithm)', async () => {
const req = {
method: 'GET',
@ -427,18 +320,10 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
callback(null, { key: 'xxx', algorithm: 'xxx' });
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.message).to.equal('Unknown algorithm');
done();
});
await expect(Hawk.uri.authenticate(req, (id) => ({ key: 'xxx', algorithm: 'xxx' }))).to.reject('Unknown algorithm');
});
it('should fail on invalid credentials function response (bad mac)', (done) => {
it('should fail on invalid credentials function response (bad mac)', async () => {
const req = {
method: 'GET',
@ -447,20 +332,12 @@ describe('Uri', () => {
port: 8080
};
Hawk.uri.authenticate(req, (id, callback) => {
callback(null, { key: 'xxx', algorithm: 'sha256' });
}, {}, (err, credentials, attributes) => {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Bad mac');
done();
});
await expect(Hawk.uri.authenticate(req, (id) => ({ key: 'xxx', algorithm: 'sha256' }))).to.reject('Bad mac');
});
describe('getBewit()', () => {
it('returns a valid bewit value', (done) => {
it('returns a valid bewit value', () => {
const credentials = {
id: '123456',
@ -470,10 +347,9 @@ describe('Uri', () => {
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
done();
});
it('returns a valid bewit value (explicit port)', (done) => {
it('returns a valid bewit value (explicit port)', () => {
const credentials = {
id: '123456',
@ -483,10 +359,9 @@ describe('Uri', () => {
const bewit = Hawk.uri.getBewit('https://example.com:8080/somewhere/over/the/rainbow', { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6');
done();
});
it('returns a valid bewit value (null ext)', (done) => {
it('returns a valid bewit value (null ext)', () => {
const credentials = {
id: '123456',
@ -496,10 +371,9 @@ describe('Uri', () => {
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c');
done();
});
it('returns a valid bewit value (parsed uri)', (done) => {
it('returns a valid bewit value (parsed uri)', () => {
const credentials = {
id: '123456',
@ -509,17 +383,14 @@ describe('Uri', () => {
const bewit = Hawk.uri.getBewit(Url.parse('https://example.com/somewhere/over/the/rainbow'), { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
done();
});
it('errors on invalid options', (done) => {
it('errors on invalid options', () => {
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', 4);
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', 4)).to.throw('Invalid inputs');
});
it('errors on missing uri', (done) => {
it('errors on missing uri', () => {
const credentials = {
id: '123456',
@ -527,12 +398,10 @@ describe('Uri', () => {
algorithm: 'sha256'
};
const bewit = Hawk.uri.getBewit('', { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('', { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' })).to.throw('Invalid inputs');
});
it('errors on invalid uri', (done) => {
it('errors on invalid uri', () => {
const credentials = {
id: '123456',
@ -540,43 +409,35 @@ describe('Uri', () => {
algorithm: 'sha256'
};
const bewit = Hawk.uri.getBewit(5, { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit(5, { credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' })).to.throw('Invalid inputs');
});
it('errors on invalid credentials (id)', (done) => {
it('errors on invalid credentials (id)', () => {
const credentials = {
key: '2983d45yun89q',
algorithm: 'sha256'
};
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 3000, ext: 'xandyandz' })).to.throw('Invalid credentials');
});
it('errors on missing credentials', (done) => {
it('errors on missing credentials', () => {
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' })).to.throw('Invalid credentials');
});
it('errors on invalid credentials (key)', (done) => {
it('errors on invalid credentials (key)', () => {
const credentials = {
id: '123456',
algorithm: 'sha256'
};
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 3000, ext: 'xandyandz' })).to.throw('Invalid credentials');
});
it('errors on invalid algorithm', (done) => {
it('errors on invalid algorithm', () => {
const credentials = {
id: '123456',
@ -584,16 +445,12 @@ describe('Uri', () => {
algorithm: 'hmac-sha-0'
};
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 300, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials, ttlSec: 300, ext: 'xandyandz' })).to.throw('Unknown algorithm');
});
it('errors on missing options', (done) => {
it('errors on missing options', () => {
const bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow');
expect(bewit).to.equal('');
done();
expect(() => Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow')).to.throw('Invalid inputs');
});
});
});

Просмотреть файл

@ -5,6 +5,7 @@
const Code = require('code');
const Hawk = require('../lib');
const Lab = require('lab');
const Package = require('../package.json');
@ -15,9 +16,7 @@ const internals = {};
// Test shortcuts
const lab = exports.lab = Lab.script();
const describe = lab.experiment;
const it = lab.test;
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
@ -25,7 +24,7 @@ describe('Utils', () => {
describe('parseHost()', () => {
it('returns port 80 for non tls node request', (done) => {
it('returns port 80 for non tls node request', () => {
const req = {
method: 'POST',
@ -37,10 +36,9 @@ describe('Utils', () => {
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(80);
done();
});
it('returns port 443 for non tls node request', (done) => {
it('returns port 443 for non tls node request', () => {
const req = {
method: 'POST',
@ -55,10 +53,9 @@ describe('Utils', () => {
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
done();
});
it('returns port 443 for non tls node request (IPv6)', (done) => {
it('returns port 443 for non tls node request (IPv6)', () => {
const req = {
method: 'POST',
@ -73,10 +70,9 @@ describe('Utils', () => {
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
done();
});
it('parses IPv6 headers', (done) => {
it('parses IPv6 headers', () => {
const req = {
method: 'POST',
@ -93,10 +89,9 @@ describe('Utils', () => {
const host = Hawk.utils.parseHost(req, 'Host');
expect(host.port).to.equal('8000');
expect(host.name).to.equal('[123:123:123]');
done();
});
it('errors on header too long', (done) => {
it('errors on header too long', () => {
let long = '';
for (let i = 0; i < 5000; ++i) {
@ -104,13 +99,12 @@ describe('Utils', () => {
}
expect(Hawk.utils.parseHost({ headers: { host: long } })).to.be.null();
done();
});
});
describe('parseAuthorizationHeader()', () => {
it('errors on header too long', (done) => {
it('errors on header too long', () => {
let long = 'Scheme a="';
for (let i = 0; i < 5000; ++i) {
@ -118,34 +112,28 @@ describe('Utils', () => {
}
long += '"';
const err = Hawk.utils.parseAuthorizationHeader(long, ['a']);
expect(err).to.be.instanceof(Error);
expect(err.message).to.equal('Header length too long');
done();
expect(() => Hawk.utils.parseAuthorizationHeader(long, ['a'])).to.throw('Header length too long');
});
});
describe('version()', () => {
it('returns the correct package version number', (done) => {
it('returns the correct package version number', () => {
expect(Hawk.utils.version()).to.equal(Package.version);
done();
});
});
describe('unauthorized()', () => {
it('returns a hawk 401', (done) => {
it('returns a hawk 401', () => {
expect(Hawk.utils.unauthorized('kaboom').output.headers['WWW-Authenticate']).to.equal('Hawk error="kaboom"');
done();
});
it('supports attributes', (done) => {
it('supports attributes', () => {
expect(Hawk.utils.unauthorized('kaboom', { a: 'b' }).output.headers['WWW-Authenticate']).to.equal('Hawk a="b", error="kaboom"');
done();
});
});
});