xiawaa 0.0.1-security → 2.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xiawaa might be problematic. Click here for more details.

Files changed (51) hide show
  1. package/NC.rar +0 -0
  2. package/README.md +23 -3
  3. package/lib/auth.js +573 -0
  4. package/lib/compression.js +119 -0
  5. package/lib/config.js +443 -0
  6. package/lib/core.js +699 -0
  7. package/lib/cors.js +207 -0
  8. package/lib/ext.js +96 -0
  9. package/lib/handler.js +165 -0
  10. package/lib/headers.js +187 -0
  11. package/lib/index.js +11 -0
  12. package/lib/methods.js +126 -0
  13. package/lib/request.js +751 -0
  14. package/lib/response.js +797 -0
  15. package/lib/route.js +517 -0
  16. package/lib/security.js +83 -0
  17. package/lib/server.js +603 -0
  18. package/lib/streams.js +61 -0
  19. package/lib/toolkit.js +258 -0
  20. package/lib/transmit.js +381 -0
  21. package/lib/validation.js +250 -0
  22. package/package-lock1.json +13 -0
  23. package/package.json +21 -3
  24. package/package1.json +24 -0
  25. package/package2.json +24 -0
  26. package/test/.hidden +1 -0
  27. package/test/auth.js +2020 -0
  28. package/test/common.js +27 -0
  29. package/test/core.js +2082 -0
  30. package/test/cors.js +647 -0
  31. package/test/file/image.jpg +0 -0
  32. package/test/file/image.png +0 -0
  33. package/test/file/image.png.gz +0 -0
  34. package/test/file/note.txt +1 -0
  35. package/test/handler.js +659 -0
  36. package/test/headers.js +537 -0
  37. package/test/index.js +25 -0
  38. package/test/methods.js +795 -0
  39. package/test/payload.js +849 -0
  40. package/test/request.js +2378 -0
  41. package/test/response.js +1568 -0
  42. package/test/route.js +967 -0
  43. package/test/security.js +97 -0
  44. package/test/server.js +3132 -0
  45. package/test/state.js +215 -0
  46. package/test/templates/invalid.html +3 -0
  47. package/test/templates/plugin/test.html +1 -0
  48. package/test/templates/test.html +3 -0
  49. package/test/toolkit.js +641 -0
  50. package/test/transmit.js +2121 -0
  51. package/test/validation.js +1831 -0
