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