@@ -29,17 +29,18 @@ import {GrowlerService} from "../../messaging/growler.service";
29
29
import { ExtensionService , SHAREDZ_EXTENSION } from "../../extendable/extensions-noop.service" ;
30
30
31
31
import semver from 'semver' ;
32
- import { cloneDeep , defer , delay , forOwn , keys , invert , isEmpty , isNil , unset , trim } from 'lodash' ;
32
+ import { cloneDeep , defer , delay , forOwn , get , keys , invert , invoke , isEmpty , isNil , unset , trim } from 'lodash' ;
33
33
import { GrowlerModel } from "../../messaging/growler.model" ;
34
34
import { SETTINGS_SERVICE , SettingsService } from "../../../services/settings.service" ;
35
35
import { ZITI_DATA_SERVICE , ZitiDataService } from "../../../services/ziti-data.service" ;
36
36
import { ActivatedRoute , Router } from "@angular/router" ;
37
37
import { JwtSigner } from "../../../models/jwt-signer" ;
38
38
import { Location } from "@angular/common" ;
39
39
import { ValidationService } from "../../../services/validation.service" ;
40
- import { OAuthService } from "angular-oauth2-oidc" ;
40
+ import { OAuthErrorEvent , OAuthService } from "angular-oauth2-oidc" ;
41
41
import { LoginServiceClass , ZAC_LOGIN_SERVICE } from "../../../services/login-service.class" ;
42
42
import { AuthService } from "../../../services/auth.service" ;
43
+ import { HttpClient } from "@angular/common/http" ;
43
44
44
45
@Component ( {
45
46
selector : 'lib-configuration' ,
@@ -65,8 +66,14 @@ export class JwtSignerFormComponent extends ProjectableForm implements OnInit, O
65
66
configKey = 'oauth_test_callback_config' ;
66
67
tokenTypeKey = 'oauth_test_callback_token_type' ;
67
68
oidcVerified = false ;
68
- oidcErrorMessage ;
69
+ oidcErrorMessageSource ;
70
+ oidcErrorMessageDetail ;
71
+ oidcErrorMessageDetail2 ;
72
+ jwksValidationError ;
73
+ oidcAuthTokenClaims ;
74
+ oidcClaims ;
69
75
overrideFormData ;
76
+ override usePreviousLocation = false
70
77
override entityType = 'external-jwt-signers' ;
71
78
override entityClass = JwtSigner ;
72
79
@@ -86,6 +93,7 @@ export class JwtSignerFormComponent extends ProjectableForm implements OnInit, O
86
93
private validationService : ValidationService ,
87
94
private authService : AuthService ,
88
95
private oauthService : OAuthService ,
96
+ private http : HttpClient ,
89
97
@Inject ( ZAC_LOGIN_SERVICE ) private loginService : LoginServiceClass ,
90
98
91
99
) {
@@ -456,7 +464,7 @@ export class JwtSignerFormComponent extends ProjectableForm implements OnInit, O
456
464
}
457
465
458
466
get callbackURL ( ) {
459
- return window . location . origin + this . baseHref + `callback`
467
+ return window . location . origin + ( this . baseHref + this . loginService . callbackRoute ) . replace ( '//' , '/' ) ;
460
468
}
461
469
462
470
copyCallbackURL ( ) {
@@ -466,35 +474,125 @@ export class JwtSignerFormComponent extends ProjectableForm implements OnInit, O
466
474
) ;
467
475
}
468
476
469
- testOIDCAuthentication ( ) {
477
+ async testOIDCAuthentication ( ) {
470
478
this . oauthLoading = true ;
471
- const callbackParams = `redirectRoute=jwt-signers/${ this . formData . id } /test-auth` ;
479
+ this . oidcAuthTokenClaims = undefined ;
480
+ this . oidcErrorMessageSource = undefined ;
481
+ this . oidcErrorMessageDetail = undefined ;
482
+ this . oidcErrorMessageDetail2 = undefined ;
483
+ this . jwksValidationError = undefined ;
484
+ const jwksValid = await this . testJWKS ( ) ;
485
+ if ( ! jwksValid ) {
486
+ this . oidcVerified = false ;
487
+ this . oauthLoading = false
488
+ return ;
489
+ }
490
+ const callbackParams = `${ this . loginService . callbackRoute } ?redirectRoute=jwt-signers/${ this . formData . id } /test-auth` ;
472
491
localStorage . setItem ( 'oidc_callback_test_config' , JSON . stringify ( this . formData ) ) ;
473
492
this . authService . configureOAuth ( this . formData , callbackParams ) . then ( ( result ) => {
474
- if ( result ) {
493
+ if ( result . success ) {
475
494
delay ( ( ) => {
476
495
this . oauthLoading = false ;
477
496
} , 4000 ) ;
478
497
} else {
479
498
delay ( ( ) => {
499
+ this . oidcErrorMessageDetail = result . message ;
500
+ this . oidcErrorMessageSource = 'OAuth Configuration Error' ;
501
+ if ( this . jwksValidationError ) {
502
+ this . oidcErrorMessageDetail2 = this . jwksValidationError ;
503
+ }
480
504
this . oauthLoading = false ;
505
+ this . oidcVerified = false ;
481
506
} , 700 ) ;
507
+
482
508
}
483
509
} ) ;
484
510
}
485
511
486
512
handleOAuthCallback ( ) {
487
513
this . showMore = true ;
488
- const errorMessage = this . route . snapshot . queryParamMap . get ( 'oidcAuthErrorMessage' ) ;
514
+ const errorMessageDetail = this . route . snapshot . queryParamMap . get ( 'oidcAuthErrorMessageDetail' ) ;
515
+ const errorMessageSource = this . route . snapshot . queryParamMap . get ( 'oidcAuthErrorMessageSource' ) ;
516
+ const oidcAuthTokenClaims = this . route . snapshot . queryParamMap . get ( 'oidcAuthTokenClaims' ) ;
489
517
const result = this . route . snapshot . queryParamMap . get ( 'oidcAuthResult' ) ;
518
+ const formData = localStorage . getItem ( 'oidc_callback_test_config' ) ;
519
+ if ( ! isEmpty ( formData ) ) {
520
+ this . overrideFormData = JSON . parse ( formData ) ;
521
+ }
490
522
if ( result === 'success' ) {
491
523
this . oidcVerified = true ;
524
+ if ( oidcAuthTokenClaims ) {
525
+ this . oidcAuthTokenClaims = JSON . parse ( oidcAuthTokenClaims ) ;
526
+ }
492
527
} else {
493
- this . oidcErrorMessage = errorMessage ;
528
+ this . oidcErrorMessageDetail = errorMessageDetail ;
529
+ this . oidcErrorMessageSource = errorMessageSource ;
530
+ if ( oidcAuthTokenClaims ) {
531
+ this . oidcAuthTokenClaims = JSON . parse ( oidcAuthTokenClaims ) ;
532
+ this . validateTokenClaims ( this . oidcAuthTokenClaims , this . overrideFormData ) ;
533
+ }
494
534
}
495
- const formData = localStorage . getItem ( 'oidc_callback_test_config' ) ;
496
- if ( ! isEmpty ( formData ) ) {
497
- this . overrideFormData = JSON . parse ( formData ) ;
535
+ }
536
+
537
+ validateTokenClaims ( token , formData ) {
538
+ if ( ! token . aud ) {
539
+ this . errors . audience = true ;
540
+ this . oidcErrorMessageDetail2 = 'Audience is missing from token claims'
541
+ } else {
542
+ let audError = true ;
543
+ token . aud . forEach ( ( audItem ) => {
544
+ if ( audItem === formData . audience ) {
545
+ audError = false ;
546
+ }
547
+ } ) ;
548
+ if ( audError ) {
549
+ this . errors . audience = true ;
550
+ this . oidcErrorMessageDetail2 = 'Audience in token does not match External JWT Signer configuration.'
551
+ }
552
+ }
553
+ }
554
+
555
+ async testJWKS ( ) {
556
+ this . errors . jwksEndpoint = false ;
557
+ try {
558
+ const response : any = await this . http . get ( this . formData . jwksEndpoint ) . toPromise ( ) . catch ( ( error ) => {
559
+ this . oidcErrorMessageSource = 'JWKS Endpoint Error' ;
560
+ this . oidcErrorMessageDetail = `HTTP error returned. Status: ${ error . status } . Message: ${ error . message } ` ;
561
+ this . errors . jwksEndpoint = true ;
562
+ return false ;
563
+ } ) ;
564
+ if ( response ) {
565
+ // Basic structural validation
566
+ if ( ! response ?. keys || ! Array . isArray ( response ?. keys ) ) {
567
+ this . oidcErrorMessageSource = 'JWKS Endpoint Error' ;
568
+ this . oidcErrorMessageDetail = `Invalid JWKS: "keys" property missing or not an array` ;
569
+ this . errors . jwksEndpoint = true ;
570
+ } else {
571
+ const logError : any = get ( this . oauthService , 'logger.error' ) ;
572
+ this . oauthService [ 'logger' ] . error = ( ...args ) => {
573
+ this . jwksValidationError = '' ;
574
+ args . forEach ( ( arg ) => {
575
+ this . jwksValidationError += arg + ' ' ;
576
+ } ) ;
577
+ logError . apply ( this , args ) ;
578
+ }
579
+ const result = invoke ( this . oauthService , 'validateDiscoveryDocument' , response ) ;
580
+ }
581
+ }
582
+ } catch ( error : any ) {
583
+ this . oidcErrorMessageSource = 'JWKS Endpoint Error' ;
584
+ this . oidcErrorMessageDetail = `${ error . message } ` ;
585
+ this . errors . jwksEndpoint = true ;
586
+ }
587
+ const growlerData = new GrowlerModel (
588
+ 'error' ,
589
+ 'Invalid' ,
590
+ this . oidcErrorMessageSource ,
591
+ this . oidcErrorMessageDetail ,
592
+ ) ;
593
+ if ( this . errors . jwksEndpoint ) {
594
+ this . growlerService . show ( growlerData ) ;
498
595
}
596
+ return ! this . errors . jwksEndpoint ;
499
597
}
500
598
}
0 commit comments