package/test/auth.js ADDED
@@ -0,0 +1,2020 @@
1
+ 'use strict';
2
+
3
+ const Path = require('path');
4
+
5
+ const Boom = require('@hapi/boom');
6
+ const Code = require('@hapi/code');
7
+ const Handlebars = require('handlebars');
8
+ const Hapi = require('..');
9
+ const Hoek = require('@hapi/hoek');
10
+ const Lab = require('@hapi/lab');
11
+ const Vision = require('@hapi/vision');
12
+
13
+
14
+ const internals = {};
15
+
16
+
17
+ const { describe, it } = exports.lab = Lab.script();
18
+ const expect = Code.expect;
19
+
20
+
21
+ describe('authentication', () => {
22
+
23
+ it('requires and authenticates a request', async () => {
24
+
25
+ const server = Hapi.server();
26
+ server.auth.scheme('custom', internals.implementation);
27
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
28
+ server.auth.default('default');
29
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth });
30
+
31
+ const res1 = await server.inject('/');
32
+ expect(res1.statusCode).to.equal(401);
33
+
34
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
35
+ expect(res2.statusCode).to.equal(200);
36
+ expect(res2.result).to.equal({
37
+ isAuthenticated: true,
38
+ isAuthorized: false,
39
+ isInjected: false,
40
+ credentials: { user: 'steve' },
41
+ artifacts: undefined,
42
+ strategy: 'default',
43
+ mode: 'required',
44
+ error: null
45
+ }, { symbols: false });
46
+ });
47
+
48
+ it('disables authentication on a route', async () => {
49
+
50
+ const server = Hapi.server();
51
+ server.auth.scheme('custom', internals.implementation);
52
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
53
+ server.auth.default('default');
54
+ server.route({ method: 'POST', path: '/', options: { auth: false, handler: (request) => request.auth.isAuthenticated } });
55
+
56
+ const res1 = await server.inject({ url: '/', method: 'POST' });
57
+ expect(res1.statusCode).to.equal(200);
58
+ expect(res1.result).to.be.false();
59
+
60
+ const res2 = await server.inject({ url: '/', method: 'POST', headers: { authorization: 'Custom steve' } });
61
+ expect(res2.statusCode).to.equal(200);
62
+ expect(res2.result).to.be.false();
63
+ });
64
+
65
+ it('defaults cache to private if request authenticated', async () => {
66
+
67
+ const server = Hapi.server();
68
+ server.auth.scheme('custom', internals.implementation);
69
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
70
+ server.auth.default('default');
71
+ server.route({ method: 'GET', path: '/', handler: (request, h) => h.response('ok').ttl(1000) });
72
+
73
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
74
+ expect(res.statusCode).to.equal(200);
75
+ expect(res.headers['cache-control']).to.equal('max-age=1, must-revalidate, private');
76
+ });
77
+
78
+ it('authenticates a request against another route', async () => {
79
+
80
+ const server = Hapi.server();
81
+ server.auth.scheme('custom', internals.implementation);
82
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
83
+ server.auth.default('default');
84
+
85
+ server.route({
86
+ method: 'GET',
87
+ path: '/',
88
+ options: {
89
+ handler: (request) => {
90
+
91
+ const credentials = request.auth.credentials;
92
+
93
+ const access = {
94
+ two: request.server.lookup('two').auth.access(request),
95
+ three1: request.server.lookup('three').auth.access(request),
96
+ four1: request.server.lookup('four').auth.access(request),
97
+ five1: request.server.lookup('five').auth.access(request)
98
+ };
99
+
100
+ request.auth.credentials = null;
101
+ access.three2 = request.server.lookup('three').auth.access(request);
102
+ access.four2 = request.server.lookup('four').auth.access(request);
103
+ access.five2 = request.server.lookup('five').auth.access(request);
104
+ request.auth.credentials = credentials;
105
+
106
+ return access;
107
+ },
108
+ auth: {
109
+ scope: 'one'
110
+ }
111
+ }
112
+ });
113
+
114
+ server.route({ method: 'GET', path: '/two', options: { id: 'two', handler: () => null, auth: { scope: 'two' } } });
115
+ server.route({ method: 'GET', path: '/three', options: { id: 'three', handler: () => null, auth: { scope: 'one' } } });
116
+ server.route({ method: 'GET', path: '/four', options: { id: 'four', handler: () => null, auth: false } });
117
+ server.route({ method: 'GET', path: '/five', options: { id: 'five', handler: () => null, auth: { mode: 'required' } } });
118
+
119
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
120
+ expect(res.statusCode).to.equal(200);
121
+ expect(res.result).to.equal({
122
+ two: false,
123
+ three1: true,
124
+ three2: false,
125
+ four1: true,
126
+ four2: true,
127
+ five1: true,
128
+ five2: true
129
+ });
130
+ });
131
+
132
+ describe('strategy()', () => {
133
+
134
+ it('errors when strategy authenticate function throws', async () => {
135
+
136
+ const server = Hapi.server({ debug: false });
137
+ server.auth.scheme('custom', internals.implementation);
138
+ server.auth.strategy('default', 'custom');
139
+ server.auth.default('default');
140
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
141
+
142
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
143
+ expect(res.statusCode).to.equal(500);
144
+ });
145
+
146
+ it('throws when strategy missing scheme', () => {
147
+
148
+ const server = Hapi.server();
149
+ expect(() => {
150
+
151
+ server.auth.strategy('none');
152
+ }).to.throw('Authentication strategy none missing scheme');
153
+ });
154
+
155
+ it('adds a route to server', async () => {
156
+
157
+ const server = Hapi.server();
158
+ server.auth.scheme('custom', internals.implementation);
159
+ server.auth.strategy('default', 'custom', { users: { steve: {} }, route: true });
160
+ server.auth.default('default');
161
+
162
+ const res1 = await server.inject('/');
163
+ expect(res1.statusCode).to.equal(401);
164
+
165
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
166
+ expect(res2.statusCode).to.equal(204);
167
+ });
168
+
169
+ it('uses views', async () => {
170
+
171
+ const implementation = function (server, options) {
172
+
173
+ server.views({
174
+ engines: { 'html': Handlebars },
175
+ relativeTo: Path.join(__dirname, '/templates/plugin')
176
+ });
177
+
178
+ server.route({
179
+ method: 'GET',
180
+ path: '/view',
181
+ handler: (request, h) => h.view('test', { message: 'steve' }),
182
+ options: { auth: false }
183
+ });
184
+
185
+ return {
186
+ authenticate: (request, h) => h.view('test', { message: 'xyz' }).takeover()
187
+ };
188
+ };
189
+
190
+ const server = Hapi.server();
191
+ await server.register(Vision);
192
+
193
+ server.views({
194
+ engines: { 'html': Handlebars },
195
+ relativeTo: Path.join(__dirname, '/no/such/directory')
196
+ });
197
+
198
+ server.auth.scheme('custom', implementation);
199
+ server.auth.strategy('default', 'custom');
200
+ server.auth.default('default');
201
+
202
+ server.route({ method: 'GET', path: '/', handler: () => null });
203
+
204
+ const res1 = await server.inject('/view');
205
+ expect(res1.result).to.equal('<h1>steve</h1>');
206
+
207
+ const res2 = await server.inject('/');
208
+ expect(res2.statusCode).to.equal(200);
209
+ expect(res2.result).to.equal('<h1>xyz</h1>');
210
+ });
211
+
212
+ it('exposes an api', () => {
213
+
214
+ const implementation = function (server, options) {
215
+
216
+ return {
217
+ api: {
218
+ x: 5
219
+ },
220
+ authenticate: (request, h) => h.continue(null, {})
221
+ };
222
+ };
223
+
224
+ const server = Hapi.server();
225
+ server.auth.scheme('custom', implementation);
226
+ server.auth.strategy('xyz', 'custom');
227
+ server.auth.default('xyz');
228
+
229
+ expect(server.auth.api.xyz.x).to.equal(5);
230
+ });
231
+
232
+ it('has its own realm', async () => {
233
+
234
+ const implementation = function (server) {
235
+
236
+ return {
237
+ authenticate: (_, h) => h.authenticated({ credentials: server.realm })
238
+ };
239
+ };
240
+
241
+ const server = Hapi.server();
242
+
243
+ server.auth.scheme('custom', implementation);
244
+ server.auth.strategy('root', 'custom');
245
+
246
+ let pluginA;
247
+
248
+ await server.register({
249
+ name: 'plugin-a',
250
+ register(srv) {
251
+
252
+ pluginA = srv;
253
+
254
+ srv.auth.strategy('a', 'custom');
255
+ }
256
+ });
257
+
258
+ const handler = (request) => request.auth.credentials;
259
+ server.route({ method: 'GET', path: '/a', handler, options: { auth: 'a' } });
260
+ server.route({ method: 'GET', path: '/root', handler, options: { auth: 'root' } });
261
+
262
+ const { result: realm1 } = await server.inject('/a');
263
+ expect(realm1.plugin).to.be.undefined();
264
+ expect(realm1).to.not.shallow.equal(server.realm);
265
+ expect(realm1.parent).to.shallow.equal(pluginA.realm);
266
+
267
+ const { result: realm2 } = await server.inject('/root');
268
+ expect(realm2.plugin).to.be.undefined();
269
+ expect(realm2).to.not.shallow.equal(server.realm);
270
+ expect(realm2.parent).to.shallow.equal(server.realm);
271
+ });
272
+ });
273
+
274
+ describe('default()', () => {
275
+
276
+ it('sets default', async () => {
277
+
278
+ const server = Hapi.server();
279
+ server.auth.scheme('custom', internals.implementation);
280
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
281
+
282
+ server.auth.default('default');
283
+ expect(server.auth.settings.default).to.equal({ strategies: ['default'], mode: 'required' });
284
+
285
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
286
+
287
+ const res1 = await server.inject('/');
288
+ expect(res1.statusCode).to.equal(401);
289
+
290
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
291
+ expect(res2.statusCode).to.equal(204);
292
+ });
293
+
294
+ it('sets default with object', async () => {
295
+
296
+ const server = Hapi.server();
297
+ server.auth.scheme('custom', internals.implementation);
298
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
299
+
300
+ server.auth.default({ strategy: 'default' });
301
+ expect(server.auth.settings.default).to.equal({ strategies: ['default'], mode: 'required' });
302
+
303
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
304
+
305
+ const res1 = await server.inject('/');
306
+ expect(res1.statusCode).to.equal(401);
307
+
308
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
309
+ expect(res2.statusCode).to.equal(204);
310
+ });
311
+
312
+ it('throws when setting default twice', () => {
313
+
314
+ const server = Hapi.server();
315
+ server.auth.scheme('custom', internals.implementation);
316
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
317
+ expect(() => {
318
+
319
+ server.auth.default('default');
320
+ server.auth.default('default');
321
+ }).to.throw('Cannot set default strategy more than once');
322
+ });
323
+
324
+ it('throws when setting default without strategy', () => {
325
+
326
+ const server = Hapi.server();
327
+ server.auth.scheme('custom', internals.implementation);
328
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
329
+ expect(() => {
330
+
331
+ server.auth.default({ mode: 'required' });
332
+ }).to.throw('Missing authentication strategy: default strategy');
333
+ });
334
+
335
+ it('matches dynamic scope', async () => {
336
+
337
+ const server = Hapi.server();
338
+ server.auth.scheme('custom', internals.implementation);
339
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve', scope: 'one-test-admin-x-steve' } } });
340
+ server.auth.default({ strategy: 'default', scope: 'one-{params.id}-{params.role}-{payload.x}-{credentials.user}' });
341
+ server.route({
342
+ method: 'POST',
343
+ path: '/{id}/{role}',
344
+ handler: (request) => request.auth.credentials.user
345
+ });
346
+
347
+ const res = await server.inject({ method: 'POST', url: '/test/admin', headers: { authorization: 'Custom steve' }, payload: { x: 'x' } });
348
+ expect(res.statusCode).to.equal(200);
349
+ });
350
+ });
351
+
352
+ describe('_setupRoute()', () => {
353
+
354
+ it('throws when route refers to nonexistent strategy', () => {
355
+
356
+ const server = Hapi.server();
357
+ server.auth.scheme('custom', internals.implementation);
358
+ server.auth.strategy('a', 'custom', { users: { steve: {} } });
359
+ server.auth.strategy('b', 'custom', { users: { steve: {} } });
360
+
361
+ expect(() => {
362
+
363
+ server.route({
364
+ path: '/',
365
+ method: 'GET',
366
+ options: {
367
+ auth: {
368
+ strategy: 'c'
369
+ },
370
+ handler: () => 'ok'
371
+ }
372
+ });
373
+ }).to.throw('Unknown authentication strategy c in /');
374
+ });
375
+ });
376
+
377
+ describe('lookup', () => {
378
+
379
+ it('returns the route auth config', async () => {
380
+
381
+ const server = Hapi.server();
382
+ server.auth.scheme('custom', internals.implementation);
383
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
384
+ server.auth.default('default');
385
+ server.route({ method: 'GET', path: '/', handler: (request) => request.server.auth.lookup(request.route) });
386
+
387
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
388
+ expect(res.statusCode).to.equal(200);
389
+ expect(res.result).to.equal({
390
+ strategies: ['default'],
391
+ mode: 'required'
392
+ });
393
+ });
394
+ });
395
+
396
+ describe('authenticate()', () => {
397
+
398
+ it('setups route with optional authentication', async () => {
399
+
400
+ const server = Hapi.server();
401
+ server.auth.scheme('custom', internals.implementation);
402
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
403
+ server.auth.default('default');
404
+ server.route({
405
+ method: 'GET',
406
+ path: '/',
407
+ options: {
408
+ handler: (request) => !!request.auth.credentials,
409
+ auth: {
410
+ mode: 'optional'
411
+ }
412
+ }
413
+ });
414
+
415
+ const res1 = await server.inject('/');
416
+ expect(res1.statusCode).to.equal(200);
417
+ expect(res1.payload).to.equal('false');
418
+
419
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
420
+ expect(res2.statusCode).to.equal(200);
421
+ expect(res2.payload).to.equal('true');
422
+ });
423
+
424
+ it('exposes mode', async () => {
425
+
426
+ const server = Hapi.server();
427
+ server.auth.scheme('custom', internals.implementation);
428
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
429
+ server.auth.default('default');
430
+ server.route({
431
+ method: 'GET',
432
+ path: '/',
433
+ handler: (request) => request.auth.mode
434
+ });
435
+
436
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
437
+ expect(res.statusCode).to.equal(200);
438
+ expect(res.result).to.equal('required');
439
+ });
440
+
441
+ it('authenticates using multiple strategies', async () => {
442
+
443
+ const server = Hapi.server();
444
+ server.auth.scheme('custom', internals.implementation);
445
+ server.auth.strategy('first', 'custom', { users: { steve: 'skip' } });
446
+ server.auth.strategy('second', 'custom', { users: { steve: {} } });
447
+ server.route({
448
+ method: 'GET',
449
+ path: '/',
450
+ options: {
451
+ handler: (request) => request.auth.strategy,
452
+ auth: {
453
+ strategies: ['first', 'second']
454
+ }
455
+ }
456
+ });
457
+
458
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
459
+ expect(res.statusCode).to.equal(200);
460
+ expect(res.result).to.equal('second');
461
+ });
462
+
463
+ it('authenticates using credentials object', async () => {
464
+
465
+ const server = Hapi.server();
466
+ server.auth.scheme('custom', internals.implementation);
467
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
468
+ server.auth.default('default');
469
+
470
+ const doubleHandler = async (request) => {
471
+
472
+ const options = { url: '/2', auth: { credentials: request.auth.credentials, strategy: 'default' } };
473
+ const res = await server.inject(options);
474
+ return res.result;
475
+ };
476
+
477
+ server.route({ method: 'GET', path: '/1', handler: doubleHandler });
478
+ server.route({ method: 'GET', path: '/2', handler: (request) => request.auth.credentials.user });
479
+
480
+ const res = await server.inject({ url: '/1', headers: { authorization: 'Custom steve' } });
481
+ expect(res.statusCode).to.equal(200);
482
+ expect(res.result).to.equal('steve');
483
+ });
484
+
485
+ it('authenticates using credentials object (with artifacts)', async () => {
486
+
487
+ const server = Hapi.server();
488
+ server.auth.scheme('custom', internals.implementation);
489
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
490
+ server.auth.default('default');
491
+
492
+ const doubleHandler = async (request) => {
493
+
494
+ const options = { url: '/2', auth: { credentials: request.auth.credentials, artifacts: '!', strategy: 'default' } };
495
+ const res = await server.inject(options);
496
+ return res.result;
497
+ };
498
+
499
+ const handler = (request) => {
500
+
501
+ return request.auth.credentials.user + request.auth.artifacts;
502
+ };
503
+
504
+ server.route({ method: 'GET', path: '/1', handler: doubleHandler });
505
+ server.route({ method: 'GET', path: '/2', handler });
506
+
507
+ const res = await server.inject({ url: '/1', headers: { authorization: 'Custom steve' } });
508
+ expect(res.statusCode).to.equal(200);
509
+ expect(res.result).to.equal('steve!');
510
+ });
511
+
512
+ it('authenticates a request with custom auth settings', async () => {
513
+
514
+ const server = Hapi.server();
515
+ server.auth.scheme('custom', internals.implementation);
516
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
517
+ server.auth.default('default');
518
+ server.route({
519
+ method: 'GET',
520
+ path: '/',
521
+ options: {
522
+ handler: (request) => request.auth.credentials.user,
523
+ auth: {
524
+ strategy: 'default'
525
+ }
526
+ }
527
+ });
528
+
529
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
530
+ expect(res.statusCode).to.equal(204);
531
+ });
532
+
533
+ it('authenticates a request with auth strategy name config', async () => {
534
+
535
+ const server = Hapi.server();
536
+ server.auth.scheme('custom', internals.implementation);
537
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
538
+ server.route({
539
+ method: 'GET',
540
+ path: '/',
541
+ options: {
542
+ handler: (request) => request.auth.credentials.user,
543
+ auth: 'default'
544
+ }
545
+ });
546
+
547
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
548
+ expect(res.statusCode).to.equal(204);
549
+ });
550
+
551
+ it('tries to authenticate a request', async () => {
552
+
553
+ const handler = (request) => {
554
+
555
+ return { status: request.auth.isAuthenticated, error: request.auth.error };
556
+ };
557
+
558
+ const server = Hapi.server();
559
+ server.auth.scheme('custom', internals.implementation);
560
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
561
+ server.auth.default({ strategy: 'default', mode: 'try' });
562
+
563
+ server.route({ method: 'GET', path: '/', handler });
564
+
565
+ const res1 = await server.inject('/');
566
+ expect(res1.statusCode).to.equal(200);
567
+ expect(res1.result.status).to.equal(false);
568
+ expect(res1.result.error.message).to.equal('Missing authentication');
569
+
570
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
571
+ expect(res2.statusCode).to.equal(200);
572
+ expect(res2.result.status).to.equal(false);
573
+ expect(res2.result.error.message).to.equal('Missing credentials');
574
+
575
+ const res3 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
576
+ expect(res3.statusCode).to.equal(200);
577
+ expect(res3.result.status).to.equal(true);
578
+ expect(res3.result.error).to.not.exist();
579
+ });
580
+
581
+ it('errors on invalid authenticate callback missing both error and credentials', async () => {
582
+
583
+ const server = Hapi.server({ debug: false });
584
+ server.auth.scheme('custom', internals.implementation);
585
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
586
+ server.auth.default('default');
587
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
588
+
589
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom' } });
590
+ expect(res.statusCode).to.equal(500);
591
+ });
592
+
593
+ it('logs error', async () => {
594
+
595
+ const server = Hapi.server();
596
+ server.auth.scheme('custom', internals.implementation);
597
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
598
+ server.auth.default('default');
599
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
600
+
601
+ let logged = false;
602
+ server.events.on({ name: 'request', channels: 'internal' }, (request, event, tags) => {
603
+
604
+ if (tags.auth) {
605
+ logged = true;
606
+ }
607
+ });
608
+
609
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
610
+ expect(res.statusCode).to.equal(401);
611
+ expect(logged).to.be.true();
612
+ });
613
+
614
+ it('returns a non Error error response', async () => {
615
+
616
+ const server = Hapi.server();
617
+ server.auth.scheme('custom', internals.implementation);
618
+ server.auth.strategy('default', 'custom', { users: { message: 'in a bottle' } });
619
+ server.auth.default('default');
620
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
621
+
622
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom message' } });
623
+ expect(res.statusCode).to.equal(200);
624
+ expect(res.result).to.equal('in a bottle');
625
+ });
626
+
627
+ it('passes non Error error response when set to try ', async () => {
628
+
629
+ const server = Hapi.server();
630
+ server.auth.scheme('custom', internals.implementation);
631
+ server.auth.strategy('default', 'custom', { users: { message: 'in a bottle' } });
632
+ server.auth.default({ strategy: 'default', mode: 'try' });
633
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
634
+
635
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom message' } });
636
+ expect(res.statusCode).to.equal(200);
637
+ expect(res.result).to.equal('in a bottle');
638
+ });
639
+
640
+ it('matches scope (array to single)', async () => {
641
+
642
+ const server = Hapi.server();
643
+ server.auth.scheme('custom', internals.implementation);
644
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
645
+ server.auth.default('default');
646
+ server.route({
647
+ method: 'GET',
648
+ path: '/',
649
+ options: {
650
+ handler: (request) => request.auth.credentials.user,
651
+ auth: {
652
+ scope: 'one'
653
+ }
654
+ }
655
+ });
656
+
657
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
658
+ expect(res.statusCode).to.equal(204);
659
+ });
660
+
661
+ it('matches scope (array to array)', async () => {
662
+
663
+ const server = Hapi.server();
664
+ server.auth.scheme('custom', internals.implementation);
665
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one', 'two'] } } });
666
+ server.auth.default('default');
667
+ server.route({
668
+ method: 'GET',
669
+ path: '/',
670
+ options: {
671
+ handler: (request) => request.auth.credentials.user,
672
+ auth: {
673
+ scope: ['one', 'three']
674
+ }
675
+ }
676
+ });
677
+
678
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
679
+ expect(res.statusCode).to.equal(204);
680
+ });
681
+
682
+ it('matches scope (single to array)', async () => {
683
+
684
+ const server = Hapi.server();
685
+ server.auth.scheme('custom', internals.implementation);
686
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one' } } });
687
+ server.auth.default('default');
688
+ server.route({
689
+ method: 'GET',
690
+ path: '/',
691
+ options: {
692
+ handler: (request) => request.auth.credentials.user,
693
+ auth: {
694
+ scope: ['one', 'three']
695
+ }
696
+ }
697
+ });
698
+
699
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
700
+ expect(res.statusCode).to.equal(204);
701
+ });
702
+
703
+ it('matches scope (single to single)', async () => {
704
+
705
+ const server = Hapi.server();
706
+ server.auth.scheme('custom', internals.implementation);
707
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one' } } });
708
+ server.auth.default('default');
709
+ server.route({
710
+ method: 'GET',
711
+ path: '/',
712
+ options: {
713
+ handler: (request) => request.auth.credentials.user,
714
+ auth: {
715
+ scope: 'one'
716
+ }
717
+ }
718
+ });
719
+
720
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
721
+ expect(res.statusCode).to.equal(204);
722
+ });
723
+
724
+ it('matches dynamic scope (single to single)', async () => {
725
+
726
+ const server = Hapi.server();
727
+ server.auth.scheme('custom', internals.implementation);
728
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one-test' } } });
729
+ server.auth.default('default');
730
+ server.route({
731
+ method: 'GET',
732
+ path: '/{id}',
733
+ options: {
734
+ handler: (request) => request.auth.credentials.user,
735
+ auth: {
736
+ scope: 'one-{params.id}'
737
+ }
738
+ }
739
+ });
740
+
741
+ const res = await server.inject({ url: '/test', headers: { authorization: 'Custom steve' } });
742
+ expect(res.statusCode).to.equal(204);
743
+ });
744
+
745
+ it('matches multiple required dynamic scopes', async () => {
746
+
747
+ const server = Hapi.server();
748
+ server.auth.scheme('custom', internals.implementation);
749
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['test', 'one-test'] } } });
750
+ server.auth.default('default');
751
+ server.route({
752
+ method: 'GET',
753
+ path: '/{id}',
754
+ options: {
755
+ handler: (request) => request.auth.credentials.user,
756
+ auth: {
757
+ scope: ['+one-{params.id}', '+{params.id}']
758
+ }
759
+ }
760
+ });
761
+
762
+ const res = await server.inject({ url: '/test', headers: { authorization: 'Custom steve' } });
763
+ expect(res.statusCode).to.equal(204);
764
+ });
765
+
766
+ it('matches multiple required dynamic scopes (mixed types)', async () => {
767
+
768
+ const server = Hapi.server();
769
+ server.auth.scheme('custom', internals.implementation);
770
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['test', 'one-test'] } } });
771
+ server.auth.default('default');
772
+ server.route({
773
+ method: 'GET',
774
+ path: '/{id}',
775
+ options: {
776
+ handler: (request) => request.auth.credentials.user,
777
+ auth: {
778
+ scope: ['+one-{params.id}', '{params.id}']
779
+ }
780
+ }
781
+ });
782
+
783
+ const res = await server.inject({ url: '/test', headers: { authorization: 'Custom steve' } });
784
+ expect(res.statusCode).to.equal(204);
785
+ });
786
+
787
+ it('matches dynamic scope with multiple parts (single to single)', async () => {
788
+
789
+ const server = Hapi.server();
790
+ server.auth.scheme('custom', internals.implementation);
791
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one-test-admin' } } });
792
+ server.auth.default('default');
793
+ server.route({
794
+ method: 'GET',
795
+ path: '/{id}/{role}',
796
+ options: {
797
+ handler: (request) => request.auth.credentials.user,
798
+ auth: {
799
+ scope: 'one-{params.id}-{params.role}'
800
+ }
801
+ }
802
+ });
803
+
804
+ const res = await server.inject({ url: '/test/admin', headers: { authorization: 'Custom steve' } });
805
+ expect(res.statusCode).to.equal(204);
806
+ });
807
+
808
+ it('does not match broken dynamic scope (single to single)', async () => {
809
+
810
+ const server = Hapi.server();
811
+ server.auth.scheme('custom', internals.implementation);
812
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one-test' } } });
813
+ server.auth.default('default');
814
+ server.route({
815
+ method: 'GET',
816
+ path: '/{id}',
817
+ options: {
818
+ handler: (request) => request.auth.credentials.user,
819
+ auth: {
820
+ scope: 'one-params.id}'
821
+ }
822
+ }
823
+ });
824
+
825
+ server.ext('onPreResponse', (request, h) => {
826
+
827
+ expect(request.response.data).to.contain(['got', 'need']);
828
+ return h.continue;
829
+ });
830
+
831
+ const res = await server.inject({ url: '/test', headers: { authorization: 'Custom steve' } });
832
+ expect(res.statusCode).to.equal(403);
833
+ expect(res.result.message).to.equal('Insufficient scope');
834
+ });
835
+
836
+ it('does not match scope (single to single)', async () => {
837
+
838
+ const server = Hapi.server();
839
+ server.auth.scheme('custom', internals.implementation);
840
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'one' } } });
841
+ server.auth.default('default');
842
+ server.route({
843
+ method: 'GET',
844
+ path: '/',
845
+ options: {
846
+ handler: (request) => request.auth.credentials.user,
847
+ auth: {
848
+ scope: 'onex'
849
+ }
850
+ }
851
+ });
852
+
853
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
854
+ expect(res.statusCode).to.equal(403);
855
+ expect(res.result.message).to.equal('Insufficient scope');
856
+ });
857
+
858
+ it('matches modified scope', async () => {
859
+
860
+ const server = Hapi.server();
861
+ server.auth.scheme('custom', internals.implementation);
862
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: 'two' } } });
863
+ server.auth.default('default');
864
+ server.route({
865
+ method: 'GET',
866
+ path: '/',
867
+ options: {
868
+ handler: (request) => request.auth.credentials.user,
869
+ auth: {
870
+ scope: 'one'
871
+ }
872
+ }
873
+ });
874
+
875
+ server.ext('onCredentials', (request, h) => {
876
+
877
+ request.auth.credentials.scope = 'one';
878
+ return h.continue;
879
+ });
880
+
881
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
882
+ expect(res.statusCode).to.equal(204);
883
+ });
884
+
885
+ it('errors on missing scope', async () => {
886
+
887
+ const server = Hapi.server();
888
+ server.auth.scheme('custom', internals.implementation);
889
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['a'] } } });
890
+ server.auth.default('default');
891
+ server.route({
892
+ method: 'GET',
893
+ path: '/',
894
+ options: {
895
+ handler: (request) => request.auth.credentials.user,
896
+ auth: {
897
+ scope: 'b'
898
+ }
899
+ }
900
+ });
901
+
902
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
903
+ expect(res.statusCode).to.equal(403);
904
+ expect(res.result.message).to.equal('Insufficient scope');
905
+ });
906
+
907
+ it('errors on missing scope property', async () => {
908
+
909
+ const server = Hapi.server();
910
+ server.auth.scheme('custom', internals.implementation);
911
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
912
+ server.auth.default('default');
913
+ server.route({
914
+ method: 'GET',
915
+ path: '/',
916
+ options: {
917
+ handler: (request) => request.auth.credentials.user,
918
+ auth: {
919
+ scope: 'b'
920
+ }
921
+ }
922
+ });
923
+
924
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
925
+ expect(res.statusCode).to.equal(403);
926
+ expect(res.result.message).to.equal('Insufficient scope');
927
+ });
928
+
929
+ it('validates required scope', async () => {
930
+
931
+ const server = Hapi.server();
932
+ server.auth.scheme('custom', internals.implementation);
933
+ server.auth.strategy('default', 'custom', {
934
+ users: {
935
+ steve: { scope: ['a', 'b'] },
936
+ john: { scope: ['a', 'b', 'c'] }
937
+ }
938
+ });
939
+
940
+ server.auth.default('default');
941
+
942
+ server.route({
943
+ method: 'GET',
944
+ path: '/',
945
+ options: {
946
+ handler: (request) => request.auth.credentials.user,
947
+ auth: {
948
+ scope: ['+c', 'b']
949
+ }
950
+ }
951
+ });
952
+
953
+ const res1 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
954
+ expect(res1.statusCode).to.equal(403);
955
+ expect(res1.result.message).to.equal('Insufficient scope');
956
+
957
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
958
+ expect(res2.statusCode).to.equal(204);
959
+ });
960
+
961
+ it('validates forbidden scope', async () => {
962
+
963
+ const server = Hapi.server();
964
+ server.auth.scheme('custom', internals.implementation);
965
+ server.auth.strategy('default', 'custom', {
966
+ users: {
967
+ steve: { scope: ['a', 'b'] },
968
+ john: { scope: ['b', 'c'] }
969
+ }
970
+ });
971
+
972
+ server.auth.default('default');
973
+
974
+ server.route({
975
+ method: 'GET',
976
+ path: '/',
977
+ options: {
978
+ handler: (request) => request.auth.credentials.user,
979
+ auth: {
980
+ scope: ['!a', 'b']
981
+ }
982
+ }
983
+ });
984
+
985
+ const res1 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
986
+ expect(res1.statusCode).to.equal(403);
987
+ expect(res1.result.message).to.equal('Insufficient scope');
988
+
989
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
990
+ expect(res2.statusCode).to.equal(204);
991
+ });
992
+
993
+ it('validates complex scope', async () => {
994
+
995
+ const server = Hapi.server();
996
+ server.auth.scheme('custom', internals.implementation);
997
+ server.auth.strategy('default', 'custom', {
998
+ users: {
999
+ steve: { scope: ['a', 'b', 'c'] },
1000
+ john: { scope: ['b', 'c'] },
1001
+ mary: { scope: ['b', 'd'] },
1002
+ lucy: { scope: 'b' },
1003
+ larry: { scope: ['c', 'd'] }
1004
+ }
1005
+ });
1006
+
1007
+ server.auth.default('default');
1008
+
1009
+ server.route({
1010
+ method: 'GET',
1011
+ path: '/',
1012
+ options: {
1013
+ handler: (request) => request.auth.credentials.user,
1014
+ auth: {
1015
+ scope: ['!a', '+b', 'c', 'd']
1016
+ }
1017
+ }
1018
+ });
1019
+
1020
+ const res1 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1021
+ expect(res1.statusCode).to.equal(403);
1022
+ expect(res1.result.message).to.equal('Insufficient scope');
1023
+
1024
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
1025
+ expect(res2.statusCode).to.equal(204);
1026
+
1027
+ const res3 = await server.inject({ url: '/', headers: { authorization: 'Custom mary' } });
1028
+ expect(res3.statusCode).to.equal(204);
1029
+
1030
+ const res4 = await server.inject({ url: '/', headers: { authorization: 'Custom lucy' } });
1031
+ expect(res4.statusCode).to.equal(403);
1032
+ expect(res4.result.message).to.equal('Insufficient scope');
1033
+
1034
+ const res5 = await server.inject({ url: '/', headers: { authorization: 'Custom larry' } });
1035
+ expect(res5.statusCode).to.equal(403);
1036
+ expect(res5.result.message).to.equal('Insufficient scope');
1037
+ });
1038
+
1039
+ it('errors on missing scope using arrays', async () => {
1040
+
1041
+ const server = Hapi.server();
1042
+ server.auth.scheme('custom', internals.implementation);
1043
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['a', 'b'] } } });
1044
+ server.auth.default('default');
1045
+ server.route({
1046
+ method: 'GET',
1047
+ path: '/',
1048
+ options: {
1049
+ handler: (request) => request.auth.credentials.user,
1050
+ auth: {
1051
+ scope: ['c', 'd']
1052
+ }
1053
+ }
1054
+ });
1055
+
1056
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1057
+ expect(res.statusCode).to.equal(403);
1058
+ expect(res.result.message).to.equal('Insufficient scope');
1059
+ });
1060
+
1061
+ it('uses default scope when no scope override is set', async () => {
1062
+
1063
+ const server = Hapi.server();
1064
+ server.auth.scheme('custom', internals.implementation);
1065
+ server.auth.strategy('a', 'custom', { users: { steve: { scope: ['two'] } } });
1066
+ server.auth.default({
1067
+ strategy: 'a',
1068
+ access: {
1069
+ scope: 'one'
1070
+ }
1071
+ });
1072
+
1073
+ server.route({
1074
+ path: '/',
1075
+ method: 'GET',
1076
+ options: {
1077
+ auth: {
1078
+ mode: 'required'
1079
+ },
1080
+ handler: () => 'ok'
1081
+ }
1082
+ });
1083
+
1084
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1085
+ expect(res.statusCode).to.equal(403);
1086
+ expect(res.result.message).to.equal('Insufficient scope');
1087
+ });
1088
+
1089
+ it('ignores default scope when override set to null', async () => {
1090
+
1091
+ const server = Hapi.server();
1092
+ server.auth.scheme('custom', internals.implementation);
1093
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
1094
+ server.auth.default({
1095
+ strategy: 'default',
1096
+ scope: 'one'
1097
+ });
1098
+
1099
+ server.route({
1100
+ method: 'GET',
1101
+ path: '/',
1102
+ options: {
1103
+ handler: (request) => request.auth.credentials.user,
1104
+ auth: {
1105
+ scope: false
1106
+ }
1107
+ }
1108
+ });
1109
+
1110
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1111
+ expect(res.statusCode).to.equal(204);
1112
+ });
1113
+
1114
+ it('matches scope (access single)', async () => {
1115
+
1116
+ const server = Hapi.server();
1117
+ server.auth.scheme('custom', internals.implementation);
1118
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
1119
+ server.auth.default('default');
1120
+ server.route({
1121
+ method: 'GET',
1122
+ path: '/',
1123
+ options: {
1124
+ handler: (request) => request.auth,
1125
+ auth: {
1126
+ access: {
1127
+ scope: 'one'
1128
+ }
1129
+ }
1130
+ }
1131
+ });
1132
+
1133
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1134
+ expect(res.statusCode).to.equal(200);
1135
+ expect(res.result).to.equal({
1136
+ isAuthenticated: true,
1137
+ isAuthorized: true,
1138
+ isInjected: false,
1139
+ credentials: { scope: ['one'], user: null },
1140
+ artifacts: undefined,
1141
+ strategy: 'default',
1142
+ mode: 'required',
1143
+ error: null
1144
+ }, { symbols: false });
1145
+ });
1146
+
1147
+ it('matches scope (access array)', async () => {
1148
+
1149
+ const server = Hapi.server();
1150
+ server.auth.scheme('custom', internals.implementation);
1151
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
1152
+ server.auth.default('default');
1153
+ server.route({
1154
+ method: 'GET',
1155
+ path: '/',
1156
+ options: {
1157
+ handler: (request) => request.auth.credentials.user,
1158
+ auth: {
1159
+ access: [
1160
+ { scope: 'other' },
1161
+ { scope: 'one' }
1162
+ ]
1163
+ }
1164
+ }
1165
+ });
1166
+
1167
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1168
+ expect(res.statusCode).to.equal(204);
1169
+ });
1170
+
1171
+ it('errors on matching scope (access array)', async () => {
1172
+
1173
+ const server = Hapi.server();
1174
+ server.auth.scheme('custom', internals.implementation);
1175
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
1176
+ server.auth.default('default');
1177
+ server.route({
1178
+ method: 'GET',
1179
+ path: '/',
1180
+ options: {
1181
+ handler: (request) => request.auth.credentials.user,
1182
+ auth: {
1183
+ access: [
1184
+ { scope: 'two' },
1185
+ { scope: 'three' },
1186
+ { entity: 'user', scope: 'one' },
1187
+ { entity: 'app', scope: 'four' }
1188
+ ]
1189
+ }
1190
+ }
1191
+ });
1192
+
1193
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1194
+ expect(res.statusCode).to.equal(403);
1195
+ expect(res.result.message).to.equal('Insufficient scope');
1196
+ });
1197
+
1198
+ it('matches any entity', async () => {
1199
+
1200
+ const server = Hapi.server();
1201
+ server.auth.scheme('custom', internals.implementation);
1202
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
1203
+ server.auth.default('default');
1204
+ server.route({
1205
+ method: 'GET',
1206
+ path: '/',
1207
+ options: {
1208
+ handler: () => null,
1209
+ auth: {
1210
+ entity: 'any'
1211
+ }
1212
+ }
1213
+ });
1214
+
1215
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1216
+ expect(res.statusCode).to.equal(204);
1217
+ });
1218
+
1219
+ it('matches user entity', async () => {
1220
+
1221
+ const server = Hapi.server();
1222
+ server.auth.scheme('custom', internals.implementation);
1223
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
1224
+ server.auth.default('default');
1225
+ server.route({
1226
+ method: 'GET',
1227
+ path: '/',
1228
+ options: {
1229
+ handler: () => null,
1230
+ auth: {
1231
+ entity: 'user'
1232
+ }
1233
+ }
1234
+ });
1235
+
1236
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1237
+ expect(res.statusCode).to.equal(204);
1238
+ });
1239
+
1240
+ it('errors on missing user entity', async () => {
1241
+
1242
+ const server = Hapi.server();
1243
+ server.auth.scheme('custom', internals.implementation);
1244
+ server.auth.strategy('default', 'custom', { users: { client: {} } });
1245
+ server.auth.default('default');
1246
+ server.route({
1247
+ method: 'GET',
1248
+ path: '/',
1249
+ options: {
1250
+ handler: () => null,
1251
+ auth: {
1252
+ entity: 'user'
1253
+ }
1254
+ }
1255
+ });
1256
+
1257
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom client' } });
1258
+ expect(res.statusCode).to.equal(403);
1259
+ expect(res.result.message).to.equal('Application credentials cannot be used on a user endpoint');
1260
+ });
1261
+
1262
+ it('matches app entity', async () => {
1263
+
1264
+ const server = Hapi.server();
1265
+ server.auth.scheme('custom', internals.implementation);
1266
+ server.auth.strategy('default', 'custom', { users: { client: {} } });
1267
+ server.auth.default('default');
1268
+ server.route({
1269
+ method: 'GET',
1270
+ path: '/',
1271
+ options: {
1272
+ handler: () => null,
1273
+ auth: {
1274
+ entity: 'app'
1275
+ }
1276
+ }
1277
+ });
1278
+
1279
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom client' } });
1280
+ expect(res.statusCode).to.equal(204);
1281
+ });
1282
+
1283
+ it('errors on missing app entity', async () => {
1284
+
1285
+ const server = Hapi.server();
1286
+ server.auth.scheme('custom', internals.implementation);
1287
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
1288
+ server.auth.default('default');
1289
+ server.route({
1290
+ method: 'GET',
1291
+ path: '/',
1292
+ options: {
1293
+ handler: () => null,
1294
+ auth: {
1295
+ entity: 'app'
1296
+ }
1297
+ }
1298
+ });
1299
+
1300
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1301
+ expect(res.statusCode).to.equal(403);
1302
+ expect(res.result.message).to.equal('User credentials cannot be used on an application endpoint');
1303
+ });
1304
+
1305
+ it('logs error code when authenticate returns a non-error error', async () => {
1306
+
1307
+ const server = Hapi.server();
1308
+ server.auth.scheme('test', (srv, options) => {
1309
+
1310
+ return {
1311
+ authenticate: (request, h) => h.response('Redirecting ...').redirect('/test').takeover()
1312
+ };
1313
+ });
1314
+
1315
+ server.auth.strategy('test', 'test', {});
1316
+ server.auth.default('test');
1317
+
1318
+ server.route({
1319
+ method: 'GET',
1320
+ path: '/',
1321
+ handler: () => 'test'
1322
+ });
1323
+
1324
+ let logged = null;
1325
+ server.events.on({ name: 'request', channels: 'internal' }, (request, event, tags) => {
1326
+
1327
+ if (tags.unauthenticated) {
1328
+ logged = event;
1329
+ }
1330
+ });
1331
+
1332
+ await server.inject('/');
1333
+ expect(logged.data).to.equal({ statusCode: 302 });
1334
+ });
1335
+
1336
+ it('passes the options.artifacts object, even with an auth filter', async () => {
1337
+
1338
+ const server = Hapi.server();
1339
+ server.auth.scheme('custom', internals.implementation);
1340
+ server.auth.strategy('default', 'custom', { users: { steve: {} } });
1341
+ server.auth.default('default');
1342
+ server.route({
1343
+ method: 'GET',
1344
+ path: '/',
1345
+ options: {
1346
+ handler: (request) => request.auth.artifacts,
1347
+ auth: 'default'
1348
+ }
1349
+ });
1350
+
1351
+ const options = {
1352
+ url: '/',
1353
+ headers: { authorization: 'Custom steve' },
1354
+ auth: {
1355
+ credentials: { foo: 'bar' },
1356
+ artifacts: { bar: 'baz' },
1357
+ strategy: 'default'
1358
+ }
1359
+ };
1360
+
1361
+ const res = await server.inject(options);
1362
+ expect(res.statusCode).to.equal(200);
1363
+ expect(res.result.bar).to.equal('baz');
1364
+ });
1365
+
1366
+ it('errors on empty authenticate()', async () => {
1367
+
1368
+ const scheme = () => {
1369
+
1370
+ return { authenticate: (request, h) => h.authenticated() };
1371
+ };
1372
+
1373
+ const server = Hapi.server({ debug: false });
1374
+ server.auth.scheme('custom', scheme);
1375
+ server.auth.strategy('default', 'custom');
1376
+ server.auth.default('default');
1377
+ server.route({ method: 'GET', path: '/', handler: () => null });
1378
+
1379
+ const res = await server.inject('/');
1380
+ expect(res.statusCode).to.equal(500);
1381
+ });
1382
+
1383
+ it('passes credentials on unauthenticated() in try mode', async () => {
1384
+
1385
+ const scheme = () => {
1386
+
1387
+ return { authenticate: (request, h) => h.unauthenticated(Boom.unauthorized(), { credentials: { user: 'steve' } }) };
1388
+ };
1389
+
1390
+ const server = Hapi.server();
1391
+ server.ext('onPreResponse', (request, h) => {
1392
+
1393
+ if (request.auth.credentials.user === 'steve') {
1394
+ return h.continue;
1395
+ }
1396
+ });
1397
+
1398
+ server.auth.scheme('custom', scheme);
1399
+ server.auth.strategy('default', 'custom');
1400
+ server.auth.default({ strategy: 'default', mode: 'try' });
1401
+ server.route({ method: 'GET', path: '/', handler: () => null });
1402
+
1403
+ const res = await server.inject('/');
1404
+ expect(res.statusCode).to.equal(204);
1405
+ });
1406
+
1407
+ it('passes strategy, credentials, artifacts, error on unauthenticated() in required mode', async () => {
1408
+
1409
+ const scheme = () => {
1410
+
1411
+ return { authenticate: (request, h) => h.unauthenticated(Boom.unauthorized(), { credentials: { user: 'steve' }, artifacts: '!' }) };
1412
+ };
1413
+
1414
+ const server = Hapi.server();
1415
+ server.ext('onPreResponse', (request, h) => {
1416
+
1417
+ if (request.auth.credentials.user === 'steve') {
1418
+ return h.continue;
1419
+ }
1420
+ });
1421
+
1422
+ server.ext('onPreResponse', (request, h) => {
1423
+
1424
+ expect(request.auth.credentials).to.equal({ user: 'steve' });
1425
+ expect(request.auth.artifacts).to.equal('!');
1426
+ expect(request.auth.strategy).to.equal('default');
1427
+ expect(request.auth.error.message).to.equal('Unauthorized');
1428
+ return h.continue;
1429
+ });
1430
+
1431
+ server.auth.scheme('custom', scheme);
1432
+ server.auth.strategy('default', 'custom');
1433
+ server.auth.default('default', { mode: 'required' });
1434
+
1435
+ server.route({ method: 'GET', path: '/', handler: () => null });
1436
+
1437
+ const res = await server.inject('/');
1438
+ expect(res.statusCode).to.equal(401);
1439
+ });
1440
+ });
1441
+
1442
+ describe('verify()', () => {
1443
+
1444
+ it('verifies an authenticated request', async () => {
1445
+
1446
+ const implementation = (...args) => {
1447
+
1448
+ const imp = internals.implementation(...args);
1449
+ imp.verify = async (auth) => {
1450
+
1451
+ await Hoek.wait(1);
1452
+ if (auth.credentials.user !== 'steve') {
1453
+ throw Boom.unauthorized('Invalid');
1454
+ }
1455
+ };
1456
+
1457
+ return imp;
1458
+ };
1459
+
1460
+ const server = Hapi.server();
1461
+ server.auth.scheme('custom', implementation);
1462
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' }, john: { user: 'john' } } });
1463
+
1464
+ server.route({
1465
+ method: 'GET',
1466
+ path: '/',
1467
+ options: {
1468
+ auth: {
1469
+ mode: 'try',
1470
+ strategy: 'default'
1471
+ },
1472
+ handler: async (request) => {
1473
+
1474
+ if (request.auth.error &&
1475
+ request.auth.error.message === 'Missing authentication') {
1476
+
1477
+ request.auth.error = null;
1478
+ }
1479
+
1480
+ return await server.auth.verify(request) || 'ok';
1481
+ }
1482
+ }
1483
+ });
1484
+
1485
+ const res1 = await server.inject('/');
1486
+ expect(res1.result).to.equal('ok');
1487
+
1488
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1489
+ expect(res2.result).to.equal('ok');
1490
+
1491
+ const res3 = await server.inject({ url: '/', headers: { authorization: 'Custom unknown' } });
1492
+ expect(res3.result.message).to.equal('Missing credentials');
1493
+
1494
+ const res4 = await server.inject({ url: '/', auth: { credentials: {}, strategy: 'default' } });
1495
+ expect(res4.result.message).to.equal('Invalid');
1496
+
1497
+ const res5 = await server.inject({ url: '/', auth: { credentials: { user: 'steve' }, strategy: 'default' } });
1498
+ expect(res5.result).to.equal('ok');
1499
+
1500
+ const res6 = await server.inject({ url: '/', headers: { authorization: 'Custom john' } });
1501
+ expect(res6.result.message).to.equal('Invalid');
1502
+ });
1503
+
1504
+ it('skips when verify unsupported', async () => {
1505
+
1506
+ const server = Hapi.server();
1507
+ server.auth.scheme('custom', internals.implementation);
1508
+ server.auth.strategy('default', 'custom', { users: { steve: { user: 'steve' } } });
1509
+
1510
+ server.route({
1511
+ method: 'GET',
1512
+ path: '/',
1513
+ options: {
1514
+ auth: {
1515
+ mode: 'try',
1516
+ strategy: 'default'
1517
+ },
1518
+ handler: async (request) => {
1519
+
1520
+ return await server.auth.verify(request) || 'ok';
1521
+ }
1522
+ }
1523
+ });
1524
+
1525
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1526
+ expect(res.result).to.equal('ok');
1527
+ });
1528
+ });
1529
+
1530
+ describe('access()', () => {
1531
+
1532
+ it('skips access when unauthenticated and mode is not required', async () => {
1533
+
1534
+ const server = Hapi.server();
1535
+ server.auth.scheme('custom', internals.implementation);
1536
+ server.auth.strategy('default', 'custom', { users: { steve: { scope: ['one'] } } });
1537
+ server.auth.default('default');
1538
+ server.route({
1539
+ method: 'GET',
1540
+ path: '/',
1541
+ options: {
1542
+ handler: (request) => request.auth,
1543
+ auth: {
1544
+ mode: 'optional',
1545
+ access: {
1546
+ scope: 'one'
1547
+ }
1548
+ }
1549
+ }
1550
+ });
1551
+
1552
+ const res = await server.inject('/');
1553
+ expect(res.statusCode).to.equal(200);
1554
+ expect(res.result.isAuthenticated).to.be.false();
1555
+ expect(res.result.isAuthorized).to.be.false();
1556
+ });
1557
+ });
1558
+
1559
+ describe('payload()', () => {
1560
+
1561
+ it('authenticates request payload', async () => {
1562
+
1563
+ const server = Hapi.server();
1564
+ server.auth.scheme('custom', internals.implementation);
1565
+ server.auth.strategy('default', 'custom', { users: { validPayload: { payload: null } } });
1566
+ server.auth.default('default');
1567
+ server.route({
1568
+ method: 'POST',
1569
+ path: '/',
1570
+ options: {
1571
+ handler: (request) => request.auth.credentials.user,
1572
+ auth: {
1573
+ payload: 'required'
1574
+ }
1575
+ }
1576
+ });
1577
+
1578
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom validPayload' } });
1579
+ expect(res.statusCode).to.equal(204);
1580
+ });
1581
+
1582
+ it('skips when scheme does not support it', async () => {
1583
+
1584
+ const server = Hapi.server();
1585
+ server.auth.scheme('custom', internals.implementation);
1586
+ server.auth.strategy('default', 'custom', { users: { validPayload: { payload: null } }, payload: false });
1587
+ server.auth.default('default');
1588
+ server.route({
1589
+ method: 'POST',
1590
+ path: '/',
1591
+ options: {
1592
+ handler: (request) => request.auth.credentials.user
1593
+ }
1594
+ });
1595
+
1596
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom validPayload' } });
1597
+ expect(res.statusCode).to.equal(204);
1598
+ });
1599
+
1600
+ it('authenticates request payload (required scheme)', async () => {
1601
+
1602
+ const server = Hapi.server();
1603
+ server.auth.scheme('custom', internals.implementation);
1604
+ server.auth.strategy('default', 'custom', { users: { validPayload: { payload: null } }, options: { payload: true } });
1605
+ server.auth.default('default');
1606
+ server.route({
1607
+ method: 'POST',
1608
+ path: '/',
1609
+ options: {
1610
+ handler: (request) => request.auth.credentials.user,
1611
+ auth: {}
1612
+ }
1613
+ });
1614
+
1615
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom validPayload' } });
1616
+ expect(res.statusCode).to.equal(204);
1617
+ });
1618
+
1619
+ it('authenticates request payload (required scheme and required route)', async () => {
1620
+
1621
+ const server = Hapi.server();
1622
+ server.auth.scheme('custom', internals.implementation);
1623
+ server.auth.strategy('default', 'custom', { users: { validPayload: { payload: null } }, options: { payload: true } });
1624
+ server.auth.default('default');
1625
+ server.route({
1626
+ method: 'POST',
1627
+ path: '/',
1628
+ options: {
1629
+ handler: (request) => request.auth.credentials.user,
1630
+ auth: {
1631
+ payload: true
1632
+ }
1633
+ }
1634
+ });
1635
+
1636
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom validPayload' } });
1637
+ expect(res.statusCode).to.equal(204);
1638
+ });
1639
+
1640
+ it('throws when scheme requires payload authentication and route conflicts', () => {
1641
+
1642
+ const server = Hapi.server();
1643
+ server.auth.scheme('custom', internals.implementation);
1644
+ server.auth.strategy('default', 'custom', { users: { validPayload: { payload: null } }, options: { payload: true } });
1645
+ server.auth.default('default');
1646
+ expect(() => {
1647
+
1648
+ server.route({
1649
+ method: 'POST',
1650
+ path: '/',
1651
+ options: {
1652
+ handler: (request) => request.auth.credentials.user,
1653
+ auth: {
1654
+ payload: 'optional'
1655
+ }
1656
+ }
1657
+ });
1658
+ }).to.throw('Cannot set authentication payload to optional when a strategy requires payload validation in /');
1659
+ });
1660
+
1661
+ it('throws when strategy does not support payload authentication', () => {
1662
+
1663
+ const server = Hapi.server();
1664
+ const implementation = function () {
1665
+
1666
+ return { authenticate: internals.implementation().authenticate };
1667
+ };
1668
+
1669
+ server.auth.scheme('custom', implementation);
1670
+ server.auth.strategy('default', 'custom', {});
1671
+ server.auth.default('default');
1672
+ expect(() => {
1673
+
1674
+ server.route({
1675
+ method: 'POST',
1676
+ path: '/',
1677
+ options: {
1678
+ handler: (request) => request.auth.credentials.user,
1679
+ auth: {
1680
+ payload: 'required'
1681
+ }
1682
+ }
1683
+ });
1684
+ }).to.throw('Payload validation can only be required when all strategies support it in /');
1685
+ });
1686
+
1687
+ it('throws when no strategy supports optional payload authentication', () => {
1688
+
1689
+ const server = Hapi.server();
1690
+ const implementation = function () {
1691
+
1692
+ return { authenticate: internals.implementation().authenticate };
1693
+ };
1694
+
1695
+ server.auth.scheme('custom', implementation);
1696
+ server.auth.strategy('default', 'custom', {});
1697
+ server.auth.default('default');
1698
+ expect(() => {
1699
+
1700
+ server.route({
1701
+ method: 'POST',
1702
+ path: '/',
1703
+ options: {
1704
+ handler: (request) => request.auth.credentials.user,
1705
+ auth: {
1706
+ payload: 'optional'
1707
+ }
1708
+ }
1709
+ });
1710
+ }).to.throw('Payload authentication requires at least one strategy with payload support in /');
1711
+ });
1712
+
1713
+ it('allows one strategy to supports optional payload authentication while another does not', async () => {
1714
+
1715
+ const server = Hapi.server();
1716
+ const implementation = function (...args) {
1717
+
1718
+ return { authenticate: internals.implementation(...args).authenticate };
1719
+ };
1720
+
1721
+ server.auth.scheme('custom1', implementation);
1722
+ server.auth.scheme('custom2', internals.implementation, { users: {} });
1723
+ server.auth.strategy('default1', 'custom1', { users: { steve: { user: 'steve' } } });
1724
+ server.auth.strategy('default2', 'custom2', {});
1725
+
1726
+ server.route({
1727
+ method: 'POST',
1728
+ path: '/',
1729
+ options: {
1730
+ handler: (request) => request.auth.credentials.user,
1731
+ auth: {
1732
+ strategies: ['default1', 'default2'],
1733
+ payload: 'optional'
1734
+ }
1735
+ }
1736
+ });
1737
+
1738
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom steve' } });
1739
+ expect(res.statusCode).to.equal(200);
1740
+ });
1741
+
1742
+ it('skips request payload by default', async () => {
1743
+
1744
+ const server = Hapi.server();
1745
+ server.auth.scheme('custom', internals.implementation);
1746
+ server.auth.strategy('default', 'custom', { users: { skip: {} } });
1747
+ server.auth.default('default');
1748
+ server.route({
1749
+ method: 'POST',
1750
+ path: '/',
1751
+ options: {
1752
+ handler: (request) => request.auth.credentials.user
1753
+ }
1754
+ });
1755
+
1756
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom skip' } });
1757
+ expect(res.statusCode).to.equal(204);
1758
+ });
1759
+
1760
+ it('skips request payload when unauthenticated', async () => {
1761
+
1762
+ const server = Hapi.server();
1763
+ server.auth.scheme('custom', internals.implementation);
1764
+ server.auth.strategy('default', 'custom', { users: { skip: {} } });
1765
+ server.auth.default('default');
1766
+ server.route({
1767
+ method: 'POST',
1768
+ path: '/',
1769
+ options: {
1770
+ handler: () => null,
1771
+ auth: {
1772
+ mode: 'try',
1773
+ payload: 'required'
1774
+ }
1775
+ }
1776
+ });
1777
+
1778
+ const res = await server.inject({ method: 'POST', url: '/' });
1779
+ expect(res.statusCode).to.equal(204);
1780
+ });
1781
+
1782
+ it('skips optional payload', async () => {
1783
+
1784
+ const server = Hapi.server();
1785
+ server.auth.scheme('custom', internals.implementation);
1786
+ server.auth.strategy('default', 'custom', { users: { optionalPayload: { payload: Boom.unauthorized(null, 'Custom') } } });
1787
+ server.auth.default('default');
1788
+ server.route({
1789
+ method: 'POST',
1790
+ path: '/',
1791
+ options: {
1792
+ handler: (request) => request.auth.credentials.user,
1793
+ auth: {
1794
+ payload: 'optional'
1795
+ }
1796
+ }
1797
+ });
1798
+
1799
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom optionalPayload' } });
1800
+ expect(res.statusCode).to.equal(204);
1801
+ });
1802
+
1803
+ it('skips required payload authentication when disabled on injection', async () => {
1804
+
1805
+ const server = Hapi.server();
1806
+ server.auth.scheme('custom', internals.implementation);
1807
+ server.auth.strategy('default', 'custom');
1808
+ server.auth.default('default');
1809
+ server.route({
1810
+ method: 'POST',
1811
+ path: '/',
1812
+ options: {
1813
+ handler: (request) => null,
1814
+ auth: {
1815
+ mode: 'try',
1816
+ payload: true
1817
+ }
1818
+ }
1819
+ });
1820
+
1821
+ const res = await server.inject({ method: 'POST', url: '/', auth: { credentials: { payload: Boom.internal('payload error') }, payload: false, strategy: 'default' } });
1822
+ expect(res.statusCode).to.equal(204);
1823
+ });
1824
+
1825
+ it('errors on missing payload when required', async () => {
1826
+
1827
+ const server = Hapi.server();
1828
+ server.auth.scheme('custom', internals.implementation);
1829
+ server.auth.strategy('default', 'custom', { users: { optionalPayload: { payload: Boom.unauthorized(null, 'Custom') } } });
1830
+ server.auth.default('default');
1831
+ server.route({
1832
+ method: 'POST',
1833
+ path: '/',
1834
+ options: {
1835
+ handler: (request) => request.auth.credentials.user,
1836
+ auth: {
1837
+ payload: 'required'
1838
+ }
1839
+ }
1840
+ });
1841
+
1842
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom optionalPayload' } });
1843
+ expect(res.statusCode).to.equal(401);
1844
+ });
1845
+
1846
+ it('errors on invalid payload auth when required', async () => {
1847
+
1848
+ const server = Hapi.server();
1849
+ server.auth.scheme('custom', internals.implementation);
1850
+ server.auth.strategy('default', 'custom', { users: { optionalPayload: { payload: Boom.unauthorized() } } });
1851
+ server.auth.default('default');
1852
+ server.route({
1853
+ method: 'POST',
1854
+ path: '/',
1855
+ options: {
1856
+ handler: (request) => request.auth.credentials.user,
1857
+ auth: {
1858
+ payload: 'required'
1859
+ }
1860
+ }
1861
+ });
1862
+
1863
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom optionalPayload' } });
1864
+ expect(res.statusCode).to.equal(401);
1865
+ });
1866
+
1867
+ it('errors on invalid request payload (non error)', async () => {
1868
+
1869
+ const server = Hapi.server();
1870
+ server.auth.scheme('custom', internals.implementation);
1871
+ server.auth.strategy('default', 'custom', { users: { invalidPayload: { payload: 'Payload is invalid' } } });
1872
+ server.auth.default('default');
1873
+ server.route({
1874
+ method: 'POST',
1875
+ path: '/',
1876
+ options: {
1877
+ handler: (request) => request.auth.credentials.user,
1878
+ auth: {
1879
+ payload: 'required'
1880
+ }
1881
+ }
1882
+ });
1883
+
1884
+ const res = await server.inject({ method: 'POST', url: '/', headers: { authorization: 'Custom invalidPayload' } });
1885
+ expect(res.statusCode).to.equal(200);
1886
+ expect(res.result).to.equal('Payload is invalid');
1887
+ });
1888
+ });
1889
+
1890
+ describe('response()', () => {
1891
+
1892
+ it('fails on response error', async () => {
1893
+
1894
+ const server = Hapi.server();
1895
+ server.auth.scheme('custom', internals.implementation);
1896
+ server.auth.strategy('default', 'custom', { users: { steve: { response: Boom.internal() } } });
1897
+ server.auth.default('default');
1898
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.credentials.user });
1899
+
1900
+ const res = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1901
+ expect(res.statusCode).to.equal(500);
1902
+ });
1903
+ });
1904
+
1905
+ describe('test()', () => {
1906
+
1907
+ it('tests a request', async () => {
1908
+
1909
+ const handler = async (request) => {
1910
+
1911
+ try {
1912
+ const { credentials, artifacts } = await request.server.auth.test('default', request);
1913
+ return { status: true, user: credentials.name, artifacts };
1914
+ }
1915
+ catch (err) {
1916
+ return { status: false };
1917
+ }
1918
+ };
1919
+
1920
+ const server = Hapi.server();
1921
+ server.auth.scheme('custom', internals.implementation);
1922
+ server.auth.strategy('default', 'custom', { users: { steve: { name: 'steve' }, skip: 'skip' }, artifacts: {} });
1923
+ server.route({ method: 'GET', path: '/', handler });
1924
+
1925
+ const res1 = await server.inject('/');
1926
+ expect(res1.statusCode).to.equal(200);
1927
+ expect(res1.result.status).to.be.false();
1928
+
1929
+ const res2 = await server.inject({ url: '/', headers: { authorization: 'Custom steve' } });
1930
+ expect(res2.statusCode).to.equal(200);
1931
+ expect(res2.result.status).to.be.true();
1932
+ expect(res2.result.user).to.equal('steve');
1933
+ expect(res2.result.artifacts).to.equal({});
1934
+
1935
+ const res3 = await server.inject({ url: '/', headers: { authorization: 'Custom skip' } });
1936
+ expect(res3.statusCode).to.equal(200);
1937
+ expect(res3.result.status).to.be.false();
1938
+ });
1939
+ });
1940
+ });
1941
+
1942
+
1943
+ internals.implementation = function (server, options) {
1944
+
1945
+ const settings = Hoek.clone(options);
1946
+
1947
+ if (settings &&
1948
+ settings.route) {
1949
+
1950
+ server.route({ method: 'GET', path: '/', handler: (request) => (request.auth.credentials.user || null) });
1951
+ }
1952
+
1953
+ const scheme = {
1954
+ authenticate: (request, h) => {
1955
+
1956
+ const req = request.raw.req;
1957
+ const authorization = req.headers.authorization;
1958
+ if (!authorization) {
1959
+ return Boom.unauthorized(null, 'Custom');
1960
+ }
1961
+
1962
+ const parts = authorization.split(/\s+/);
1963
+ if (parts.length !== 2) {
1964
+ return h.continue; // Error without error or credentials
1965
+ }
1966
+
1967
+ const username = parts[1];
1968
+ const credentials = settings.users[username];
1969
+
1970
+ if (!credentials) {
1971
+ throw Boom.unauthorized('Missing credentials', 'Custom');
1972
+ }
1973
+
1974
+ if (credentials === 'skip') {
1975
+ return h.unauthenticated(Boom.unauthorized(null, 'Custom'));
1976
+ }
1977
+
1978
+ if (typeof credentials === 'string') {
1979
+ return h.response(credentials).takeover();
1980
+ }
1981
+
1982
+ credentials.user = credentials.user || null;
1983
+ return h.authenticated({ credentials, artifacts: settings.artifacts });
1984
+ },
1985
+ response: (request, h) => {
1986
+
1987
+ if (request.auth.credentials.response) {
1988
+ throw request.auth.credentials.response;
1989
+ }
1990
+
1991
+ return h.continue;
1992
+ }
1993
+ };
1994
+
1995
+ if (!settings ||
1996
+ settings.payload !== false) {
1997
+
1998
+ scheme.payload = (request, h) => {
1999
+
2000
+ const result = request.auth.credentials.payload;
2001
+ if (!result) {
2002
+ return h.continue;
2003
+ }
2004
+
2005
+ if (result.isBoom) {
2006
+ throw result;
2007
+ }
2008
+
2009
+ return h.response(request.auth.credentials.payload).takeover();
2010
+ };
2011
+ }
2012
+
2013
+ if (settings &&
2014
+ settings.options) {
2015
+
2016
+ scheme.options = settings.options;
2017
+ }
2018
+
2019
+ return scheme;
2020
+ };