7 var _typeof = function (obj) {
9 return obj && typeof Symbol !== 'undefined' && obj.constructor === Symbol ? 'symbol' : typeof obj;
12 var Url = require('url');
13 var Hoek = require('hoek');
14 var Cryptiles = require('cryptiles');
15 var Crypto = require('./crypto');
16 var Utils = require('./utils');
22 // Generate an Authorization header for a given request
25 uri: 'http://example.com/resource?a=b' or object from Url.parse()
26 method: HTTP verb (e.g. 'GET', 'POST')
33 key: 'aoijedoaijsdlaksjdl',
34 algorithm: 'sha256' // 'sha1', 'sha256'
39 ext: 'application-specific', // Application specific data sent via the ext attribute
40 timestamp: Date.now(), // A pre-calculated timestamp
41 nonce: '2334f34f', // A pre-generated nonce
42 localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
43 payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
44 contentType: 'application/json', // Payload content-type (ignored if hash provided)
45 hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
46 app: '24s23423f34dx', // Oz application id
47 dlg: '234sz34tww3sd' // Oz delegated-by application id
51 exports.header = function (uri, method, options) {
60 if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !method || typeof method !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
62 result.err = 'Invalid argument type';
68 var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
70 // Validate credentials
72 var credentials = options.credentials;
73 if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
75 result.err = 'Invalid credential object';
79 if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
80 result.err = 'Unknown algorithm';
86 if (typeof uri === 'string') {
90 // Calculate signature
94 nonce: options.nonce || Cryptiles.randomString(6),
96 resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
98 port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
105 result.artifacts = artifacts;
107 // Calculate payload hash
109 if (!artifacts.hash && (options.payload || options.payload === '')) {
111 artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
114 var mac = Crypto.calculateMac('header', credentials, artifacts);
118 var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
119 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 + '"';
122 header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
125 result.field = header;
130 // Validate server response
133 res: node's response object
134 artifacts: object received from header().artifacts
136 payload: optional payload received
137 required: specifies if a Server-Authorization header is required. Defaults to 'false'
141 exports.authenticate = function (res, credentials, artifacts, options) {
143 artifacts = Hoek.clone(artifacts);
144 options = options || {};
146 if (res.headers['www-authenticate']) {
148 // Parse HTTP WWW-Authenticate header
150 var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
151 if (wwwAttributes instanceof Error) {
155 // Validate server timestamp (not used to update clock since it is done via the SNPT client)
157 if (wwwAttributes.ts) {
158 var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
159 if (tsm !== wwwAttributes.tsm) {
165 // Parse HTTP Server-Authorization header
167 if (!res.headers['server-authorization'] && !options.required) {
172 var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
173 if (attributes instanceof Error) {
177 artifacts.ext = attributes.ext;
178 artifacts.hash = attributes.hash;
180 var mac = Crypto.calculateMac('response', credentials, artifacts);
181 if (mac !== attributes.mac) {
185 if (!options.payload && options.payload !== '') {
190 if (!attributes.hash) {
194 var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
195 return calculatedHash === attributes.hash;
198 // Generate a bewit value for a given URI
201 uri: 'http://example.com/resource?a=b' or object from Url.parse()
208 key: 'aoijedoaijsdlaksjdl',
209 algorithm: 'sha256' // 'sha1', 'sha256'
211 ttlSec: 60 * 60, // TTL in seconds
215 ext: 'application-specific', // Application specific data sent via the ext attribute
216 localtimeOffsetMsec: 400 // Time offset to sync with server time
220 exports.getBewit = function (uri, options) {
224 if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || !options.ttlSec) {
229 options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value
233 var now = Utils.now(options.localtimeOffsetMsec);
235 // Validate credentials
237 var credentials = options.credentials;
238 if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
243 if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
249 if (typeof uri === 'string') {
250 uri = Url.parse(uri);
253 // Calculate signature
255 var exp = Math.floor(now / 1000) + options.ttlSec;
256 var mac = Crypto.calculateMac('bewit', credentials, {
260 resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
262 port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
266 // Construct bewit: id\exp\mac\ext
268 var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
269 return Hoek.base64urlEncode(bewit);
272 // Generate an authorization string for a message
277 message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation
284 key: 'aoijedoaijsdlaksjdl',
285 algorithm: 'sha256' // 'sha1', 'sha256'
290 timestamp: Date.now(), // A pre-calculated timestamp
291 nonce: '2334f34f', // A pre-generated nonce
292 localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
296 exports.message = function (host, port, message, options) {
300 if (!host || typeof host !== 'string' || !port || typeof port !== 'number' || message === null || message === undefined || typeof message !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
307 var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
309 // Validate credentials
311 var credentials = options.credentials;
312 if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
314 // Invalid credential object
318 if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
322 // Calculate signature
326 nonce: options.nonce || Cryptiles.randomString(6),
329 hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
332 // Construct authorization
337 nonce: artifacts.nonce,
338 hash: artifacts.hash,
339 mac: Crypto.calculateMac('message', credentials, artifacts)