Skip to content
This repository has been archived by the owner on Apr 5, 2024. It is now read-only.

Commit

Permalink
3.1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
hueniverse committed Nov 19, 2015
1 parent 10daa13 commit 66dd8f9
Show file tree
Hide file tree
Showing 2 changed files with 342 additions and 2 deletions.
340 changes: 340 additions & 0 deletions dist/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
'use strict'

// Load modules

;

function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; }

var Url = require('url');
var Hoek = require('hoek');
var Cryptiles = require('cryptiles');
var Crypto = require('./crypto');
var Utils = require('./utils');

// Declare internals

var internals = {};

// Generate an Authorization header for a given request

/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now(), // A pre-calculated timestamp
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/

exports.header = function (uri, method, options) {

var result = {
field: '',
artifacts: {}
};

// Validate inputs

if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !method || typeof method !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {

result.err = 'Invalid argument type';
return result;
}

// Application time

var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);

// Validate credentials

var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {

result.err = 'Invalid credential object';
return result;
}

if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}

// Parse URI

if (typeof uri === 'string') {
uri = Url.parse(uri);
}

// Calculate signature

var artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
method: method,
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};

result.artifacts = artifacts;

// Calculate payload hash

if (!artifacts.hash && (options.payload || options.payload === '')) {

artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}

var mac = Crypto.calculateMac('header', credentials, artifacts);

// Construct header

var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id + '", 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 : '') + '"';
}

result.field = header;

return result;
};

// Validate server response

/*
res: node's response object
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/

exports.authenticate = function (res, credentials, artifacts, options) {

artifacts = Hoek.clone(artifacts);
options = options || {};

if (res.headers['www-authenticate']) {

// Parse HTTP WWW-Authenticate header

var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
if (wwwAttributes instanceof Error) {
return false;
}

// Validate server timestamp (not used to update clock since it is done via the SNPT client)

if (wwwAttributes.ts) {
var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
}
}

// Parse HTTP Server-Authorization header

if (!res.headers['server-authorization'] && !options.required) {

return true;
}

var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (attributes instanceof Error) {
return false;
}

artifacts.ext = attributes.ext;
artifacts.hash = attributes.hash;

var mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== attributes.mac) {
return false;
}

if (!options.payload && options.payload !== '') {

return true;
}

if (!attributes.hash) {
return false;
}

var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
return calculatedHash === attributes.hash;
};

// Generate a bewit value for a given URI

/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
options: {
// 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
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/

exports.getBewit = function (uri, options) {

// Validate inputs

if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || !options.ttlSec) {

return '';
}

options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value

// Application time

var now = Utils.now(options.localtimeOffsetMsec);

// Validate credentials

var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {

return '';
}

if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}

// Parse URI

if (typeof uri === 'string') {
uri = Url.parse(uri);
}

// Calculate signature

var exp = Math.floor(now / 1000) + options.ttlSec;
var mac = Crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
ext: options.ext
});

// Construct bewit: id\exp\mac\ext

var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return Hoek.base64urlEncode(bewit);
};

// Generate an authorization string for a message

/*
host: 'example.com',
port: 8000,
message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
timestamp: Date.now(), // A pre-calculated timestamp
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
}
*/

exports.message = function (host, port, message, options) {

// Validate inputs

if (!host || typeof host !== 'string' || !port || typeof port !== 'number' || message === null || message === undefined || typeof message !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {

return null;
}

// Application time

var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);

// Validate credentials

var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {

// Invalid credential object
return null;
}

if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}

// Calculate signature

var artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
host: host,
port: port,
hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
};

// Construct authorization

var result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: Crypto.calculateMac('message', credentials, artifacts)
};

return result;
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hawk",
"description": "HTTP Hawk Authentication Scheme",
"version": "3.1.1",
"version": "3.1.2",
"author": "Eran Hammer <eran@hammer.io> (http://hueniverse.com)",
"contributors": [],
"repository": "git://github.com/hueniverse/hawk",
Expand All @@ -18,7 +18,7 @@
"browser": "./lib/browser.js",
"dependencies": {
"hoek": "2.x.x",
"boom": "^2.8.x",
"boom": "2.x.x",
"cryptiles": "2.x.x",
"sntp": "1.x.x"
},
Expand Down

0 comments on commit 66dd8f9

Please sign in to comment.