Initial commit
[yaffs-website] / node_modules / hawk / dist / client.js
1 'use strict'\r
2 \r
3 // Load modules\r
4 \r
5 ;\r
6 \r
7 var _typeof = function (obj) {\r
8 \r
9     return obj && typeof Symbol !== 'undefined' && obj.constructor === Symbol ? 'symbol' : typeof obj;\r
10 };\r
11 \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
17 \r
18 // Declare internals\r
19 \r
20 var internals = {};\r
21 \r
22 // Generate an Authorization header for a given request\r
23 \r
24 /*\r
25     uri: 'http://example.com/resource?a=b' or object from Url.parse()\r
26     method: HTTP verb (e.g. 'GET', 'POST')\r
27     options: {\r
28 \r
29         // Required\r
30 \r
31         credentials: {\r
32             id: 'dh37fgj492je',\r
33             key: 'aoijedoaijsdlaksjdl',\r
34             algorithm: 'sha256'                                 // 'sha1', 'sha256'\r
35         },\r
36 \r
37         // Optional\r
38 \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
48     }\r
49 */\r
50 \r
51 exports.header = function (uri, method, options) {\r
52 \r
53     var result = {\r
54         field: '',\r
55         artifacts: {}\r
56     };\r
57 \r
58     // Validate inputs\r
59 \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
61 \r
62         result.err = 'Invalid argument type';\r
63         return result;\r
64     }\r
65 \r
66     // Application time\r
67 \r
68     var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);\r
69 \r
70     // Validate credentials\r
71 \r
72     var credentials = options.credentials;\r
73     if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {\r
74 \r
75         result.err = 'Invalid credential object';\r
76         return result;\r
77     }\r
78 \r
79     if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
80         result.err = 'Unknown algorithm';\r
81         return result;\r
82     }\r
83 \r
84     // Parse URI\r
85 \r
86     if (typeof uri === 'string') {\r
87         uri = Url.parse(uri);\r
88     }\r
89 \r
90     // Calculate signature\r
91 \r
92     var artifacts = {\r
93         ts: timestamp,\r
94         nonce: options.nonce || Cryptiles.randomString(6),\r
95         method: method,\r
96         resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'\r
97         host: uri.hostname,\r
98         port: uri.port || (uri.protocol === 'http:' ? 80 : 443),\r
99         hash: options.hash,\r
100         ext: options.ext,\r
101         app: options.app,\r
102         dlg: options.dlg\r
103     };\r
104 \r
105     result.artifacts = artifacts;\r
106 \r
107     // Calculate payload hash\r
108 \r
109     if (!artifacts.hash && (options.payload || options.payload === '')) {\r
110 \r
111         artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);\r
112     }\r
113 \r
114     var mac = Crypto.calculateMac('header', credentials, artifacts);\r
115 \r
116     // Construct header\r
117 \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
120 \r
121     if (artifacts.app) {\r
122         header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';\r
123     }\r
124 \r
125     result.field = header;\r
126 \r
127     return result;\r
128 };\r
129 \r
130 // Validate server response\r
131 \r
132 /*\r
133     res:        node's response object\r
134     artifacts:  object received from header().artifacts\r
135     options: {\r
136         payload:    optional payload received\r
137         required:   specifies if a Server-Authorization header is required. Defaults to 'false'\r
138     }\r
139 */\r
140 \r
141 exports.authenticate = function (res, credentials, artifacts, options) {\r
142 \r
143     artifacts = Hoek.clone(artifacts);\r
144     options = options || {};\r
145 \r
146     if (res.headers['www-authenticate']) {\r
147 \r
148         // Parse HTTP WWW-Authenticate header\r
149 \r
150         var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);\r
151         if (wwwAttributes instanceof Error) {\r
152             return false;\r
153         }\r
154 \r
155         // Validate server timestamp (not used to update clock since it is done via the SNPT client)\r
156 \r
157         if (wwwAttributes.ts) {\r
158             var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);\r
159             if (tsm !== wwwAttributes.tsm) {\r
160                 return false;\r
161             }\r
162         }\r
163     }\r
164 \r
165     // Parse HTTP Server-Authorization header\r
166 \r
167     if (!res.headers['server-authorization'] && !options.required) {\r
168 \r
169         return true;\r
170     }\r
171 \r
172     var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);\r
173     if (attributes instanceof Error) {\r
174         return false;\r
175     }\r
176 \r
177     artifacts.ext = attributes.ext;\r
178     artifacts.hash = attributes.hash;\r
179 \r
180     var mac = Crypto.calculateMac('response', credentials, artifacts);\r
181     if (mac !== attributes.mac) {\r
182         return false;\r
183     }\r
184 \r
185     if (!options.payload && options.payload !== '') {\r
186 \r
187         return true;\r
188     }\r
189 \r
190     if (!attributes.hash) {\r
191         return false;\r
192     }\r
193 \r
194     var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);\r
195     return calculatedHash === attributes.hash;\r
196 };\r
197 \r
198 // Generate a bewit value for a given URI\r
199 \r
200 /*\r
201     uri: 'http://example.com/resource?a=b' or object from Url.parse()\r
202     options: {\r
203 \r
204         // Required\r
205 \r
206         credentials: {\r
207             id: 'dh37fgj492je',\r
208             key: 'aoijedoaijsdlaksjdl',\r
209             algorithm: 'sha256'                             // 'sha1', 'sha256'\r
210         },\r
211         ttlSec: 60 * 60,                                    // TTL in seconds\r
212 \r
213         // Optional\r
214 \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
217     };\r
218 */\r
219 \r
220 exports.getBewit = function (uri, options) {\r
221 \r
222     // Validate inputs\r
223 \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
225 \r
226         return '';\r
227     }\r
228 \r
229     options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value\r
230 \r
231     // Application time\r
232 \r
233     var now = Utils.now(options.localtimeOffsetMsec);\r
234 \r
235     // Validate credentials\r
236 \r
237     var credentials = options.credentials;\r
238     if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {\r
239 \r
240         return '';\r
241     }\r
242 \r
243     if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
244         return '';\r
245     }\r
246 \r
247     // Parse URI\r
248 \r
249     if (typeof uri === 'string') {\r
250         uri = Url.parse(uri);\r
251     }\r
252 \r
253     // Calculate signature\r
254 \r
255     var exp = Math.floor(now / 1000) + options.ttlSec;\r
256     var mac = Crypto.calculateMac('bewit', credentials, {\r
257         ts: exp,\r
258         nonce: '',\r
259         method: 'GET',\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
263         ext: options.ext\r
264     });\r
265 \r
266     // Construct bewit: id\exp\mac\ext\r
267 \r
268     var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;\r
269     return Hoek.base64urlEncode(bewit);\r
270 };\r
271 \r
272 // Generate an authorization string for a message\r
273 \r
274 /*\r
275     host: 'example.com',\r
276     port: 8000,\r
277     message: '{"some":"payload"}',                          // UTF-8 encoded string for body hash generation\r
278     options: {\r
279 \r
280         // Required\r
281 \r
282         credentials: {\r
283             id: 'dh37fgj492je',\r
284             key: 'aoijedoaijsdlaksjdl',\r
285             algorithm: 'sha256'                             // 'sha1', 'sha256'\r
286         },\r
287 \r
288         // Optional\r
289 \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
293     }\r
294 */\r
295 \r
296 exports.message = function (host, port, message, options) {\r
297 \r
298     // Validate inputs\r
299 \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
301 \r
302         return null;\r
303     }\r
304 \r
305     // Application time\r
306 \r
307     var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);\r
308 \r
309     // Validate credentials\r
310 \r
311     var credentials = options.credentials;\r
312     if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {\r
313 \r
314         // Invalid credential object\r
315         return null;\r
316     }\r
317 \r
318     if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
319         return null;\r
320     }\r
321 \r
322     // Calculate signature\r
323 \r
324     var artifacts = {\r
325         ts: timestamp,\r
326         nonce: options.nonce || Cryptiles.randomString(6),\r
327         host: host,\r
328         port: port,\r
329         hash: Crypto.calculatePayloadHash(message, credentials.algorithm)\r
330     };\r
331 \r
332     // Construct authorization\r
333 \r
334     var result = {\r
335         id: credentials.id,\r
336         ts: artifacts.ts,\r
337         nonce: artifacts.nonce,\r
338         hash: artifacts.hash,\r
339         mac: Crypto.calculateMac('message', credentials, artifacts)\r
340     };\r
341 \r
342     return result;\r
343 };\r