@openziti/ziti-console-lib 1.4.0 → 1.5.0

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.
@@ -24739,7 +24739,7 @@ class ServiceEdgeRouterPolicyFormService {
24739
24739
  });
24740
24740
  }
24741
24741
  getServiceNamedAttributes() {
24742
- return this.zitiService.get('services', {}, []).then((result) => {
24742
+ return this.zitiService.get('services', { rawFilter: true, filter: '', sort: 'name', order: 'asc', total: -1, page: 1 }, []).then((result) => {
24743
24743
  const namedAttributes = result.data.map((service) => {
24744
24744
  this.serviceNamedAttributesMap[service.name] = service.id;
24745
24745
  return service.name;
@@ -24749,7 +24749,7 @@ class ServiceEdgeRouterPolicyFormService {
24749
24749
  });
24750
24750
  }
24751
24751
  getEdgeRouterNamedAttributes() {
24752
- return this.zitiService.get('edge-routers', {}, []).then((result) => {
24752
+ return this.zitiService.get('edge-routers', { rawFilter: true, filter: '', sort: 'name', order: 'asc', total: -1, page: 1 }, []).then((result) => {
24753
24753
  const namedAttributes = result.data.map((router) => {
24754
24754
  this.edgeRouterNamedAttributesMap[router.name] = router.id;
24755
24755
  return router.name;
@@ -26261,11 +26261,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
26261
26261
  }] }, { type: GrowlerService }, { type: ValidationService }] });
26262
26262
 
26263
26263
  class AuthService {
26264
- constructor(oauthService, http, growlerService, router) {
26264
+ constructor(oauthService, http, growlerService, router, settingsService) {
26265
26265
  this.oauthService = oauthService;
26266
26266
  this.http = http;
26267
26267
  this.growlerService = growlerService;
26268
26268
  this.router = router;
26269
+ this.settingsService = settingsService;
26269
26270
  this.basePath = '/';
26270
26271
  router.events.subscribe((event) => {
26271
26272
  if (event instanceof NavigationEnd) {
@@ -26285,6 +26286,7 @@ class AuthService {
26285
26286
  window.history.pushState({}, '', urlWithoutHash);
26286
26287
  localStorage.removeItem(configKey);
26287
26288
  localStorage.removeItem(tokenTypeKey);
26289
+ localStorage.removeItem('oauth_callback_controller');
26288
26290
  this.oauthService.logOut();
26289
26291
  this.oauthService.configure({});
26290
26292
  }
@@ -26311,6 +26313,9 @@ class AuthService {
26311
26313
  if (extJwtSigner.targetToken) {
26312
26314
  localStorage.setItem(tokenTypeKey || 'oauth_callback_target_token', extJwtSigner.targetToken);
26313
26315
  }
26316
+ // Pin the controller selected when the flow was initiated. The IdP round-trip reloads state,
26317
+ // so the callback must exchange against this controller, not whatever is selected on return.
26318
+ localStorage.setItem('oauth_callback_controller', this.settingsService.settings?.selectedEdgeController || '');
26314
26319
  this.oauthService.configure(oauthConfig);
26315
26320
  return this.oauthService.loadDiscoveryDocumentAndTryLogin().then((initSuccess) => {
26316
26321
  if (initSuccess) {
@@ -26338,7 +26343,7 @@ class AuthService {
26338
26343
  get accessToken() {
26339
26344
  return this.oauthService.getAccessToken();
26340
26345
  }
26341
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthService, deps: [{ token: i1$6.OAuthService }, { token: i1$3.HttpClient }, { token: GrowlerService }, { token: i4.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
26346
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthService, deps: [{ token: i1$6.OAuthService }, { token: i1$3.HttpClient }, { token: GrowlerService }, { token: i4.Router }, { token: SETTINGS_SERVICE }], target: i0.ɵɵFactoryTarget.Injectable }); }
26342
26347
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthService, providedIn: 'root' }); }
26343
26348
  }
26344
26349
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthService, decorators: [{
@@ -26346,7 +26351,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
26346
26351
  args: [{
26347
26352
  providedIn: 'root'
26348
26353
  }]
26349
- }], ctorParameters: () => [{ type: i1$6.OAuthService }, { type: i1$3.HttpClient }, { type: GrowlerService }, { type: i4.Router }] });
26354
+ }], ctorParameters: () => [{ type: i1$6.OAuthService }, { type: i1$3.HttpClient }, { type: GrowlerService }, { type: i4.Router }, { type: SettingsServiceClass, decorators: [{
26355
+ type: Inject,
26356
+ args: [SETTINGS_SERVICE]
26357
+ }] }] });
26350
26358
 
26351
26359
  /*
26352
26360
  Copyright NetFoundry Inc.
@@ -26409,9 +26417,6 @@ class JwtSignerFormComponent extends ProjectableForm {
26409
26417
  }
26410
26418
  ngAfterViewInit() {
26411
26419
  super.ngAfterViewInit(true);
26412
- defer(() => {
26413
- this.scrollContainer?.nativeElement.scrollTo(0, this.scrollContainer?.nativeElement.scrollHeight);
26414
- });
26415
26420
  }
26416
26421
  entityUpdated() {
26417
26422
  super.entityUpdated();
@@ -26870,11 +26875,11 @@ class JwtSignerFormComponent extends ProjectableForm {
26870
26875
  return !this.errors.jwksEndpoint;
26871
26876
  }
26872
26877
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, deps: [{ token: JwtSignerFormService }, { token: SchemaService }, { token: GrowlerService }, { token: SHAREDZ_EXTENSION }, { token: SETTINGS_SERVICE }, { token: ZITI_DATA_SERVICE }, { token: i4.Router }, { token: i4.ActivatedRoute }, { token: i1$1.Location }, { token: ValidationService }, { token: AuthService }, { token: i1$6.OAuthService }, { token: i1$3.HttpClient }, { token: ZAC_LOGIN_SERVICE }, { token: i5.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
26873
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <input class=\"form-field-input read-only\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
26878
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
26874
26879
  }
26875
26880
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, decorators: [{
26876
26881
  type: Component,
26877
- args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <input class=\"form-field-input read-only\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
26882
+ args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
26878
26883
  }], ctorParameters: () => [{ type: JwtSignerFormService }, { type: SchemaService }, { type: GrowlerService }, { type: undefined, decorators: [{
26879
26884
  type: Inject,
26880
26885
  args: [SHAREDZ_EXTENSION]
@@ -31445,7 +31450,11 @@ class CallbackComponent {
31445
31450
  token = this.oauthService.getAccessToken();
31446
31451
  }
31447
31452
  const prefix = '/edge/client/v1';
31448
- const url = this.settingsService.settings.selectedEdgeController;
31453
+ // Use the controller pinned when the flow was initiated, not the currently
31454
+ // selected one (which may have changed across the IdP redirect).
31455
+ const url = localStorage.getItem('oauth_callback_controller') || this.settingsService.settings.selectedEdgeController;
31456
+ localStorage.removeItem('oauth_callback_controller');
31457
+ this.settingsService.settings.selectedEdgeController = url;
31449
31458
  let doNav = true;
31450
31459
  let isTest = false;
31451
31460
  if (!isEmpty(redirectRoute)) {
@@ -31750,15 +31759,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
31750
31759
  }], ctorParameters: () => [] });
31751
31760
 
31752
31761
  class ProfileComponent {
31753
- constructor(zitiService, growlerService) {
31762
+ constructor(zitiService, settingsService, growlerService) {
31754
31763
  this.zitiService = zitiService;
31764
+ this.settingsService = settingsService;
31755
31765
  this.growlerService = growlerService;
31756
31766
  this.pageTitle = 'Profile';
31757
31767
  this.currentPassword = '';
31758
31768
  this.newPassword = '';
31759
31769
  this.confirmPassword = '';
31770
+ this.passwordChangeDisabled = false;
31771
+ this.passwordChangeMessage = '';
31772
+ this.updatePasswordChangeState();
31773
+ this.settingsSubscription = this.settingsService.settingsChange.subscribe(() => {
31774
+ this.updatePasswordChangeState();
31775
+ });
31776
+ }
31777
+ ngOnDestroy() {
31778
+ this.settingsSubscription?.unsubscribe();
31779
+ }
31780
+ updatePasswordChangeState() {
31781
+ const authMode = this.settingsService?.settings?.session?.authMode;
31782
+ this.passwordChangeDisabled = authMode === 'oidc';
31783
+ this.passwordChangeMessage = this.passwordChangeDisabled
31784
+ ? 'Your password is managed by your identity provider and cannot be changed here.'
31785
+ : '';
31760
31786
  }
31761
31787
  resetPassword() {
31788
+ if (this.passwordChangeDisabled) {
31789
+ const growlerData = new GrowlerModel('error', 'Error', 'Password Change Not Allowed', 'Your password is managed by your identity provider and cannot be changed here.');
31790
+ this.growlerService.show(growlerData);
31791
+ return;
31792
+ }
31762
31793
  if (!this.validate()) {
31763
31794
  return;
31764
31795
  }
@@ -31775,7 +31806,7 @@ class ProfileComponent {
31775
31806
  });
31776
31807
  }
31777
31808
  validate() {
31778
- if (isEmpty(this.newPassword) || isEmpty(this.newPassword) || isEmpty(this.confirmPassword)) {
31809
+ if (isEmpty(this.currentPassword) || isEmpty(this.newPassword) || isEmpty(this.confirmPassword)) {
31779
31810
  const growlerData = new GrowlerModel('error', 'Error', `Password Missing`, `You have missing fields in your submission. Check your input and try again.`);
31780
31811
  this.growlerService.show(growlerData);
31781
31812
  return false;
@@ -31787,15 +31818,18 @@ class ProfileComponent {
31787
31818
  }
31788
31819
  return true;
31789
31820
  }
31790
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ProfileComponent, deps: [{ token: ZITI_DATA_SERVICE }, { token: GrowlerService }], target: i0.ɵɵFactoryTarget.Component }); }
31791
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ProfileComponent, isStandalone: false, selector: "lib-profile", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab\" routerLink=\"/settings\">Manage Settings</div>\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Current Password</label>\n <input [(ngModel)]=\"currentPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <label>New Password</label>\n <input [(ngModel)]=\"newPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div>\n <label>Confirm Password</label>\n <input [(ngModel)]=\"confirmPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <div class=\"button\" (click)=\"resetPassword()\">Save</div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}.button{font-size:.6875rem;font-weight:400;font-family:Open Sans;font-weight:600;display:inline-block;min-width:9.375rem;padding-left:1.25rem;clear:both;overflow:hidden;padding-right:1.25rem;height:2.5rem;padding:.4375rem;width:fit-content;text-transform:uppercase;text-align:center;border-radius:var(--inputBorderRadius);line-height:1.5625rem;position:relative;border:0;background:var(--secondary);color:#fff;white-space:nowrap;transition:all .15s ease;cursor:pointer;box-shadow:var(--shadow)}label{color:var(--dark);text-transform:uppercase;margin-top:.625rem;position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
31821
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ProfileComponent, deps: [{ token: ZITI_DATA_SERVICE }, { token: SETTINGS_SERVICE }, { token: GrowlerService }], target: i0.ɵɵFactoryTarget.Component }); }
31822
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ProfileComponent, isStandalone: false, selector: "lib-profile", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab\" routerLink=\"/settings\">Manage Settings</div>\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n </div>\n <div class=\"constrained\">\n @if (passwordChangeDisabled) {\n <div class=\"password-change-message\">{{passwordChangeMessage}}</div>\n } @else {\n <div class=\"grid split\">\n <div>\n <label>Current Password</label>\n <input [(ngModel)]=\"currentPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <label>New Password</label>\n <input [(ngModel)]=\"newPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div>\n <label>Confirm Password</label>\n <input [(ngModel)]=\"confirmPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <div class=\"button\" (click)=\"resetPassword()\">Save</div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}.password-change-message{padding:1rem;border:.0625rem solid var(--stroke);border-radius:var(--inputBorderRadius);background:var(--light);color:var(--dark);line-height:1.5}.button{font-size:.6875rem;font-weight:400;font-family:Open Sans;font-weight:600;display:inline-block;min-width:9.375rem;padding-left:1.25rem;clear:both;overflow:hidden;padding-right:1.25rem;height:2.5rem;padding:.4375rem;width:fit-content;text-transform:uppercase;text-align:center;border-radius:var(--inputBorderRadius);line-height:1.5625rem;position:relative;border:0;background:var(--secondary);color:#fff;white-space:nowrap;transition:all .15s ease;cursor:pointer;box-shadow:var(--shadow)}label{color:var(--dark);text-transform:uppercase;margin-top:.625rem;position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
31792
31823
  }
31793
31824
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ProfileComponent, decorators: [{
31794
31825
  type: Component,
31795
- args: [{ selector: 'lib-profile', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab\" routerLink=\"/settings\">Manage Settings</div>\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Current Password</label>\n <input [(ngModel)]=\"currentPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <label>New Password</label>\n <input [(ngModel)]=\"newPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div>\n <label>Confirm Password</label>\n <input [(ngModel)]=\"confirmPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <div class=\"button\" (click)=\"resetPassword()\">Save</div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}.button{font-size:.6875rem;font-weight:400;font-family:Open Sans;font-weight:600;display:inline-block;min-width:9.375rem;padding-left:1.25rem;clear:both;overflow:hidden;padding-right:1.25rem;height:2.5rem;padding:.4375rem;width:fit-content;text-transform:uppercase;text-align:center;border-radius:var(--inputBorderRadius);line-height:1.5625rem;position:relative;border:0;background:var(--secondary);color:#fff;white-space:nowrap;transition:all .15s ease;cursor:pointer;box-shadow:var(--shadow)}label{color:var(--dark);text-transform:uppercase;margin-top:.625rem;position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
31826
+ args: [{ selector: 'lib-profile', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab\" routerLink=\"/settings\">Manage Settings</div>\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n </div>\n <div class=\"constrained\">\n @if (passwordChangeDisabled) {\n <div class=\"password-change-message\">{{passwordChangeMessage}}</div>\n } @else {\n <div class=\"grid split\">\n <div>\n <label>Current Password</label>\n <input [(ngModel)]=\"currentPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <label>New Password</label>\n <input [(ngModel)]=\"newPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div>\n <label>Confirm Password</label>\n <input [(ngModel)]=\"confirmPassword\" type=\"password\" autocomplete=\"new-password\"/>\n </div>\n <div></div>\n <div>\n <div class=\"button\" (click)=\"resetPassword()\">Save</div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}.password-change-message{padding:1rem;border:.0625rem solid var(--stroke);border-radius:var(--inputBorderRadius);background:var(--light);color:var(--dark);line-height:1.5}.button{font-size:.6875rem;font-weight:400;font-family:Open Sans;font-weight:600;display:inline-block;min-width:9.375rem;padding-left:1.25rem;clear:both;overflow:hidden;padding-right:1.25rem;height:2.5rem;padding:.4375rem;width:fit-content;text-transform:uppercase;text-align:center;border-radius:var(--inputBorderRadius);line-height:1.5625rem;position:relative;border:0;background:var(--secondary);color:#fff;white-space:nowrap;transition:all .15s ease;cursor:pointer;box-shadow:var(--shadow)}label{color:var(--dark);text-transform:uppercase;margin-top:.625rem;position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
31796
31827
  }], ctorParameters: () => [{ type: ZitiDataService, decorators: [{
31797
31828
  type: Inject,
31798
31829
  args: [ZITI_DATA_SERVICE]
31830
+ }] }, { type: SettingsServiceClass, decorators: [{
31831
+ type: Inject,
31832
+ args: [SETTINGS_SERVICE]
31799
31833
  }] }, { type: GrowlerService }] });
31800
31834
 
31801
31835
  class GeolocationService {
@@ -39835,12 +39869,18 @@ class NodeDataService extends ZitiDataService {
39835
39869
  return results;
39836
39870
  })));
39837
39871
  }
39838
- doGet(type, paging, filters = [], url) {
39872
+ doGet(type, paging, filters = [], url, useClient) {
39839
39873
  const nodeServerURL = window.location.origin;
39840
39874
  const serviceUrl = nodeServerURL + '/api/data';
39841
39875
  const urlFilter = this.getUrlFilter(paging, filters);
39842
39876
  paging.filter = urlFilter;
39843
39877
  const body = { paging: paging, type: type, url: url };
39878
+ if (useClient) {
39879
+ // Fetch the public client API unauthenticated (pre-login) via the selected
39880
+ // controller, not the session-scoped mgmt API. See openziti/ziti-console#915.
39881
+ body.useClient = true;
39882
+ body.controllerUrl = this.settingsService?.settings?.selectedEdgeController;
39883
+ }
39844
39884
  return firstValueFrom(this.httpClient.post(serviceUrl, body, {}).pipe(catchError((err) => {
39845
39885
  const error = "Server Not Accessible";
39846
39886
  if (err.code != "ECONNREFUSED")
@@ -41022,6 +41062,11 @@ class SessionRefreshService {
41022
41062
  };
41023
41063
  }
41024
41064
  start() {
41065
+ // Skip the monitor on /callback so a stale expired session can't redirect to /login
41066
+ // before the code exchange completes. See openziti/ziti-console#915.
41067
+ if (window.location.pathname.endsWith('/callback')) {
41068
+ return;
41069
+ }
41025
41070
  if (!this.hasOidcSession()) {
41026
41071
  return;
41027
41072
  }
@@ -41150,7 +41195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
41150
41195
  }] }, { type: ZitiOidcService }, { type: GrowlerService }, { type: i0.Injector }] });
41151
41196
 
41152
41197
  const ZAC_VERSION = {
41153
- "version": "4.4.0"
41198
+ "version": "4.5.0"
41154
41199
  };
41155
41200
 
41156
41201
  /*