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