@@ -383,22 +383,68 @@ module.exports = (db, server, userHandler) => {
383
383
384
384
// Get webauthn challenge
385
385
server . post (
386
- '/users/:user/2fa/webauthn/authentication-challenge' ,
386
+ {
387
+ path : '/users/:user/2fa/webauthn/authentication-challenge' ,
388
+ tags : [ 'TwoFactorAuth' ] ,
389
+ summary : 'Begin WebAuthN authentication challenge' ,
390
+ description : 'This method retrieves the WebAuthN PublicKeyCredentialRequestOptions object to use it for authentication' ,
391
+ validationObjs : {
392
+ requestBody : {
393
+ origin : Joi . string ( ) . empty ( '' ) . uri ( ) . required ( ) . description ( 'Origin domain' ) ,
394
+ authenticatorAttachment : Joi . string ( )
395
+ . valid ( 'platform' , 'cross-platform' )
396
+ . example ( 'cross-platform' )
397
+ . default ( 'cross-platform' )
398
+ . description (
399
+ 'Indicates whether authenticators should be part of the OS ("platform"), or can be roaming authenticators ("cross-platform")'
400
+ ) ,
401
+
402
+ rpId : Joi . string ( ) . hostname ( ) . empty ( '' ) . description ( 'Relaying party ID. Domain' ) ,
403
+
404
+ sess : sessSchema ,
405
+ ip : sessIPSchema
406
+ } ,
407
+ queryParams : { } ,
408
+ pathParams : { user : userId } ,
409
+ response : {
410
+ 200 : {
411
+ description : 'Success' ,
412
+ model : Joi . object ( {
413
+ success : successRes ,
414
+ authenticationOptions : Joi . object ( {
415
+ challenge : Joi . string ( ) . hex ( ) . required ( ) . description ( 'Challenge as hex string' ) ,
416
+ allowCredentials : Joi . array ( )
417
+ . items (
418
+ Joi . object ( {
419
+ rawId : Joi . string ( ) . hex ( ) . required ( ) . description ( 'RawId of the credential as hex string' ) ,
420
+ type : Joi . string ( ) . required ( ) . description ( 'Credential type' )
421
+ } )
422
+ )
423
+ . required ( )
424
+ . description ( 'Allowed credential(s) based on the request' ) ,
425
+ rpId : Joi . string ( ) . description ( 'Relaying Party ID. Domain' ) ,
426
+ rawChallenge : Joi . string ( ) . description ( 'Raw challenge bytes. ArrayBuffer' ) ,
427
+ attestation : Joi . string ( ) . description ( 'Attestation string. `direct`/`indirect`/`none`' ) ,
428
+ extensions : Joi . object ( { } ) . description ( 'Any credential extensions' ) ,
429
+ userVerification : Joi . string ( ) . description ( 'User verification type. `required`/`preferred`/`discouraged`' ) ,
430
+ timeout : Joi . number ( ) . description ( 'Timeout in milliseconds (ms)' )
431
+ } )
432
+ . required ( )
433
+ . description ( 'PublicKeyCredentialRequestOptions object' )
434
+ } )
435
+ }
436
+ }
437
+ }
438
+ } ,
387
439
tools . responseWrapper ( async ( req , res ) => {
388
440
res . charSet ( 'utf-8' ) ;
389
- const schema = Joi . object ( ) . keys ( {
390
- user : Joi . string ( ) . hex ( ) . lowercase ( ) . length ( 24 ) . required ( ) ,
391
- origin : Joi . string ( ) . empty ( '' ) . uri ( ) . required ( ) ,
392
- authenticatorAttachment : Joi . string ( )
393
- . valid ( 'platform' , 'cross-platform' )
394
- . example ( 'cross-platform' )
395
- . default ( 'cross-platform' )
396
- . description ( 'Indicates whether authenticators should be part of the OS ("platform"), or can be roaming authenticators ("cross-platform")' ) ,
397
-
398
- rpId : Joi . string ( ) . hostname ( ) . empty ( '' ) ,
399
-
400
- sess : sessSchema ,
401
- ip : sessIPSchema
441
+
442
+ const { pathParams, requestBody, queryParams } = req . route . spec . validationObjs ;
443
+
444
+ const schema = Joi . object ( {
445
+ ...pathParams ,
446
+ ...requestBody ,
447
+ ...queryParams
402
448
} ) ;
403
449
404
450
const result = schema . validate ( req . params , {
@@ -433,37 +479,70 @@ module.exports = (db, server, userHandler) => {
433
479
) ;
434
480
435
481
server . post (
436
- '/users/:user/2fa/webauthn/authentication-assertion' ,
482
+ {
483
+ path : '/users/:user/2fa/webauthn/authentication-assertion' ,
484
+ tags : [ 'TwoFactorAuth' ] ,
485
+ summary : 'WebAuthN authentication Assertion' ,
486
+ description : 'Assert WebAuthN authentication request and actually authenticate the user' ,
487
+ validationObjs : {
488
+ requestBody : {
489
+ challenge : Joi . string ( ) . empty ( '' ) . hex ( ) . max ( 2048 ) . required ( ) . description ( 'Challenge of the credential as hex string' ) ,
490
+ rawId : Joi . string ( ) . empty ( '' ) . hex ( ) . max ( 2048 ) . required ( ) . description ( 'RawId of the credential' ) ,
491
+ clientDataJSON : Joi . string ( )
492
+ . empty ( '' )
493
+ . hex ( )
494
+ . max ( 1024 * 1024 )
495
+ . required ( )
496
+ . description ( 'Client data JSON as hex string' ) ,
497
+ authenticatorData : Joi . string ( )
498
+ . empty ( '' )
499
+ . hex ( )
500
+ . max ( 1024 * 1024 )
501
+ . required ( )
502
+ . description ( 'Authentication data as hex string' ) ,
503
+
504
+ signature : Joi . string ( )
505
+ . empty ( '' )
506
+ . hex ( )
507
+ . max ( 1024 * 1024 )
508
+ . required ( )
509
+ . description ( 'Private key encrypted signature to verify with public key on the server. Hex string' ) ,
510
+
511
+ rpId : Joi . string ( ) . hostname ( ) . empty ( '' ) . description ( 'Relaying party ID. Domain' ) ,
512
+
513
+ token : booleanSchema . default ( false ) . description ( 'If true response will contain the user auth token' ) ,
514
+
515
+ sess : sessSchema ,
516
+ ip : sessIPSchema
517
+ } ,
518
+ queryParams : { } ,
519
+ pathParams : { user : userId } ,
520
+ response : {
521
+ 200 : {
522
+ description : 'Success' ,
523
+ model : Joi . object ( {
524
+ success : successRes ,
525
+ response : Joi . object ( {
526
+ authenticated : booleanSchema . required ( ) . description ( 'Authentication status' ) ,
527
+ credential : Joi . string ( ) . required ( ) . description ( 'WebAuthN credential ID' )
528
+ } )
529
+ . required ( )
530
+ . description ( 'Auth data' ) ,
531
+ token : Joi . string ( ) . description ( 'User auth token' )
532
+ } )
533
+ }
534
+ }
535
+ }
536
+ } ,
437
537
tools . responseWrapper ( async ( req , res ) => {
438
538
res . charSet ( 'utf-8' ) ;
439
- const schema = Joi . object ( ) . keys ( {
440
- user : Joi . string ( ) . hex ( ) . lowercase ( ) . length ( 24 ) . required ( ) ,
441
-
442
- challenge : Joi . string ( ) . empty ( '' ) . hex ( ) . max ( 2048 ) . required ( ) ,
443
- rawId : Joi . string ( ) . empty ( '' ) . hex ( ) . max ( 2048 ) . required ( ) ,
444
- clientDataJSON : Joi . string ( )
445
- . empty ( '' )
446
- . hex ( )
447
- . max ( 1024 * 1024 )
448
- . required ( ) ,
449
- authenticatorData : Joi . string ( )
450
- . empty ( '' )
451
- . hex ( )
452
- . max ( 1024 * 1024 )
453
- . required ( ) ,
454
-
455
- signature : Joi . string ( )
456
- . empty ( '' )
457
- . hex ( )
458
- . max ( 1024 * 1024 )
459
- . required ( ) ,
460
-
461
- rpId : Joi . string ( ) . hostname ( ) . empty ( '' ) ,
462
-
463
- token : booleanSchema . default ( false ) ,
464
-
465
- sess : sessSchema ,
466
- ip : sessIPSchema
539
+
540
+ const { pathParams, requestBody, queryParams } = req . route . spec . validationObjs ;
541
+
542
+ const schema = Joi . object ( {
543
+ ...pathParams ,
544
+ ...requestBody ,
545
+ ...queryParams
467
546
} ) ;
468
547
469
548
const result = schema . validate ( req . params , {
0 commit comments