Initial commit
[yaffs-website] / node_modules / hawk / test / browser.js
1 // Load modules\r
2 \r
3 var Url = require('url');\r
4 var Code = require('code');\r
5 var Hawk = require('../lib');\r
6 var Hoek = require('hoek');\r
7 var Lab = require('lab');\r
8 var Browser = require('../lib/browser');\r
9 \r
10 \r
11 // Declare internals\r
12 \r
13 var internals = {};\r
14 \r
15 \r
16 // Test shortcuts\r
17 \r
18 var lab = exports.lab = Lab.script();\r
19 var describe = lab.experiment;\r
20 var it = lab.test;\r
21 var expect = Code.expect;\r
22 \r
23 \r
24 describe('Browser', function () {\r
25 \r
26     var credentialsFunc = function (id, callback) {\r
27 \r
28         var credentials = {\r
29             id: id,\r
30             key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
31             algorithm: (id === '1' ? 'sha1' : 'sha256'),\r
32             user: 'steve'\r
33         };\r
34 \r
35         return callback(null, credentials);\r
36     };\r
37 \r
38     it('should generate a bewit then successfully authenticate it', function (done) {\r
39 \r
40         var req = {\r
41             method: 'GET',\r
42             url: '/resource/4?a=1&b=2',\r
43             host: 'example.com',\r
44             port: 80\r
45         };\r
46 \r
47         credentialsFunc('123456', function (err, credentials1) {\r
48 \r
49             var bewit = Browser.client.bewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });\r
50             req.url += '&bewit=' + bewit;\r
51 \r
52             Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials2, attributes) {\r
53 \r
54                 expect(err).to.not.exist();\r
55                 expect(credentials2.user).to.equal('steve');\r
56                 expect(attributes.ext).to.equal('some-app-data');\r
57                 done();\r
58             });\r
59         });\r
60     });\r
61 \r
62     it('should generate a bewit then successfully authenticate it (no ext)', function (done) {\r
63 \r
64         var req = {\r
65             method: 'GET',\r
66             url: '/resource/4?a=1&b=2',\r
67             host: 'example.com',\r
68             port: 80\r
69         };\r
70 \r
71         credentialsFunc('123456', function (err, credentials1) {\r
72 \r
73             var bewit = Browser.client.bewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100 });\r
74             req.url += '&bewit=' + bewit;\r
75 \r
76             Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials2, attributes) {\r
77 \r
78                 expect(err).to.not.exist();\r
79                 expect(credentials2.user).to.equal('steve');\r
80                 done();\r
81             });\r
82         });\r
83     });\r
84 \r
85     describe('bewit()', function () {\r
86 \r
87         it('returns a valid bewit value', function (done) {\r
88 \r
89             var credentials = {\r
90                 id: '123456',\r
91                 key: '2983d45yun89q',\r
92                 algorithm: 'sha256'\r
93             };\r
94 \r
95             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });\r
96             expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');\r
97             done();\r
98         });\r
99 \r
100         it('returns a valid bewit value (explicit HTTP port)', function (done) {\r
101 \r
102             var credentials = {\r
103                 id: '123456',\r
104                 key: '2983d45yun89q',\r
105                 algorithm: 'sha256'\r
106             };\r
107 \r
108             var bewit = Browser.client.bewit('http://example.com:8080/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });\r
109             expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6');\r
110             done();\r
111         });\r
112 \r
113         it('returns a valid bewit value (explicit HTTPS port)', function (done) {\r
114 \r
115             var credentials = {\r
116                 id: '123456',\r
117                 key: '2983d45yun89q',\r
118                 algorithm: 'sha256'\r
119             };\r
120 \r
121             var bewit = Browser.client.bewit('https://example.com:8043/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });\r
122             expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcL2t4UjhwK0xSaTdvQTRnUXc3cWlxa3BiVHRKYkR4OEtRMC9HRUwvVytTUT1ceGFuZHlhbmR6');\r
123             done();\r
124         });\r
125 \r
126         it('returns a valid bewit value (null ext)', function (done) {\r
127 \r
128             var credentials = {\r
129                 id: '123456',\r
130                 key: '2983d45yun89q',\r
131                 algorithm: 'sha256'\r
132             };\r
133 \r
134             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null });\r
135             expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c');\r
136             done();\r
137         });\r
138 \r
139         it('errors on invalid options', function (done) {\r
140 \r
141             var credentials = {\r
142                 id: '123456',\r
143                 key: '2983d45yun89q',\r
144                 algorithm: 'sha256'\r
145             };\r
146 \r
147             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', 4);\r
148             expect(bewit).to.equal('');\r
149             done();\r
150         });\r
151 \r
152         it('errors on missing uri', function (done) {\r
153 \r
154             var credentials = {\r
155                 id: '123456',\r
156                 key: '2983d45yun89q',\r
157                 algorithm: 'sha256'\r
158             };\r
159 \r
160             var bewit = Browser.client.bewit('', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });\r
161             expect(bewit).to.equal('');\r
162             done();\r
163         });\r
164 \r
165         it('errors on invalid uri', function (done) {\r
166 \r
167             var credentials = {\r
168                 id: '123456',\r
169                 key: '2983d45yun89q',\r
170                 algorithm: 'sha256'\r
171             };\r
172 \r
173             var bewit = Browser.client.bewit(5, { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });\r
174             expect(bewit).to.equal('');\r
175             done();\r
176         });\r
177 \r
178         it('errors on invalid credentials (id)', function (done) {\r
179 \r
180             var credentials = {\r
181                 key: '2983d45yun89q',\r
182                 algorithm: 'sha256'\r
183             };\r
184 \r
185             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });\r
186             expect(bewit).to.equal('');\r
187             done();\r
188         });\r
189 \r
190         it('errors on missing credentials', function (done) {\r
191 \r
192             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' });\r
193             expect(bewit).to.equal('');\r
194             done();\r
195         });\r
196 \r
197         it('errors on invalid credentials (key)', function (done) {\r
198 \r
199             var credentials = {\r
200                 id: '123456',\r
201                 algorithm: 'sha256'\r
202             };\r
203 \r
204             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });\r
205             expect(bewit).to.equal('');\r
206             done();\r
207         });\r
208 \r
209         it('errors on invalid algorithm', function (done) {\r
210 \r
211             var credentials = {\r
212                 id: '123456',\r
213                 key: '2983d45yun89q',\r
214                 algorithm: 'hmac-sha-0'\r
215             };\r
216 \r
217             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, ext: 'xandyandz' });\r
218             expect(bewit).to.equal('');\r
219             done();\r
220         });\r
221 \r
222         it('errors on missing options', function (done) {\r
223 \r
224             var credentials = {\r
225                 id: '123456',\r
226                 key: '2983d45yun89q',\r
227                 algorithm: 'hmac-sha-0'\r
228             };\r
229 \r
230             var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow');\r
231             expect(bewit).to.equal('');\r
232             done();\r
233         });\r
234     });\r
235 \r
236     it('generates a header then successfully parse it (configuration)', function (done) {\r
237 \r
238         var req = {\r
239             method: 'GET',\r
240             url: '/resource/4?filter=a',\r
241             host: 'example.com',\r
242             port: 8080\r
243         };\r
244 \r
245         credentialsFunc('123456', function (err, credentials1) {\r
246 \r
247             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' }).field;\r
248             expect(req.authorization).to.exist();\r
249 \r
250             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
251 \r
252                 expect(err).to.not.exist();\r
253                 expect(credentials2.user).to.equal('steve');\r
254                 expect(artifacts.ext).to.equal('some-app-data');\r
255                 done();\r
256             });\r
257         });\r
258     });\r
259 \r
260     it('generates a header then successfully parse it (node request)', function (done) {\r
261 \r
262         var req = {\r
263             method: 'POST',\r
264             url: '/resource/4?filter=a',\r
265             headers: {\r
266                 host: 'example.com:8080',\r
267                 'content-type': 'text/plain;x=y'\r
268             }\r
269         };\r
270 \r
271         var payload = 'some not so random text';\r
272 \r
273         credentialsFunc('123456', function (err, credentials1) {\r
274 \r
275             var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });\r
276             req.headers.authorization = reqHeader.field;\r
277 \r
278             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
279 \r
280                 expect(err).to.not.exist();\r
281                 expect(credentials2.user).to.equal('steve');\r
282                 expect(artifacts.ext).to.equal('some-app-data');\r
283                 expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);\r
284 \r
285                 var res = {\r
286                     headers: {\r
287                         'content-type': 'text/plain'\r
288                     },\r
289                     getResponseHeader: function (header) {\r
290 \r
291                         return res.headers[header.toLowerCase()];\r
292                     }\r
293                 };\r
294 \r
295                 res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });\r
296                 expect(res.headers['server-authorization']).to.exist();\r
297 \r
298                 expect(Browser.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);\r
299                 done();\r
300             });\r
301         });\r
302     });\r
303 \r
304     it('generates a header then successfully parse it (browserify)', function (done) {\r
305 \r
306         var req = {\r
307             method: 'POST',\r
308             url: '/resource/4?filter=a',\r
309             headers: {\r
310                 host: 'example.com:8080',\r
311                 'content-type': 'text/plain;x=y'\r
312             }\r
313         };\r
314 \r
315         var payload = 'some not so random text';\r
316 \r
317         credentialsFunc('123456', function (err, credentials1) {\r
318 \r
319             var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });\r
320             req.headers.authorization = reqHeader.field;\r
321 \r
322             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
323 \r
324                 expect(err).to.not.exist();\r
325                 expect(credentials2.user).to.equal('steve');\r
326                 expect(artifacts.ext).to.equal('some-app-data');\r
327                 expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);\r
328 \r
329                 var res = {\r
330                     headers: {\r
331                         'content-type': 'text/plain'\r
332                     },\r
333                     getHeader: function (header) {\r
334 \r
335                         return res.headers[header.toLowerCase()];\r
336                     }\r
337                 };\r
338 \r
339                 res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });\r
340                 expect(res.headers['server-authorization']).to.exist();\r
341 \r
342                 expect(Browser.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);\r
343                 done();\r
344             });\r
345         });\r
346     });\r
347 \r
348     it('generates a header then successfully parse it (time offset)', function (done) {\r
349 \r
350         var req = {\r
351             method: 'GET',\r
352             url: '/resource/4?filter=a',\r
353             host: 'example.com',\r
354             port: 8080\r
355         };\r
356 \r
357         credentialsFunc('123456', function (err, credentials1) {\r
358 \r
359             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', localtimeOffsetMsec: 100000 }).field;\r
360             expect(req.authorization).to.exist();\r
361 \r
362             Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 100000 }, function (err, credentials2, artifacts) {\r
363 \r
364                 expect(err).to.not.exist();\r
365                 expect(credentials2.user).to.equal('steve');\r
366                 expect(artifacts.ext).to.equal('some-app-data');\r
367                 done();\r
368             });\r
369         });\r
370     });\r
371 \r
372     it('generates a header then successfully parse it (no server header options)', function (done) {\r
373 \r
374         var req = {\r
375             method: 'POST',\r
376             url: '/resource/4?filter=a',\r
377             headers: {\r
378                 host: 'example.com:8080',\r
379                 'content-type': 'text/plain;x=y'\r
380             }\r
381         };\r
382 \r
383         var payload = 'some not so random text';\r
384 \r
385         credentialsFunc('123456', function (err, credentials1) {\r
386 \r
387             var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });\r
388             req.headers.authorization = reqHeader.field;\r
389 \r
390             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
391 \r
392                 expect(err).to.not.exist();\r
393                 expect(credentials2.user).to.equal('steve');\r
394                 expect(artifacts.ext).to.equal('some-app-data');\r
395                 expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);\r
396 \r
397                 var res = {\r
398                     headers: {\r
399                         'content-type': 'text/plain'\r
400                     },\r
401                     getResponseHeader: function (header) {\r
402 \r
403                         return res.headers[header.toLowerCase()];\r
404                     }\r
405                 };\r
406 \r
407                 res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);\r
408                 expect(res.headers['server-authorization']).to.exist();\r
409 \r
410                 expect(Browser.client.authenticate(res, credentials2, artifacts)).to.equal(true);\r
411                 done();\r
412             });\r
413         });\r
414     });\r
415 \r
416     it('generates a header then successfully parse it (no server header)', function (done) {\r
417 \r
418         var req = {\r
419             method: 'POST',\r
420             url: '/resource/4?filter=a',\r
421             headers: {\r
422                 host: 'example.com:8080',\r
423                 'content-type': 'text/plain;x=y'\r
424             }\r
425         };\r
426 \r
427         var payload = 'some not so random text';\r
428 \r
429         credentialsFunc('123456', function (err, credentials1) {\r
430 \r
431             var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });\r
432             req.headers.authorization = reqHeader.field;\r
433 \r
434             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
435 \r
436                 expect(err).to.not.exist();\r
437                 expect(credentials2.user).to.equal('steve');\r
438                 expect(artifacts.ext).to.equal('some-app-data');\r
439                 expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);\r
440 \r
441                 var res = {\r
442                     headers: {\r
443                         'content-type': 'text/plain'\r
444                     },\r
445                     getResponseHeader: function (header) {\r
446 \r
447                         return res.headers[header.toLowerCase()];\r
448                     }\r
449                 };\r
450 \r
451                 expect(Browser.client.authenticate(res, credentials2, artifacts)).to.equal(true);\r
452                 done();\r
453             });\r
454         });\r
455     });\r
456 \r
457     it('generates a header with stale ts and successfully authenticate on second call', function (done) {\r
458 \r
459         var req = {\r
460             method: 'GET',\r
461             url: '/resource/4?filter=a',\r
462             host: 'example.com',\r
463             port: 8080\r
464         };\r
465 \r
466         credentialsFunc('123456', function (err, credentials1) {\r
467 \r
468             Browser.utils.setNtpOffset(60 * 60 * 1000);\r
469             var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' });\r
470             req.authorization = header.field;\r
471             expect(req.authorization).to.exist();\r
472 \r
473             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts2) {\r
474 \r
475                 expect(err).to.exist();\r
476                 expect(err.message).to.equal('Stale timestamp');\r
477 \r
478                 var res = {\r
479                     headers: {\r
480                         'www-authenticate': err.output.headers['WWW-Authenticate']\r
481                     },\r
482                     getResponseHeader: function (lookup) {\r
483 \r
484                         return res.headers[lookup.toLowerCase()];\r
485                     }\r
486                 };\r
487 \r
488                 expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);\r
489                 expect(Browser.client.authenticate(res, credentials2, header.artifacts)).to.equal(true);\r
490                 expect(Browser.utils.getNtpOffset()).to.equal(0);\r
491 \r
492                 req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials2, ext: 'some-app-data' }).field;\r
493                 expect(req.authorization).to.exist();\r
494 \r
495                 Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials3, artifacts3) {\r
496 \r
497                     expect(err).to.not.exist();\r
498                     expect(credentials3.user).to.equal('steve');\r
499                     expect(artifacts3.ext).to.equal('some-app-data');\r
500                     done();\r
501                 });\r
502             });\r
503         });\r
504     });\r
505 \r
506     it('generates a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) {\r
507 \r
508         var req = {\r
509             method: 'GET',\r
510             url: '/resource/4?filter=a',\r
511             host: 'example.com',\r
512             port: 8080\r
513         };\r
514 \r
515         credentialsFunc('123456', function (err, credentials1) {\r
516 \r
517             var localStorage = new Browser.internals.LocalStorage();\r
518 \r
519             Browser.utils.setStorage(localStorage);\r
520 \r
521             Browser.utils.setNtpOffset(60 * 60 * 1000);\r
522             var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' });\r
523             req.authorization = header.field;\r
524             expect(req.authorization).to.exist();\r
525 \r
526             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts2) {\r
527 \r
528                 expect(err).to.exist();\r
529                 expect(err.message).to.equal('Stale timestamp');\r
530 \r
531                 var res = {\r
532                     headers: {\r
533                         'www-authenticate': err.output.headers['WWW-Authenticate']\r
534                     },\r
535                     getResponseHeader: function (lookup) {\r
536 \r
537                         return res.headers[lookup.toLowerCase()];\r
538                     }\r
539                 };\r
540 \r
541                 expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000);\r
542                 expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);\r
543                 expect(Browser.client.authenticate(res, credentials2, header.artifacts)).to.equal(true);\r
544                 expect(Browser.utils.getNtpOffset()).to.equal(0);\r
545                 expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(0);\r
546 \r
547                 req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials2, ext: 'some-app-data' }).field;\r
548                 expect(req.authorization).to.exist();\r
549 \r
550                 Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials3, artifacts3) {\r
551 \r
552                     expect(err).to.not.exist();\r
553                     expect(credentials3.user).to.equal('steve');\r
554                     expect(artifacts3.ext).to.equal('some-app-data');\r
555                     done();\r
556                 });\r
557             });\r
558         });\r
559     });\r
560 \r
561     it('generates a header then fails to parse it (missing server header hash)', function (done) {\r
562 \r
563         var req = {\r
564             method: 'POST',\r
565             url: '/resource/4?filter=a',\r
566             headers: {\r
567                 host: 'example.com:8080',\r
568                 'content-type': 'text/plain;x=y'\r
569             }\r
570         };\r
571 \r
572         var payload = 'some not so random text';\r
573 \r
574         credentialsFunc('123456', function (err, credentials1) {\r
575 \r
576             var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });\r
577             req.headers.authorization = reqHeader.field;\r
578 \r
579             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
580 \r
581                 expect(err).to.not.exist();\r
582                 expect(credentials2.user).to.equal('steve');\r
583                 expect(artifacts.ext).to.equal('some-app-data');\r
584                 expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);\r
585 \r
586                 var res = {\r
587                     headers: {\r
588                         'content-type': 'text/plain'\r
589                     },\r
590                     getResponseHeader: function (header) {\r
591 \r
592                         return res.headers[header.toLowerCase()];\r
593                     }\r
594                 };\r
595 \r
596                 res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);\r
597                 expect(res.headers['server-authorization']).to.exist();\r
598 \r
599                 expect(Browser.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(false);\r
600                 done();\r
601             });\r
602         });\r
603     });\r
604 \r
605     it('generates a header then successfully parse it (with hash)', function (done) {\r
606 \r
607         var req = {\r
608             method: 'GET',\r
609             url: '/resource/4?filter=a',\r
610             host: 'example.com',\r
611             port: 8080\r
612         };\r
613 \r
614         credentialsFunc('123456', function (err, credentials1) {\r
615 \r
616             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;\r
617             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
618 \r
619                 expect(err).to.not.exist();\r
620                 expect(credentials2.user).to.equal('steve');\r
621                 expect(artifacts.ext).to.equal('some-app-data');\r
622                 done();\r
623             });\r
624         });\r
625     });\r
626 \r
627     it('generates a header then successfully parse it then validate payload', function (done) {\r
628 \r
629         var req = {\r
630             method: 'GET',\r
631             url: '/resource/4?filter=a',\r
632             host: 'example.com',\r
633             port: 8080\r
634         };\r
635 \r
636         credentialsFunc('123456', function (err, credentials1) {\r
637 \r
638             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;\r
639             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
640 \r
641                 expect(err).to.not.exist();\r
642                 expect(credentials2.user).to.equal('steve');\r
643                 expect(artifacts.ext).to.equal('some-app-data');\r
644                 expect(Hawk.server.authenticatePayload('hola!', credentials2, artifacts)).to.be.true();\r
645                 expect(Hawk.server.authenticatePayload('hello!', credentials2, artifacts)).to.be.false();\r
646                 done();\r
647             });\r
648         });\r
649     });\r
650 \r
651     it('generates a header then successfully parse it (app)', function (done) {\r
652 \r
653         var req = {\r
654             method: 'GET',\r
655             url: '/resource/4?filter=a',\r
656             host: 'example.com',\r
657             port: 8080\r
658         };\r
659 \r
660         credentialsFunc('123456', function (err, credentials1) {\r
661 \r
662             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased' }).field;\r
663             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
664 \r
665                 expect(err).to.not.exist();\r
666                 expect(credentials2.user).to.equal('steve');\r
667                 expect(artifacts.ext).to.equal('some-app-data');\r
668                 expect(artifacts.app).to.equal('asd23ased');\r
669                 done();\r
670             });\r
671         });\r
672     });\r
673 \r
674     it('generates a header then successfully parse it (app, dlg)', function (done) {\r
675 \r
676         var req = {\r
677             method: 'GET',\r
678             url: '/resource/4?filter=a',\r
679             host: 'example.com',\r
680             port: 8080\r
681         };\r
682 \r
683         credentialsFunc('123456', function (err, credentials1) {\r
684 \r
685             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;\r
686             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
687 \r
688                 expect(err).to.not.exist();\r
689                 expect(credentials2.user).to.equal('steve');\r
690                 expect(artifacts.ext).to.equal('some-app-data');\r
691                 expect(artifacts.app).to.equal('asd23ased');\r
692                 expect(artifacts.dlg).to.equal('23434szr3q4d');\r
693                 done();\r
694             });\r
695         });\r
696     });\r
697 \r
698     it('generates a header then fail authentication due to bad hash', function (done) {\r
699 \r
700         var req = {\r
701             method: 'GET',\r
702             url: '/resource/4?filter=a',\r
703             host: 'example.com',\r
704             port: 8080\r
705         };\r
706 \r
707         credentialsFunc('123456', function (err, credentials1) {\r
708 \r
709             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;\r
710             Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials2, artifacts) {\r
711 \r
712                 expect(err).to.exist();\r
713                 expect(err.output.payload.message).to.equal('Bad payload hash');\r
714                 done();\r
715             });\r
716         });\r
717     });\r
718 \r
719     it('generates a header for one resource then fail to authenticate another', function (done) {\r
720 \r
721         var req = {\r
722             method: 'GET',\r
723             url: '/resource/4?filter=a',\r
724             host: 'example.com',\r
725             port: 8080\r
726         };\r
727 \r
728         credentialsFunc('123456', function (err, credentials1) {\r
729 \r
730             req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' }).field;\r
731             req.url = '/something/else';\r
732 \r
733             Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {\r
734 \r
735                 expect(err).to.exist();\r
736                 expect(credentials2).to.exist();\r
737                 done();\r
738             });\r
739         });\r
740     });\r
741 \r
742     describe('client', function () {\r
743 \r
744         describe('header()', function () {\r
745 \r
746             it('returns a valid authorization header (sha1)', function (done) {\r
747 \r
748                 var credentials = {\r
749                     id: '123456',\r
750                     key: '2983d45yun89q',\r
751                     algorithm: 'sha1'\r
752                 };\r
753 \r
754                 var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;\r
755                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');\r
756                 done();\r
757             });\r
758 \r
759             it('returns a valid authorization header (sha256)', function (done) {\r
760 \r
761                 var credentials = {\r
762                     id: '123456',\r
763                     key: '2983d45yun89q',\r
764                     algorithm: 'sha256'\r
765                 };\r
766 \r
767                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;\r
768                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');\r
769                 done();\r
770             });\r
771 \r
772             it('returns a valid authorization header (empty payload)', function (done) {\r
773 \r
774                 var credentials = {\r
775                     id: '123456',\r
776                     key: '2983d45yun89q',\r
777                     algorithm: 'sha1'\r
778                 };\r
779 \r
780                 var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: '' }).field;\r
781                 expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"404ghL7K+hfyhByKKejFBRGgTjU=\", ext=\"Bazinga!\", mac=\"Bh1sj1DOfFRWOdi3ww52nLCJdBE=\"');\r
782                 done();\r
783             });\r
784 \r
785             it('returns a valid authorization header (no ext)', function (done) {\r
786 \r
787                 var credentials = {\r
788                     id: '123456',\r
789                     key: '2983d45yun89q',\r
790                     algorithm: 'sha256'\r
791                 };\r
792 \r
793                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;\r
794                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');\r
795                 done();\r
796             });\r
797 \r
798             it('returns a valid authorization header (null ext)', function (done) {\r
799 \r
800                 var credentials = {\r
801                     id: '123456',\r
802                     key: '2983d45yun89q',\r
803                     algorithm: 'sha256'\r
804                 };\r
805 \r
806                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field;\r
807                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');\r
808                 done();\r
809             });\r
810 \r
811             it('returns a valid authorization header (uri object)', function (done) {\r
812 \r
813                 var credentials = {\r
814                     id: '123456',\r
815                     key: '2983d45yun89q',\r
816                     algorithm: 'sha256'\r
817                 };\r
818 \r
819                 var uri = Browser.utils.parseUri('https://example.net/somewhere/over/the/rainbow');\r
820                 var header = Browser.client.header(uri, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;\r
821                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');\r
822                 done();\r
823             });\r
824 \r
825             it('errors on missing options', function (done) {\r
826 \r
827                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST');\r
828                 expect(header.field).to.equal('');\r
829                 expect(header.err).to.equal('Invalid argument type');\r
830                 done();\r
831             });\r
832 \r
833             it('errors on empty uri', function (done) {\r
834 \r
835                 var credentials = {\r
836                     id: '123456',\r
837                     key: '2983d45yun89q',\r
838                     algorithm: 'sha256'\r
839                 };\r
840 \r
841                 var header = Browser.client.header('', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });\r
842                 expect(header.field).to.equal('');\r
843                 expect(header.err).to.equal('Invalid argument type');\r
844                 done();\r
845             });\r
846 \r
847             it('errors on invalid uri', function (done) {\r
848 \r
849                 var credentials = {\r
850                     id: '123456',\r
851                     key: '2983d45yun89q',\r
852                     algorithm: 'sha256'\r
853                 };\r
854 \r
855                 var header = Browser.client.header(4, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });\r
856                 expect(header.field).to.equal('');\r
857                 expect(header.err).to.equal('Invalid argument type');\r
858                 done();\r
859             });\r
860 \r
861             it('errors on missing method', function (done) {\r
862 \r
863                 var credentials = {\r
864                     id: '123456',\r
865                     key: '2983d45yun89q',\r
866                     algorithm: 'sha256'\r
867                 };\r
868 \r
869                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', '', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });\r
870                 expect(header.field).to.equal('');\r
871                 expect(header.err).to.equal('Invalid argument type');\r
872                 done();\r
873             });\r
874 \r
875             it('errors on invalid method', function (done) {\r
876 \r
877                 var credentials = {\r
878                     id: '123456',\r
879                     key: '2983d45yun89q',\r
880                     algorithm: 'sha256'\r
881                 };\r
882 \r
883                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 5, { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });\r
884                 expect(header.field).to.equal('');\r
885                 expect(header.err).to.equal('Invalid argument type');\r
886                 done();\r
887             });\r
888 \r
889             it('errors on missing credentials', function (done) {\r
890 \r
891                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 });\r
892                 expect(header.field).to.equal('');\r
893                 expect(header.err).to.equal('Invalid credentials object');\r
894                 done();\r
895             });\r
896 \r
897             it('errors on invalid credentials (id)', function (done) {\r
898 \r
899                 var credentials = {\r
900                     key: '2983d45yun89q',\r
901                     algorithm: 'sha256'\r
902                 };\r
903 \r
904                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });\r
905                 expect(header.field).to.equal('');\r
906                 expect(header.err).to.equal('Invalid credentials object');\r
907                 done();\r
908             });\r
909 \r
910             it('errors on invalid credentials (key)', function (done) {\r
911 \r
912                 var credentials = {\r
913                     id: '123456',\r
914                     algorithm: 'sha256'\r
915                 };\r
916 \r
917                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });\r
918                 expect(header.field).to.equal('');\r
919                 expect(header.err).to.equal('Invalid credentials object');\r
920                 done();\r
921             });\r
922 \r
923             it('errors on invalid algorithm', function (done) {\r
924 \r
925                 var credentials = {\r
926                     id: '123456',\r
927                     key: '2983d45yun89q',\r
928                     algorithm: 'hmac-sha-0'\r
929                 };\r
930 \r
931                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });\r
932                 expect(header.field).to.equal('');\r
933                 expect(header.err).to.equal('Unknown algorithm');\r
934                 done();\r
935             });\r
936 \r
937             it('uses a pre-calculated payload hash', function (done) {\r
938 \r
939                 var credentials = {\r
940                     id: '123456',\r
941                     key: '2983d45yun89q',\r
942                     algorithm: 'sha256'\r
943                 };\r
944 \r
945                 var options = { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };\r
946                 options.hash = Browser.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);\r
947                 var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;\r
948                 expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');\r
949                 done();\r
950             });\r
951         });\r
952 \r
953         describe('authenticate()', function () {\r
954 \r
955             it('skips tsm validation when missing ts', function (done) {\r
956 \r
957                 var res = {\r
958                     headers: {\r
959                         'www-authenticate': 'Hawk error="Stale timestamp"'\r
960                     },\r
961                     getResponseHeader: function (header) {\r
962 \r
963                         return res.headers[header.toLowerCase()];\r
964                     }\r
965                 };\r
966 \r
967                 var credentials = {\r
968                     id: '123456',\r
969                     key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
970                     algorithm: 'sha256',\r
971                     user: 'steve'\r
972                 };\r
973 \r
974                 var artifacts = {\r
975                     ts: 1402135580,\r
976                     nonce: 'iBRB6t',\r
977                     method: 'GET',\r
978                     resource: '/resource/4?filter=a',\r
979                     host: 'example.com',\r
980                     port: '8080',\r
981                     ext: 'some-app-data'\r
982                 };\r
983 \r
984                 expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);\r
985                 done();\r
986             });\r
987 \r
988             it('returns false on invalid header', function (done) {\r
989 \r
990                 var res = {\r
991                     headers: {\r
992                         'server-authorization': 'Hawk mac="abc", bad="xyz"'\r
993                     },\r
994                     getResponseHeader: function (header) {\r
995 \r
996                         return res.headers[header.toLowerCase()];\r
997                     }\r
998                 };\r
999 \r
1000                 expect(Browser.client.authenticate(res, {})).to.equal(false);\r
1001                 done();\r
1002             });\r
1003 \r
1004             it('returns false on invalid mac', function (done) {\r
1005 \r
1006                 var res = {\r
1007                     headers: {\r
1008                         'content-type': 'text/plain',\r
1009                         'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'\r
1010                     },\r
1011                     getResponseHeader: function (header) {\r
1012 \r
1013                         return res.headers[header.toLowerCase()];\r
1014                     }\r
1015                 };\r
1016 \r
1017                 var artifacts = {\r
1018                     method: 'POST',\r
1019                     host: 'example.com',\r
1020                     port: '8080',\r
1021                     resource: '/resource/4?filter=a',\r
1022                     ts: '1362336900',\r
1023                     nonce: 'eb5S_L',\r
1024                     hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',\r
1025                     ext: 'some-app-data',\r
1026                     app: undefined,\r
1027                     dlg: undefined,\r
1028                     mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',\r
1029                     id: '123456'\r
1030                 };\r
1031 \r
1032                 var credentials = {\r
1033                     id: '123456',\r
1034                     key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
1035                     algorithm: 'sha256',\r
1036                     user: 'steve'\r
1037                 };\r
1038 \r
1039                 expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(false);\r
1040                 done();\r
1041             });\r
1042 \r
1043             it('returns true on ignoring hash', function (done) {\r
1044 \r
1045                 var res = {\r
1046                     headers: {\r
1047                         'content-type': 'text/plain',\r
1048                         'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'\r
1049                     },\r
1050                     getResponseHeader: function (header) {\r
1051 \r
1052                         return res.headers[header.toLowerCase()];\r
1053                     }\r
1054                 };\r
1055 \r
1056                 var artifacts = {\r
1057                     method: 'POST',\r
1058                     host: 'example.com',\r
1059                     port: '8080',\r
1060                     resource: '/resource/4?filter=a',\r
1061                     ts: '1362336900',\r
1062                     nonce: 'eb5S_L',\r
1063                     hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',\r
1064                     ext: 'some-app-data',\r
1065                     app: undefined,\r
1066                     dlg: undefined,\r
1067                     mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',\r
1068                     id: '123456'\r
1069                 };\r
1070 \r
1071                 var credentials = {\r
1072                     id: '123456',\r
1073                     key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
1074                     algorithm: 'sha256',\r
1075                     user: 'steve'\r
1076                 };\r
1077 \r
1078                 expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);\r
1079                 done();\r
1080             });\r
1081 \r
1082             it('errors on invalid WWW-Authenticate header format', function (done) {\r
1083 \r
1084                 var res = {\r
1085                     headers: {\r
1086                         'www-authenticate': 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"'\r
1087                     },\r
1088                     getResponseHeader: function (header) {\r
1089 \r
1090                         return res.headers[header.toLowerCase()];\r
1091                     }\r
1092                 };\r
1093 \r
1094                 expect(Browser.client.authenticate(res, {})).to.equal(false);\r
1095                 done();\r
1096             });\r
1097 \r
1098             it('errors on invalid WWW-Authenticate header format', function (done) {\r
1099 \r
1100                 var credentials = {\r
1101                     id: '123456',\r
1102                     key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
1103                     algorithm: 'sha256',\r
1104                     user: 'steve'\r
1105                 };\r
1106 \r
1107                 var res = {\r
1108                     headers: {\r
1109                         'www-authenticate': 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"'\r
1110                     },\r
1111                     getResponseHeader: function (header) {\r
1112 \r
1113                         return res.headers[header.toLowerCase()];\r
1114                     }\r
1115                 };\r
1116 \r
1117                 expect(Browser.client.authenticate(res, credentials)).to.equal(false);\r
1118                 done();\r
1119             });\r
1120         });\r
1121 \r
1122         describe('message()', function () {\r
1123 \r
1124             it('generates an authorization then successfully parse it', function (done) {\r
1125 \r
1126                 credentialsFunc('123456', function (err, credentials1) {\r
1127 \r
1128                     var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials1 });\r
1129                     expect(auth).to.exist();\r
1130 \r
1131                     Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials2) {\r
1132 \r
1133                         expect(err).to.not.exist();\r
1134                         expect(credentials2.user).to.equal('steve');\r
1135                         done();\r
1136                     });\r
1137                 });\r
1138             });\r
1139 \r
1140             it('generates an authorization using custom nonce/timestamp', function (done) {\r
1141 \r
1142                 credentialsFunc('123456', function (err, credentials) {\r
1143 \r
1144                     var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials, nonce: 'abc123', timestamp: 1398536270957 });\r
1145                     expect(auth).to.exist();\r
1146                     expect(auth.nonce).to.equal('abc123');\r
1147                     expect(auth.ts).to.equal(1398536270957);\r
1148                     done();\r
1149                 });\r
1150             });\r
1151 \r
1152             it('errors on missing host', function (done) {\r
1153 \r
1154                 credentialsFunc('123456', function (err, credentials) {\r
1155 \r
1156                     var auth = Browser.client.message(null, 8080, 'some message', { credentials: credentials });\r
1157                     expect(auth).to.not.exist();\r
1158                     done();\r
1159                 });\r
1160             });\r
1161 \r
1162             it('errors on invalid host', function (done) {\r
1163 \r
1164                 credentialsFunc('123456', function (err, credentials) {\r
1165 \r
1166                     var auth = Browser.client.message(5, 8080, 'some message', { credentials: credentials });\r
1167                     expect(auth).to.not.exist();\r
1168                     done();\r
1169                 });\r
1170             });\r
1171 \r
1172             it('errors on missing port', function (done) {\r
1173 \r
1174                 credentialsFunc('123456', function (err, credentials) {\r
1175 \r
1176                     var auth = Browser.client.message('example.com', 0, 'some message', { credentials: credentials });\r
1177                     expect(auth).to.not.exist();\r
1178                     done();\r
1179                 });\r
1180             });\r
1181 \r
1182             it('errors on invalid port', function (done) {\r
1183 \r
1184                 credentialsFunc('123456', function (err, credentials) {\r
1185 \r
1186                     var auth = Browser.client.message('example.com', 'a', 'some message', { credentials: credentials });\r
1187                     expect(auth).to.not.exist();\r
1188                     done();\r
1189                 });\r
1190             });\r
1191 \r
1192             it('errors on missing message', function (done) {\r
1193 \r
1194                 credentialsFunc('123456', function (err, credentials) {\r
1195 \r
1196                     var auth = Browser.client.message('example.com', 8080, undefined, { credentials: credentials });\r
1197                     expect(auth).to.not.exist();\r
1198                     done();\r
1199                 });\r
1200             });\r
1201 \r
1202             it('errors on null message', function (done) {\r
1203 \r
1204                 credentialsFunc('123456', function (err, credentials) {\r
1205 \r
1206                     var auth = Browser.client.message('example.com', 8080, null, { credentials: credentials });\r
1207                     expect(auth).to.not.exist();\r
1208                     done();\r
1209                 });\r
1210             });\r
1211 \r
1212             it('errors on invalid message', function (done) {\r
1213 \r
1214                 credentialsFunc('123456', function (err, credentials) {\r
1215 \r
1216                     var auth = Browser.client.message('example.com', 8080, 5, { credentials: credentials });\r
1217                     expect(auth).to.not.exist();\r
1218                     done();\r
1219                 });\r
1220             });\r
1221 \r
1222             it('errors on missing credentials', function (done) {\r
1223 \r
1224                 var auth = Browser.client.message('example.com', 8080, 'some message', {});\r
1225                 expect(auth).to.not.exist();\r
1226                 done();\r
1227             });\r
1228 \r
1229             it('errors on missing options', function (done) {\r
1230 \r
1231                 var auth = Browser.client.message('example.com', 8080, 'some message');\r
1232                 expect(auth).to.not.exist();\r
1233                 done();\r
1234             });\r
1235 \r
1236             it('errors on invalid credentials (id)', function (done) {\r
1237 \r
1238                 credentialsFunc('123456', function (err, credentials) {\r
1239 \r
1240                     var creds = Hoek.clone(credentials);\r
1241                     delete creds.id;\r
1242                     var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });\r
1243                     expect(auth).to.not.exist();\r
1244                     done();\r
1245                 });\r
1246             });\r
1247 \r
1248             it('errors on invalid credentials (key)', function (done) {\r
1249 \r
1250                 credentialsFunc('123456', function (err, credentials) {\r
1251 \r
1252                     var creds = Hoek.clone(credentials);\r
1253                     delete creds.key;\r
1254                     var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });\r
1255                     expect(auth).to.not.exist();\r
1256                     done();\r
1257                 });\r
1258             });\r
1259 \r
1260             it('errors on invalid algorithm', function (done) {\r
1261 \r
1262                 credentialsFunc('123456', function (err, credentials) {\r
1263 \r
1264                     var creds = Hoek.clone(credentials);\r
1265                     creds.algorithm = 'blah';\r
1266                     var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });\r
1267                     expect(auth).to.not.exist();\r
1268                     done();\r
1269                 });\r
1270             });\r
1271         });\r
1272 \r
1273         describe('authenticateTimestamp()', function (done) {\r
1274 \r
1275             it('validates a timestamp', function (done) {\r
1276 \r
1277                 credentialsFunc('123456', function (err, credentials) {\r
1278 \r
1279                     var tsm = Hawk.crypto.timestampMessage(credentials);\r
1280                     expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(true);\r
1281                     done();\r
1282                 });\r
1283             });\r
1284 \r
1285             it('validates a timestamp without updating local time', function (done) {\r
1286 \r
1287                 credentialsFunc('123456', function (err, credentials) {\r
1288 \r
1289                     var offset = Browser.utils.getNtpOffset();\r
1290                     var tsm = Hawk.crypto.timestampMessage(credentials, 10000);\r
1291                     expect(Browser.client.authenticateTimestamp(tsm, credentials, false)).to.equal(true);\r
1292                     expect(offset).to.equal(Browser.utils.getNtpOffset());\r
1293                     done();\r
1294                 });\r
1295             });\r
1296 \r
1297             it('detects a bad timestamp', function (done) {\r
1298 \r
1299                 credentialsFunc('123456', function (err, credentials) {\r
1300 \r
1301                     var tsm = Hawk.crypto.timestampMessage(credentials);\r
1302                     tsm.ts = 4;\r
1303                     expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(false);\r
1304                     done();\r
1305                 });\r
1306             });\r
1307         });\r
1308     });\r
1309 \r
1310     describe('internals', function () {\r
1311 \r
1312         describe('LocalStorage', function () {\r
1313 \r
1314             it('goes through the full lifecycle', function (done) {\r
1315 \r
1316                 var storage = new Browser.internals.LocalStorage();\r
1317                 expect(storage.length).to.equal(0);\r
1318                 expect(storage.getItem('a')).to.equal(null);\r
1319                 storage.setItem('a', 5);\r
1320                 expect(storage.length).to.equal(1);\r
1321                 expect(storage.key()).to.equal('a');\r
1322                 expect(storage.key(0)).to.equal('a');\r
1323                 expect(storage.getItem('a')).to.equal('5');\r
1324                 storage.setItem('b', 'test');\r
1325                 expect(storage.key()).to.equal('a');\r
1326                 expect(storage.key(0)).to.equal('a');\r
1327                 expect(storage.key(1)).to.equal('b');\r
1328                 expect(storage.length).to.equal(2);\r
1329                 expect(storage.getItem('b')).to.equal('test');\r
1330                 storage.removeItem('a');\r
1331                 expect(storage.length).to.equal(1);\r
1332                 expect(storage.getItem('a')).to.equal(null);\r
1333                 expect(storage.getItem('b')).to.equal('test');\r
1334                 storage.clear();\r
1335                 expect(storage.length).to.equal(0);\r
1336                 expect(storage.getItem('a')).to.equal(null);\r
1337                 expect(storage.getItem('b')).to.equal(null);\r
1338                 done();\r
1339             });\r
1340         });\r
1341     });\r
1342 \r
1343     describe('utils', function () {\r
1344 \r
1345         describe('setStorage()', function () {\r
1346 \r
1347             it('sets storage for the first time', function (done) {\r
1348 \r
1349                 Browser.utils.storage = new Browser.internals.LocalStorage();        // Reset state\r
1350 \r
1351                 expect(Browser.utils.storage.getItem('hawk_ntp_offset')).to.not.exist();\r
1352                 Browser.utils.storage.setItem('test', '1');\r
1353                 Browser.utils.setStorage(new Browser.internals.LocalStorage());\r
1354                 expect(Browser.utils.storage.getItem('test')).to.not.exist();\r
1355                 Browser.utils.storage.setItem('test', '2');\r
1356                 expect(Browser.utils.storage.getItem('test')).to.equal('2');\r
1357                 done();\r
1358             });\r
1359         });\r
1360 \r
1361         describe('setNtpOffset()', function (done) {\r
1362 \r
1363             it('catches localStorage errors', { parallel: false }, function (done) {\r
1364 \r
1365                 var orig = Browser.utils.storage.setItem;\r
1366                 var consoleOrig = console.error;\r
1367                 var count = 0;\r
1368                 console.error = function () {\r
1369 \r
1370                     if (count++ === 2) {\r
1371 \r
1372                         console.error = consoleOrig;\r
1373                     }\r
1374                 };\r
1375 \r
1376                 Browser.utils.storage.setItem = function () {\r
1377 \r
1378                     Browser.utils.storage.setItem = orig;\r
1379                     throw new Error();\r
1380                 };\r
1381 \r
1382                 expect(function () {\r
1383 \r
1384                     Browser.utils.setNtpOffset(100);\r
1385                 }).not.to.throw();\r
1386 \r
1387                 done();\r
1388             });\r
1389         });\r
1390 \r
1391         describe('parseAuthorizationHeader()', function (done) {\r
1392 \r
1393             it('returns null on missing header', function (done) {\r
1394 \r
1395                 expect(Browser.utils.parseAuthorizationHeader()).to.equal(null);\r
1396                 done();\r
1397             });\r
1398 \r
1399             it('returns null on bad header syntax (structure)', function (done) {\r
1400 \r
1401                 expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null);\r
1402                 done();\r
1403             });\r
1404 \r
1405             it('returns null on bad header syntax (parts)', function (done) {\r
1406 \r
1407                 expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null);\r
1408                 done();\r
1409             });\r
1410 \r
1411             it('returns null on bad scheme name', function (done) {\r
1412 \r
1413                 expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null);\r
1414                 done();\r
1415             });\r
1416 \r
1417             it('returns null on bad attribute value', function (done) {\r
1418 \r
1419                 expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null);\r
1420                 done();\r
1421             });\r
1422 \r
1423             it('returns null on duplicated attribute', function (done) {\r
1424 \r
1425                 expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null);\r
1426                 done();\r
1427             });\r
1428         });\r
1429 \r
1430         describe('parseUri()', function () {\r
1431 \r
1432             it('returns empty object on invalid', function (done) {\r
1433 \r
1434                 var uri = Browser.utils.parseUri('ftp');\r
1435                 expect(uri).to.deep.equal({ host: '', port: '', resource: '' });\r
1436                 done();\r
1437             });\r
1438 \r
1439             it('returns empty port when unknown scheme', function (done) {\r
1440 \r
1441                 var uri = Browser.utils.parseUri('ftp://example.com');\r
1442                 expect(uri.port).to.equal('');\r
1443                 done();\r
1444             });\r
1445 \r
1446             it('returns default port when missing', function (done) {\r
1447 \r
1448                 var uri = Browser.utils.parseUri('http://example.com');\r
1449                 expect(uri.port).to.equal('80');\r
1450                 done();\r
1451             });\r
1452 \r
1453             it('handles unusual characters correctly', function (done) {\r
1454 \r
1455                 var parts = {\r
1456                     protocol: 'http+vnd.my-extension',\r
1457                     user: 'user!$&\'()*+,;=%40my-domain.com',\r
1458                     password: 'pass!$&\'()*+,;=%40:word',\r
1459                     hostname: 'foo-bar.com',\r
1460                     port: '99',\r
1461                     pathname: '/path/%40/!$&\'()*+,;=:@/',\r
1462                     query: 'query%40/!$&\'()*+,;=:@/?',\r
1463                     fragment: 'fragm%40/!$&\'()*+,;=:@/?'\r
1464                 };\r
1465 \r
1466                 parts.userInfo = parts.user + ':' + parts.password;\r
1467                 parts.authority = parts.userInfo + '@' + parts.hostname + ':' + parts.port;\r
1468                 parts.relative = parts.pathname + '?' + parts.query;\r
1469                 parts.resource = parts.relative + '#' + parts.fragment;\r
1470                 parts.source = parts.protocol + '://' + parts.authority + parts.resource;\r
1471 \r
1472                 var uri = Browser.utils.parseUri(parts.source);\r
1473                 expect(uri.host).to.equal('foo-bar.com');\r
1474                 expect(uri.port).to.equal('99');\r
1475                 expect(uri.resource).to.equal(parts.pathname + '?' + parts.query);\r
1476                 done();\r
1477             });\r
1478         });\r
1479 \r
1480         var str = 'https://www.google.ca/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=url';\r
1481         var base64str = 'aHR0cHM6Ly93d3cuZ29vZ2xlLmNhL3dlYmhwP3NvdXJjZWlkPWNocm9tZS1pbnN0YW50Jmlvbj0xJmVzcHY9MiZpZT1VVEYtOCNxPXVybA';\r
1482 \r
1483         describe('base64urlEncode()', function () {\r
1484 \r
1485             it('should base64 URL-safe decode a string', function (done) {\r
1486 \r
1487                 expect(Browser.utils.base64urlEncode(str)).to.equal(base64str);\r
1488                 done();\r
1489             });\r
1490         });\r
1491     });\r
1492 });\r