feat(console): guide users when configuring IDPs (#7572)

* feat(console): show external idp remote config

* reuse copy-row

* finish google and saml sp

* finish apps

* add next steps modal

* rollout

* activate

* fix saml urls

* fix saml provider

* complete providers

* translate

* update google docs

* update entra id oidc docs

* update entra id saml docs

* update github docs

* update gitlab docs

* update apple docs

* update okta oidc docs

* update okta saml docs

* update keycloak docs

* update mocksaml

* cleanup

* lint

* lint

* fix overriden classes

* encapsulate styles

* fix icon classes

---------

Co-authored-by: peintnermax <max@caos.ch>
This commit is contained in:
Elio Bischof 2024-03-27 21:10:31 +01:00 committed by GitHub
parent 84644214d7
commit d26391a642
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
79 changed files with 1960 additions and 604 deletions

View File

@ -0,0 +1,16 @@
<div class="cnsl-cr-row">
<div class="cnsl-cr-secondary-text" [ngStyle]="{ minWidth: labelMinWidth }">{{ label }}</div>
<button
class="cnsl-cr-copy"
[disabled]="copied === value"
[matTooltip]="(copied !== value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard
[valueToCopy]="value"
(copiedValue)="copied = $event"
>
{{ value }}
</button>
<div class="cnsl-cr-item">
<ng-content></ng-content>
</div>
</div>

View File

@ -0,0 +1,41 @@
.cnsl-cr-row {
display: flex;
align-items: center;
.cnsl-cr-right {
flex-shrink: 0;
}
}
@mixin copy-row-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$foreground: map-get($theme, foreground);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
.cnsl-cr-copy {
flex-grow: 1;
text-align: left;
transition: opacity 0.15s ease-in-out;
background-color: #8795a110;
border: 1px solid #8795a160;
border-radius: 4px;
padding: 0.25rem 1rem;
margin: 0.25rem 0rem;
color: $button-text-color;
text-overflow: ellipsis;
overflow: hidden;
cursor: copy;
&[disabled] {
color: $button-disabled-text-color;
}
}
}
.row {
display: flex;
align-items: center;
.right {
flex-shrink: 0;
}
}

View File

@ -0,0 +1,21 @@
import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip';
import { CopyToClipboardModule } from '../../directives/copy-to-clipboard/copy-to-clipboard.module';
@Component({
standalone: true,
selector: 'cnsl-copy-row',
templateUrl: './copy-row.component.html',
styleUrls: ['./copy-row.component.scss'],
imports: [CommonModule, TranslateModule, MatButtonModule, MatTooltipModule, CopyToClipboardModule],
})
export class CopyRowComponent {
@Input({ required: true }) public label = '';
@Input({ required: true }) public value = '';
@Input() public labelMinWidth = '';
public copied = '';
}

View File

@ -5,7 +5,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { Router, RouterLink } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { BehaviorSubject, firstValueFrom, Observable, Subject } from 'rxjs';
import {
ListProvidersRequest as AdminListProvidersRequest,
ListProvidersResponse as AdminListProvidersResponse,
@ -36,6 +36,8 @@ import { ContextChangedWorkflowOverlays } from 'src/app/services/overlay/workflo
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { ActivateIdpService } from '../../services/activate-idp.service';
import { first } from 'rxjs/operators';
@Component({
selector: 'cnsl-idp-table',
@ -70,6 +72,7 @@ export class IdpTableComponent implements OnInit, OnDestroy {
private toast: ToastService,
private dialog: MatDialog,
private router: Router,
private activateIdpSvc: ActivateIdpService,
) {
this.selection.changed.subscribe(() => {
this.changedSelection.emit(this.selection.selected);
@ -298,93 +301,15 @@ export class IdpTableComponent implements OnInit, OnDestroy {
}
}
private addLoginPolicy(): Promise<AddCustomLoginPolicyResponse.AsObject> {
const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(this.loginPolicy.allowExternalIdp);
mgmtreq.setAllowRegister(this.loginPolicy.allowRegister);
mgmtreq.setAllowUsernamePassword(this.loginPolicy.allowUsernamePassword);
mgmtreq.setForceMfa(this.loginPolicy.forceMfa);
mgmtreq.setPasswordlessType(this.loginPolicy.passwordlessType);
mgmtreq.setHidePasswordReset(this.loginPolicy.hidePasswordReset);
mgmtreq.setMultiFactorsList(this.loginPolicy.multiFactorsList);
mgmtreq.setSecondFactorsList(this.loginPolicy.secondFactorsList);
const pcl = new Duration()
.setSeconds(this.loginPolicy.passwordCheckLifetime?.seconds ?? 0)
.setNanos(this.loginPolicy.passwordCheckLifetime?.nanos ?? 0);
mgmtreq.setPasswordCheckLifetime(pcl);
const elcl = new Duration()
.setSeconds(this.loginPolicy.externalLoginCheckLifetime?.seconds ?? 0)
.setNanos(this.loginPolicy.externalLoginCheckLifetime?.nanos ?? 0);
mgmtreq.setExternalLoginCheckLifetime(elcl);
const misl = new Duration()
.setSeconds(this.loginPolicy.mfaInitSkipLifetime?.seconds ?? 0)
.setNanos(this.loginPolicy.mfaInitSkipLifetime?.nanos ?? 0);
mgmtreq.setMfaInitSkipLifetime(misl);
const sfcl = new Duration()
.setSeconds(this.loginPolicy.secondFactorCheckLifetime?.seconds ?? 0)
.setNanos(this.loginPolicy.secondFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setSecondFactorCheckLifetime(sfcl);
const mficl = new Duration()
.setSeconds(this.loginPolicy.multiFactorCheckLifetime?.seconds ?? 0)
.setNanos(this.loginPolicy.multiFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setMultiFactorCheckLifetime(mficl);
mgmtreq.setAllowDomainDiscovery(this.loginPolicy.allowDomainDiscovery);
mgmtreq.setIgnoreUnknownUsernames(this.loginPolicy.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(this.loginPolicy.defaultRedirectUri);
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
}
public addIdp(idp: Provider.AsObject): Promise<any> {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
if (this.isDefault) {
return this.addLoginPolicy()
.then(() => {
this.loginPolicy.isDefault = false;
return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, idp.owner).then(() => {
this.toast.showInfo('IDP.TOAST.ADDED', true);
setTimeout(() => {
this.reloadIDPs$.next();
}, 2000);
});
})
.catch((error) => {
this.toast.showError(error);
});
} else {
return (this.service as ManagementService)
.addIDPToLoginPolicy(idp.id, idp.owner)
.then(() => {
this.toast.showInfo('IDP.TOAST.ADDED', true);
setTimeout(() => {
this.reloadIDPs$.next();
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
});
}
case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService)
.addIDPToLoginPolicy(idp.id)
.then(() => {
this.toast.showInfo('IDP.TOAST.ADDED', true);
setTimeout(() => {
this.reloadIDPs$.next();
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
});
}
return firstValueFrom(this.activateIdpSvc.activateIdp(this.service, idp.id, idp.owner, this.loginPolicy))
.then(() => {
this.toast.showInfo('IDP.TOAST.ADDED', true);
setTimeout(() => {
this.reloadIDPs$.next();
}, 2000);
})
.catch(this.toast.showError);
}
public removeIdp(idp: Provider.AsObject): void {
@ -403,7 +328,8 @@ export class IdpTableComponent implements OnInit, OnDestroy {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
if (this.isDefault) {
this.addLoginPolicy()
this.activateIdpSvc
.addLoginPolicy(this.service as ManagementService, this.loginPolicy)
.then(() => {
this.loginPolicy.isDefault = false;
return (this.service as ManagementService)

View File

@ -7,9 +7,29 @@
<img class="idp-logo apple dark" src="./assets/images/idp/apple-dark.svg" alt="apple" />
<img class="idp-logo apple light" src="./assets/images/idp/apple.svg" alt="apple" />
<h1>{{ 'IDP.CREATE.APPLE.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Apple' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Apple' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/apple#apple-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.APPLE.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -131,8 +151,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, Observable, take } from 'rxjs';
import {
AddAppleProviderRequest as AdminAddAppleProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
const MAX_ALLOWED_SIZE = 5 * 1024;
@ -34,8 +35,11 @@ const MAX_ALLOWED_SIZE = 5 * 1024;
export class ProviderAppleComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -47,6 +51,25 @@ export class ProviderAppleComponent {
public provider?: Provider.AsObject;
public updatePrivateKey: boolean = false;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/apple#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -54,6 +77,7 @@ export class ProviderAppleComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', []),
@ -115,6 +139,10 @@ export class ProviderAppleComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -138,7 +166,7 @@ export class ProviderAppleComponent {
}
public submitForm(): void {
this.provider ? this.updateAppleProvider() : this.addAppleProvider();
this.provider || this.justCreated$.value ? this.updateAppleProvider() : this.addAppleProvider();
}
public addAppleProvider(): void {
@ -158,11 +186,9 @@ export class ProviderAppleComponent {
this.loading = true;
this.service
.addAppleProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -171,10 +197,10 @@ export class ProviderAppleComponent {
}
public updateAppleProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new MgmtUpdateAppleProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setTeamId(this.teamId?.value);
@ -201,7 +227,7 @@ export class ProviderAppleComponent {
});
} else if (PolicyComponentServiceType.ADMIN) {
const req = new AdminUpdateAppleProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setTeamId(this.teamId?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/ms.svg" alt="microsoft" />
<h1>{{ 'IDP.CREATE.AZUREAD.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Microsoft' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Microsoft' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-oidc#entra-id-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.AZUREAD.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -104,8 +124,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,13 +4,13 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddAzureADProviderRequest as AdminAddAzureADProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
UpdateAzureADProviderRequest as AdminUpdateAzureADProviderRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { AzureADTenant, AzureADTenantType, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
import { AzureADTenant, AzureADTenantType, IDPOwnerType, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
import {
AddAzureADProviderRequest as MgmtAddAzureADProviderRequest,
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-azure-ad',
@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
export class ProviderAzureADComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -53,6 +57,25 @@ export class ProviderAzureADComponent {
AzureADTenantType.AZURE_AD_TENANT_TYPE_CONSUMERS,
];
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-oidc#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -60,6 +83,7 @@ export class ProviderAzureADComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', []),
@ -122,6 +146,10 @@ export class ProviderAzureADComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -166,7 +194,7 @@ export class ProviderAzureADComponent {
}
public submitForm(): void {
this.provider ? this.updateAzureADProvider() : this.addAzureADProvider();
this.provider || this.justCreated$.value ? this.updateAzureADProvider() : this.addAzureADProvider();
}
public addAzureADProvider(): void {
@ -194,11 +222,9 @@ export class ProviderAzureADComponent {
this.loading = true;
this.service
.addAzureADProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -207,13 +233,13 @@ export class ProviderAzureADComponent {
}
public updateAzureADProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateAzureADProviderRequest()
: new AdminUpdateAzureADProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setEmailVerified(this.emailVerified?.value);

View File

@ -6,11 +6,30 @@
<div class="title-row">
<img class="idp-logo dark" src="./assets/images/idp/github-dark.svg" alt="github" />
<img class="idp-logo light" src="./assets/images/idp/github.svg" alt="github" />
<h1>{{ 'IDP.CREATE.GITHUBES.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitHub' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitHub' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/github#github-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.GITHUBES.DESCRIPTION' | translate }}</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
@ -103,8 +122,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGitHubEnterpriseServerProviderRequest as AdminAddGitHubEnterpriseServerProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-github-es',
@ -33,9 +34,12 @@ export class ProviderGithubESComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
public updateClientSecret: boolean = false;
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public form!: UntypedFormGroup;
@ -44,6 +48,25 @@ export class ProviderGithubESComponent {
public provider?: Provider.AsObject;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/github#optional-add-zitadel-action-to-autofill-userdata`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/github#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -51,6 +74,7 @@ export class ProviderGithubESComponent {
private injector: Injector,
private _location: Location,
breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new UntypedFormGroup({
name: new UntypedFormControl('', [requiredValidator]),
@ -113,6 +137,10 @@ export class ProviderGithubESComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
this.loading = true;
const req =
@ -137,7 +165,7 @@ export class ProviderGithubESComponent {
}
public submitForm(): void {
this.provider ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
this.provider || this.justCreated$.value ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
}
public addGenericOAuthProvider(): void {
@ -158,11 +186,9 @@ export class ProviderGithubESComponent {
this.loading = true;
this.service
.addGitHubEnterpriseServerProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -171,12 +197,12 @@ export class ProviderGithubESComponent {
}
public updateGenericOAuthProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGitHubEnterpriseServerProviderRequest()
: new AdminUpdateGitHubEnterpriseServerProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setAuthorizationEndpoint(this.authorizationEndpoint?.value);
req.setTokenEndpoint(this.tokenEndpoint?.value);

View File

@ -7,9 +7,29 @@
<img class="idp-logo dark" src="./assets/images/idp/github-dark.svg" alt="github" />
<img class="idp-logo light" src="./assets/images/idp/github.svg" alt="github" />
<h1>{{ 'IDP.CREATE.GITHUB.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitHub' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitHub' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/github#github-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.GITHUB.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -90,8 +110,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGitHubProviderRequest as AdminAddGithubProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-github',
@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
export class ProviderGithubComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -45,6 +49,25 @@ export class ProviderGithubComponent {
public provider?: Provider.AsObject;
public updateClientSecret: boolean = false;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/github#optional-add-zitadel-action-to-autofill-userdata`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/github#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -52,6 +75,7 @@ export class ProviderGithubComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', []),
@ -111,6 +135,10 @@ export class ProviderGithubComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -134,7 +162,7 @@ export class ProviderGithubComponent {
}
public submitForm(): void {
this.provider ? this.updateGithubProvider() : this.addGithubProvider();
this.provider || this.justCreated$.value ? this.updateGithubProvider() : this.addGithubProvider();
}
public addGithubProvider(): void {
@ -152,11 +180,9 @@ export class ProviderGithubComponent {
this.loading = true;
this.service
.addGitHubProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -165,12 +191,12 @@ export class ProviderGithubComponent {
}
public updateGithubProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGithubProviderRequest()
: new AdminUpdateGithubProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setScopesList(this.scopesList?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/gitlab.svg" alt="gitlab" />
<h1>{{ 'IDP.CREATE.GITLABSELFHOSTED.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitLab' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitLab' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#gitlab-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.GITLABSELFHOSTED.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -93,8 +113,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGitLabSelfHostedProviderRequest as AdminAddGitLabSelfHostedProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-gitlab-self-hosted',
@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
export class ProviderGitlabSelfHostedComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -45,6 +49,25 @@ export class ProviderGitlabSelfHostedComponent {
public provider?: Provider.AsObject;
public updateClientSecret: boolean = false;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#optional-add-zitadel-action-to-autofill-userdata`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -52,6 +75,7 @@ export class ProviderGitlabSelfHostedComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', [requiredValidator]),
@ -112,6 +136,10 @@ export class ProviderGitlabSelfHostedComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -135,7 +163,7 @@ export class ProviderGitlabSelfHostedComponent {
}
public submitForm(): void {
this.provider ? this.updateGitlabSelfHostedProvider() : this.addGitlabSelfHostedProvider();
this.provider || this.justCreated$.value ? this.updateGitlabSelfHostedProvider() : this.addGitlabSelfHostedProvider();
}
public addGitlabSelfHostedProvider(): void {
@ -154,11 +182,9 @@ export class ProviderGitlabSelfHostedComponent {
this.loading = true;
this.service
.addGitLabSelfHostedProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -167,12 +193,12 @@ export class ProviderGitlabSelfHostedComponent {
}
public updateGitlabSelfHostedProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGitLabSelfHostedProviderRequest()
: new AdminUpdateGitLabSelfHostedProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setIssuer(this.issuer?.value);
req.setClientId(this.clientId?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/gitlab.svg" alt="gitlab" />
<h1>{{ 'IDP.CREATE.GITLAB.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitLab' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitLab' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#gitlab-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.GITLAB.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -89,8 +109,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGitLabProviderRequest as AdminAddGitLabProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-gitlab',
@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
export class ProviderGitlabComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -45,6 +49,25 @@ export class ProviderGitlabComponent {
public provider?: Provider.AsObject;
public updateClientSecret: boolean = false;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#optional-add-zitadel-action-to-autofill-userdata`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -52,6 +75,7 @@ export class ProviderGitlabComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', []),
@ -111,6 +135,10 @@ export class ProviderGitlabComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -134,7 +162,7 @@ export class ProviderGitlabComponent {
}
public submitForm(): void {
this.provider ? this.updateGitlabProvider() : this.addGitlabProvider();
this.provider || this.justCreated$.value ? this.updateGitlabProvider() : this.addGitlabProvider();
}
public addGitlabProvider(): void {
@ -152,11 +180,9 @@ export class ProviderGitlabComponent {
this.loading = true;
this.service
.addGitLabProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -165,12 +191,12 @@ export class ProviderGitlabComponent {
}
public updateGitlabProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGitLabProviderRequest()
: new AdminUpdateGitLabProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setScopesList(this.scopesList?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/google.png" alt="google" />
<h1>{{ 'IDP.CREATE.GOOGLE.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Google' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Google' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/google#google-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.GOOGLE.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -88,8 +108,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGoogleProviderRequest as AdminAddGoogleProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-google',
@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
export class ProviderGoogleComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -45,6 +49,25 @@ export class ProviderGoogleComponent {
public provider?: Provider.AsObject;
public updateClientSecret: boolean = false;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/google#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -52,6 +75,7 @@ export class ProviderGoogleComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', []),
@ -59,7 +83,6 @@ export class ProviderGoogleComponent {
clientSecret: new FormControl('', [requiredValidator]),
scopesList: new FormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
@ -102,7 +125,6 @@ export class ProviderGoogleComponent {
this.breadcrumbService.setBreadcrumb([iamBread]);
break;
}
this.id = this.route.snapshot.paramMap.get('id');
if (this.id) {
this.clientSecret?.setValidators([]);
@ -111,6 +133,10 @@ export class ProviderGoogleComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -134,7 +160,7 @@ export class ProviderGoogleComponent {
}
public submitForm(): void {
this.provider ? this.updateGoogleProvider() : this.addGoogleProvider();
this.provider || this.justCreated$.value ? this.updateGoogleProvider() : this.addGoogleProvider();
}
public addGoogleProvider(): void {
@ -152,11 +178,9 @@ export class ProviderGoogleComponent {
this.loading = true;
this.service
.addGoogleProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -165,10 +189,10 @@ export class ProviderGoogleComponent {
}
public updateGoogleProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new MgmtUpdateGoogleProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setScopesList(this.scopesList?.value);
@ -193,7 +217,7 @@ export class ProviderGoogleComponent {
});
} else if (PolicyComponentServiceType.ADMIN) {
const req = new AdminUpdateGoogleProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setScopesList(this.scopesList?.value);

View File

@ -6,9 +6,28 @@
<div class="title-row">
<mat-icon class="idp-logo" svgIcon="mdi_jwt" alt="jwt" />
<h1>{{ 'IDP.CREATE.JWT.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.JWT.TITLE' | translate"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.JWT.DESCRIPTION' | translate"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/jwt-idp"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.JWT.DESCRIPTION' | translate }}</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
@ -57,8 +76,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -23,6 +23,8 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { BehaviorSubject } from 'rxjs';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-jwt',
@ -32,8 +34,11 @@ export class ProviderJWTComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@ -42,6 +47,24 @@ export class ProviderJWTComponent {
public provider?: Provider.AsObject;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -49,6 +72,7 @@ export class ProviderJWTComponent {
private injector: Injector,
private _location: Location,
breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data['serviceType'];
@ -108,6 +132,10 @@ export class ProviderJWTComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -131,7 +159,7 @@ export class ProviderJWTComponent {
}
public submitForm(): void {
this.provider ? this.updateJWTProvider() : this.addJWTProvider();
this.provider || this.justCreated$.value ? this.updateJWTProvider() : this.addJWTProvider();
}
public addJWTProvider(): void {
@ -149,11 +177,9 @@ export class ProviderJWTComponent {
this.loading = true;
this.service
.addJWTProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -162,12 +188,12 @@ export class ProviderJWTComponent {
}
public updateJWTProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateJWTProviderRequest()
: new AdminUpdateJWTProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setHeaderName(this.headerName?.value);
req.setIssuer(this.issuer?.value);

View File

@ -6,9 +6,27 @@
<div class="title-row">
<i class="idp-icon las la-building"></i>
<h1>{{ 'IDP.CREATE.LDAP.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.LDAP.TITLE' | translate: { provider: 'Google' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.LDAP.DESCRIPTION' | translate: { provider: 'Google' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/ldap"
[activateLink]="activateLink$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.LDAP.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -118,8 +136,9 @@
[disabled]="!form.valid || !attributes.toObject().idAttribute || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -3,7 +3,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddLDAPProviderRequest as AdminAddLDAPProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -23,6 +23,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { minArrayLengthValidator, requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-ldap',
@ -33,8 +34,11 @@ export class ProviderLDAPComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
public attributes: LDAPAttributes = new LDAPAttributes();
// DEPRECATED: use id$ instead
public id: string | null = '';
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public form!: FormGroup;
@ -43,6 +47,20 @@ export class ProviderLDAPComponent {
public provider?: Provider.AsObject;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/google#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -50,6 +68,7 @@ export class ProviderLDAPComponent {
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new FormGroup({
name: new FormControl('', [requiredValidator]),
@ -116,6 +135,10 @@ export class ProviderLDAPComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
@ -179,11 +202,9 @@ export class ProviderLDAPComponent {
this.loading = true;
(this.service as ManagementService)
.addLDAPProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);

View File

@ -0,0 +1,77 @@
<div class="cnsl-provider-next">
<cnsl-card
[title]="'DESCRIPTIONS.SETTINGS.IDPS.NEXT' | translate"
[expanded]="!!expanded"
*ngIf="configureProvider || autofillLink || activateLink"
>
<cnsl-info-section *ngIf="activateLink">
<div class="title-row">
<div class="left">
<h2 class="title">{{ 'DESCRIPTIONS.SETTINGS.IDPS.ACTIVATE.TITLE' | translate }}</h2>
<div>
<a
mat-icon-button
*ngIf="activateLink"
card-actions
mat-icon-button
[href]="activateLink"
rel="noreferrer"
target="_blank"
>
<mat-icon class="next-icon">info_outline</mat-icon>
</a>
</div>
</div>
<div class="right">
<button color="primary" mat-raised-button class="continue-button" (click)="activate.emit()">
{{ 'ACTIONS.ACTIVATE' | translate }}
</button>
</div>
</div>
<p class="cnsl-secondary-text description">{{ 'DESCRIPTIONS.SETTINGS.IDPS.ACTIVATE.DESCRIPTION' | translate }}</p>
</cnsl-info-section>
<div class="section" *ngIf="configureProvider">
<div class="title-row">
<div class="left">
<h2 class="title">{{ configureTitle }}</h2>
<a
mat-icon-button
*ngIf="configureLink"
card-actions
mat-icon-button
[href]="configureLink"
rel="noreferrer"
target="_blank"
>
<mat-icon class="next-icon">info_outline</mat-icon>
</a>
</div>
</div>
<p class="cnsl-secondary-text description">{{ configureDescription }}</p>
<cnsl-copy-row *ngFor="let row of copyUrls" [label]="row.label" [value]="row.url" labelMinWidth="200px">
<a *ngIf="row.downloadable" class="download-button" mat-stroked-button [href]="row.url" download>{{
'ACTIONS.DOWNLOAD' | translate
}}</a>
</cnsl-copy-row>
</div>
<div class="section" *ngIf="autofillLink">
<div class="title-row">
<div class="left">
<h2 class="title">{{ 'DESCRIPTIONS.SETTINGS.IDPS.AUTOFILL.TITLE' | translate }}</h2>
<a
mat-icon-button
*ngIf="autofillLink"
card-actions
mat-icon-button
[href]="autofillLink"
rel="noreferrer"
target="_blank"
>
<mat-icon class="next-icon">info_outline</mat-icon>
</a>
</div>
</div>
<p class="cnsl-secondary-text description">{{ 'DESCRIPTIONS.SETTINGS.IDPS.AUTOFILL.DESCRIPTION' | translate }}</p>
</div>
</cnsl-card>
</div>

View File

@ -0,0 +1,69 @@
.cnsl-provider-next {
.section {
background-color: #8795a110;
border: 1px solid #8795a160;
border-radius: 4px;
padding: 0.25rem 1rem;
margin: 0.25rem 0rem;
.box {
display: flex;
flex-direction: row;
justify-content: space-between;
.right {
flex-shrink: 0;
}
}
}
.title-row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.left {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.right {
flex-shrink: 0;
}
.title {
margin: 0;
}
.next-icon {
font-size: 1.2rem;
height: 1.2rem;
width: 1.2rem;
}
}
.description {
margin-block-start: 0;
font-size: 14px;
}
.download-button {
margin-left: 1rem;
}
}
@mixin provider-next-theme($theme) {
$foreground: map-get($theme, foreground);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
.cnsl-provider-next {
.section {
color: $button-text-color;
&[disabled] {
color: $button-disabled-text-color;
}
}
}
}

View File

@ -0,0 +1,26 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
export interface CopyUrl {
label: string;
url: string;
downloadable?: boolean;
}
@Component({
selector: 'cnsl-provider-next',
templateUrl: './provider-next.component.html',
styleUrls: ['./provider-next.component.scss'],
})
export class ProviderNextComponent {
@Input() copyUrls?: CopyUrl[] | null;
@Input() autofillLink?: string | null;
@Input() activateLink?: string | null;
@Input() configureProvider?: boolean | null;
@Input() configureTitle?: string;
@Input() configureDescription?: string;
@Input() configureLink?: string;
@Input() expanded?: boolean;
@Output() activate = new EventEmitter<void>();
constructor() {}
}

View File

@ -0,0 +1,135 @@
import { Injectable, Injector, Type } from '@angular/core';
import { BehaviorSubject, combineLatestWith, from, Observable, of, shareReplay, switchMap, take } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators';
import { EnvironmentService } from '../../../services/environment.service';
import { CopyUrl } from './provider-next.component';
import { ManagementService } from '../../../services/mgmt.service';
import { AdminService } from '../../../services/admin.service';
import { IDPOwnerType } from '../../../proto/generated/zitadel/idp_pb';
import { ToastService } from '../../../services/toast.service';
import { Data, ParamMap } from '@angular/router';
import { ActivateIdpService } from '../../../services/activate-idp.service';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
@Injectable({
providedIn: 'root',
})
export class ProviderNextService {
constructor(
private env: EnvironmentService,
private toast: ToastService,
private addIdpSvc: ActivateIdpService,
private injector: Injector,
) {}
service(routeData: Observable<Data>): Observable<ManagementService | AdminService> {
return routeData.pipe(
map((data) => {
switch (data['serviceType']) {
case PolicyComponentServiceType.MGMT:
return this.injector.get(ManagementService as Type<ManagementService>);
case PolicyComponentServiceType.ADMIN:
return this.injector.get(AdminService as Type<AdminService>);
default:
throw new Error('Unknown Service Type');
}
}),
shareReplay(1),
);
}
id(paramMap: Observable<ParamMap>, justCreated$: Observable<string>): Observable<string | null> {
return paramMap.pipe(
// The ID observable should also emit when the IDP was just created
combineLatestWith(justCreated$),
map(([params, created]) => (created ? created : params.get('id'))),
shareReplay(1),
);
}
exists(id$: Observable<string | null>): Observable<boolean> {
return id$.pipe(
map((id) => !!id),
shareReplay(1),
);
}
autofillLink(id$: Observable<string | null>, link: string): Observable<string> {
return id$.pipe(
filter((id) => !!id),
map(() => link),
shareReplay(1),
);
}
activateLink(
id$: Observable<string | null>,
justActivated$: Observable<boolean>,
link: string,
service$: Observable<ManagementService | AdminService>,
): Observable<string> {
return id$.pipe(
combineLatestWith(justActivated$, service$),
// Because we also want to emit when the IDP is not active, we return an empty string if the IDP does not exist
switchMap(([id, activated, service]) =>
(!id || activated
? of(false)
: from(service.getLoginPolicy()).pipe(map((policy) => !policy.policy?.idpsList.find((idp) => idp.idpId === id)))
).pipe(map((show) => (!show ? '' : link))),
),
shareReplay(1),
);
}
callbackUrls(): Observable<CopyUrl[]> {
return this.env.env.pipe(
map((env) => [
{
label: 'ZITADEL Callback URL',
url: `${env.issuer}/ui/login/login/externalidp/callback`,
},
]),
);
}
expandWhatNow(
id$: Observable<string | null>,
activateLink$: Observable<string>,
justCreated$: Observable<string>,
): Observable<boolean> {
return id$.pipe(
combineLatestWith(activateLink$, justCreated$),
map(([id, activateLink, created]) => !id || activateLink || created),
map((expand) => !!expand),
shareReplay(1),
);
}
activate(
id$: Observable<string | null>,
emitActivated$: BehaviorSubject<boolean>,
service$: Observable<ManagementService | AdminService>,
): void {
id$
.pipe(
combineLatestWith(service$),
take(1),
switchMap(([id, service]) => {
if (!id) {
throw new Error('No ID');
}
return this.addIdpSvc.activateIdp(
service,
id,
service instanceof AdminService ? IDPOwnerType.IDP_OWNER_TYPE_SYSTEM : IDPOwnerType.IDP_OWNER_TYPE_ORG,
);
}),
)
.subscribe({
next: () => {
this.toast.showInfo('POLICY.LOGIN_POLICY.PROVIDER_ADDED', true);
emitActivated$.next(true);
},
error: (error) => this.toast.showError(error),
});
}
}

View File

@ -5,11 +5,25 @@
<div class="identity-provider-create-content">
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/oauth.svg" alt="oauth" />
<h1>{{ 'IDP.CREATE.OAUTH.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.OAUTH.DESCRIPTION' | translate }}</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
@ -107,8 +121,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGenericOAuthProviderRequest as AdminAddGenericOAuthProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-oauth',
@ -33,9 +34,12 @@ export class ProviderOAuthComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
public updateClientSecret: boolean = false;
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public form!: UntypedFormGroup;
@ -44,6 +48,24 @@ export class ProviderOAuthComponent {
public provider?: Provider.AsObject;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
@ -51,6 +73,7 @@ export class ProviderOAuthComponent {
private injector: Injector,
private _location: Location,
breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new UntypedFormGroup({
name: new UntypedFormControl('', [requiredValidator]),
@ -114,6 +137,10 @@ export class ProviderOAuthComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
this.loading = true;
const req =
@ -138,7 +165,7 @@ export class ProviderOAuthComponent {
}
public submitForm(): void {
this.provider ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
this.provider || this.justCreated$.value ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
}
public addGenericOAuthProvider(): void {
@ -160,11 +187,9 @@ export class ProviderOAuthComponent {
this.loading = true;
this.service
.addGenericOAuthProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -173,12 +198,12 @@ export class ProviderOAuthComponent {
}
public updateGenericOAuthProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGenericOAuthProviderRequest()
: new AdminUpdateGenericOAuthProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setAuthorizationEndpoint(this.authorizationEndpoint?.value);
req.setIdAttribute(this.idAttribute?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<mat-icon class="idp-logo" svgIcon="mdi_openid" alt="openid" />
<h1>{{ 'IDP.CREATE.OIDC.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="(exists$ | async) === false"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'OIDC' }"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'OIDC' }"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/google#google-configuration"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.OIDC.DESCRIPTION' | translate }}</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
@ -95,8 +115,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import { BehaviorSubject, take } from 'rxjs';
import {
AddGenericOIDCProviderRequest as AdminAddGenericOIDCProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
@ -23,6 +23,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-oidc',
@ -33,9 +34,12 @@ export class ProviderOIDCComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: use id$ instead
public id: string | null = '';
public updateClientSecret: boolean = false;
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public form!: UntypedFormGroup;
@ -44,12 +48,32 @@ export class ProviderOIDCComponent {
public provider?: Provider.AsObject;
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.nextSvc.callbackUrls();
constructor(
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
private _location: Location,
breadcrumbService: BreadcrumbService,
private nextSvc: ProviderNextService,
) {
this.form = new UntypedFormGroup({
name: new UntypedFormControl('', [requiredValidator]),
@ -94,6 +118,10 @@ export class ProviderOIDCComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
private getData(id: string): void {
this.loading = true;
const req =
@ -118,7 +146,7 @@ export class ProviderOIDCComponent {
}
public submitForm(): void {
this.provider ? this.updateGenericOIDCProvider() : this.addGenericOIDCProvider();
this.provider || this.justCreated$.value ? this.updateGenericOIDCProvider() : this.addGenericOIDCProvider();
}
public addGenericOIDCProvider(): void {
@ -138,11 +166,9 @@ export class ProviderOIDCComponent {
this.loading = true;
this.service
.addGenericOIDCProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -151,12 +177,12 @@ export class ProviderOIDCComponent {
}
public updateGenericOIDCProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateGenericOIDCProviderRequest()
: new AdminUpdateGenericOIDCProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value);

View File

@ -6,9 +6,29 @@
<div class="title-row">
<img class="idp-logo" src="./assets/images/idp/saml-icon.svg" alt="saml" />
<h1>{{ 'IDP.CREATE.SAML.TITLE' | translate }}</h1>
<ng-container *ngIf="exists$ | async">
<div
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
class="cnsl-state-dot"
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
></div>
</ng-container>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<cnsl-provider-next
[configureProvider]="exists$ | async"
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.SAML.TITLE' | translate"
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.SAML.DESCRIPTION' | translate"
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml"
[autofillLink]="autofillLink$ | async"
[activateLink]="activateLink$ | async"
[copyUrls]="copyUrls$ | async"
[expanded]="!!(expandWhatNow$ | async)"
(activate)="activate()"
></cnsl-provider-next>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.SAML.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
@ -55,7 +75,6 @@
(optionsChanged)="options = $event"
></cnsl-provider-options>
</div>
<div class="identity-provider-create-actions">
<button
color="primary"
@ -64,8 +83,9 @@
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
}}</span>
</button>
</div>
</form>

View File

@ -7,7 +7,7 @@ import { ManagementService } from '../../../services/mgmt.service';
import { AdminService } from '../../../services/admin.service';
import { ToastService } from '../../../services/toast.service';
import { GrpcAuthService } from '../../../services/grpc-auth.service';
import { take } from 'rxjs';
import { BehaviorSubject, shareReplay, switchMap, take } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from '../../../services/breadcrumb.service';
import { atLeastOneIsFilled, requiredValidator } from '../../form-field/validators/validators';
@ -21,6 +21,9 @@ import {
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
UpdateSAMLProviderRequest as MgmtUpdateSAMLProviderRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { Environment, EnvironmentService } from '../../../services/environment.service';
import { filter, map } from 'rxjs/operators';
import { ProviderNextService } from '../provider-next/provider-next.service';
@Component({
selector: 'cnsl-provider-saml-sp',
@ -28,17 +31,67 @@ import {
styleUrls: ['./provider-saml-sp.component.scss'],
})
export class ProviderSamlSpComponent {
// DEPRECATED: use id$ instead
public id: string | null = '';
public loading: boolean = false;
public provider?: Provider.AsObject;
public form!: FormGroup;
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
// DEPRECATED: assert service$ instead
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
// DEPRECATED: use service$ instead
private service!: ManagementService | AdminService;
bindingValues: string[] = Object.keys(SAMLBinding);
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
public justActivated$ = new BehaviorSubject<boolean>(false);
private service$ = this.nextSvc.service(this.route.data);
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
public exists$ = this.nextSvc.exists(this.id$);
public autofillLink$ = this.nextSvc.autofillLink(
this.id$,
`https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml#optional-add-zitadel-action-to-autofill-userdata`,
);
public activateLink$ = this.nextSvc.activateLink(
this.id$,
this.justActivated$,
'https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml#activate-idp',
this.service$,
);
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
public copyUrls$ = this.id$.pipe(
filter((id) => !!id),
switchMap((id) =>
this.envSvc.env.pipe(
map((environment: Environment) => {
const idpBase = `${environment.issuer}/idps/${id}/saml`;
return [
{
label: 'ZITADEL Metadata',
url: `${idpBase}/metadata`,
downloadable: true,
},
{
label: 'ZITADEL ACS Login Form',
url: `${environment.issuer}/ui/login/login/externalidp/saml/acs`,
},
{
label: 'ZITADEL ACS Intent API',
url: `${idpBase}/acs`,
},
{
label: 'ZITADEL Single Logout',
url: `${idpBase}/slo`,
},
];
}),
),
),
shareReplay(1),
);
constructor(
private _location: Location,
private toast: ToastService,
@ -46,6 +99,8 @@ export class ProviderSamlSpComponent {
private route: ActivatedRoute,
private injector: Injector,
private breadcrumbService: BreadcrumbService,
private envSvc: EnvironmentService,
private nextSvc: ProviderNextService,
) {
this._buildBreadcrumbs();
this._initializeForm();
@ -117,14 +172,18 @@ export class ProviderSamlSpComponent {
});
}
public activate() {
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
}
public updateSAMLProvider(): void {
if (this.provider) {
if (this.provider || this.justCreated$.value) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateSAMLProviderRequest()
: new AdminUpdateSAMLProviderRequest();
req.setId(this.provider.id);
req.setId(this.provider?.id || this.justCreated$.value);
req.setName(this.name?.value);
if (this.metadataXml?.value) {
req.setMetadataXml(this.metadataXml?.value);
@ -170,11 +229,9 @@ export class ProviderSamlSpComponent {
this.loading = true;
this.service
.addSAMLProvider(req)
.then(() => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
.then((addedIDP) => {
this.justCreated$.next(addedIDP.id);
this.loading = false;
})
.catch((error) => {
this.toast.showError(error);
@ -183,7 +240,7 @@ export class ProviderSamlSpComponent {
}
public submitForm(): void {
this.provider ? this.updateSAMLProvider() : this.addSAMLProvider();
this.provider || this.justCreated$.value ? this.updateSAMLProvider() : this.addSAMLProvider();
}
private getData(id: string): void {

View File

@ -30,6 +30,9 @@ import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.componen
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
import { ProvidersRoutingModule } from './providers-routing.module';
import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.component';
import { CopyRowComponent } from '../../components/copy-row/copy-row.component';
import { ProviderNextComponent } from './provider-next/provider-next.component';
import { ProviderNextService } from './provider-next/provider-next.service';
@NgModule({
declarations: [
@ -47,6 +50,7 @@ import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.com
ProviderLDAPComponent,
ProviderAppleComponent,
ProviderSamlSpComponent,
ProviderNextComponent,
],
imports: [
ProvidersRoutingModule,
@ -67,6 +71,8 @@ import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.com
TranslateModule,
ProviderOptionsModule,
MatProgressSpinnerModule,
CopyRowComponent,
],
providers: [ProviderNextService],
})
export default class ProvidersModule {}

View File

@ -2,6 +2,8 @@
$is-dark-theme: map-get($theme, is-dark);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
.identity-provider-desc {
font-size: 14px;
@ -192,3 +194,18 @@
}
}
}
.cnsl-state-dot {
height: 8px;
width: 8px;
border-radius: 50%;
flex-shrink: 0;
&.active {
background-color: var(--success);
}
&.inactive {
background-color: var(--warn);
}
}

View File

@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs';
@ -41,7 +41,7 @@ import { EnvironmentService } from 'src/app/services/environment.service';
templateUrl: './instance.component.html',
styleUrls: ['./instance.component.scss'],
})
export class InstanceComponent {
export class InstanceComponent implements OnInit, OnDestroy {
public instance?: InstanceDetail.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

View File

@ -420,116 +420,52 @@
</div>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'urls' && !app?.samlConfig">
<cnsl-card title=" {{ 'APP.URLS' | translate }}" *ngIf="environmentMap$ | async as environmentMap">
<cnsl-info-section *ngIf="environmentMap.issuer">
<ng-container *ngIf="currentSetting === 'urls'">
<cnsl-card title=" {{ 'APP.URLS' | translate }}">
<cnsl-info-section *ngIf="(apiMap$ | async)?.issuer as issuer">
<div
class="link"
[innerHtml]="
'APP.OIDC.WELLKNOWN' | translate: { url: environmentMap['issuer'] + '/.well-known/openid-configuration' }
"
[innerHtml]="'APP.OIDC.WELLKNOWN' | translate: { url: issuer + '/.well-known/openid-configuration' }"
></div>
</cnsl-info-section>
<div class="app-info-row">
<div class="app-info-wrapper" *ngFor="let environmentV of environmentMap | keyvalue">
<p class="app-info-row-title cnsl-secondary-text">{{ environmentV.key }}</p>
<div class="app-copy-row">
<div *ngIf="environmentV.value" class="environment">
<button
[disabled]="copied === environmentV.value"
[matTooltip]="(copied !== environmentV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard
[valueToCopy]="environmentV.value"
(copiedValue)="copied = environmentV.key"
>
{{ environmentV.value }}
</button>
</div>
</div>
</div>
</div>
<div class="app-info-row">
<div class="app-info-wrapper" *ngFor="let wellKnownV of wellKnownMap$ | async | keyvalue">
<ng-container *ngIf="wellKnownV.key.endsWith('endpoint') || wellKnownV.key.toString() === 'jwks_uri'">
<p class="app-info-row-title cnsl-secondary-text">{{ wellKnownV.key }}</p>
<div class="app-copy-row">
<div *ngIf="wellKnownV.value" class="environment">
<button
[disabled]="copied === wellKnownV.value"
[matTooltip]="(copied !== wellKnownV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard
[valueToCopy]="wellKnownV.value"
(copiedValue)="copied = wellKnownV.key"
>
{{ wellKnownV.value }}
</button>
</div>
</div></ng-container
>
</div>
</div>
</cnsl-card>
</ng-container>
<ng-container *ngIf="currentSetting === 'urls' && samlForm && app?.samlConfig">
<cnsl-card title=" {{ 'APP.URLS' | translate }}" *ngIf="environmentMap$ | async as environmentMap">
<div class="app-info-row">
<div class="app-info-wrapper" *ngIf="environmentMap['samlCertificateURL']">
<p class="app-info-row-title cnsl-secondary-text">{{ 'APP.SAML.CERTIFICATE' | translate }}</p>
<div class="app-copy-row">
<div class="environment">
<button
[disabled]="copied === environmentMap['samlCertificateURL']"
[matTooltip]="
(copied !== environmentMap['samlCertificateURL'] ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate
"
cnslCopyToClipboard
[valueToCopy]="environmentMap['samlCertificateURL']"
(copiedValue)="copied = environmentMap['samlCertificateURL']"
>
{{ environmentMap['samlCertificateURL'] }}
</button>
</div>
</div>
</div>
<div class="app-info-wrapper" *ngIf="environmentMap['samlSSO']">
<p class="app-info-row-title cnsl-secondary-text">Single Sign On (SSO)</p>
<div class="app-copy-row">
<div class="environment">
<button
[disabled]="copied === environmentMap['samlSSO']"
[matTooltip]="(copied !== environmentMap['samlSSO'] ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard
[valueToCopy]="environmentMap['samlSSO']"
(copiedValue)="copied = environmentMap['samlSSO']"
>
{{ environmentMap['samlSSO'] }}
</button>
</div>
</div>
</div>
<div class="app-info-wrapper" *ngIf="environmentMap['samlSLO']">
<p class="app-info-row-title cnsl-secondary-text">Single Logoout (SLO)</p>
<div class="app-copy-row">
<div class="environment">
<button
[disabled]="copied === environmentMap['samlSLO']"
[matTooltip]="(copied !== environmentMap['samlSLO'] ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard
[valueToCopy]="environmentMap['samlSLO']"
(copiedValue)="copied = environmentMap['samlSLO']"
>
{{ environmentMap['samlSLO'] }}
</button>
</div>
</div>
</div>
</div>
<cnsl-copy-row
labelMinWidth="220px"
*ngFor="let apiUrl of apiMap$ | async | keyvalue"
[label]="apiUrl.key"
[value]="apiUrl.value"
></cnsl-copy-row>
<ng-container *ngIf="app?.samlConfig && samlMap$ | async as samlMap">
<cnsl-copy-row
labelMinWidth="220px"
*ngIf="samlMap['samlCertificateURL'] as url"
[label]="'APP.SAML.CERTIFICATE' | translate"
[value]="url"
>
<a class="download-button" mat-stroked-button [href]="url" download>{{ 'ACTIONS.DOWNLOAD' | translate }}</a>
</cnsl-copy-row>
<cnsl-copy-row
labelMinWidth="220px"
*ngIf="samlMap['samlSSO'] as url"
label="Single Sign On (SSO)"
[value]="url"
></cnsl-copy-row>
<cnsl-copy-row
labelMinWidth="220px"
*ngIf="samlMap['samlSLO'] as url"
label="Single Logout (SLO)"
[value]="url"
></cnsl-copy-row>
</ng-container>
<ng-container *ngIf="!app?.samlConfig">
<ng-container *ngFor="let wellknown of wellknownMap$ | async | keyvalue">
<cnsl-copy-row
labelMinWidth="220px"
*ngIf="wellknown.key.endsWith('endpoint') || wellknown.key.toString() === 'jwks_uri'"
[label]="wellknown.key"
[value]="wellknown.value"
></cnsl-copy-row>
</ng-container>
</ng-container>
</cnsl-card>
</ng-container>

View File

@ -93,7 +93,26 @@ export class AppDetailComponent implements OnInit, OnDestroy {
};
}),
);
public wellKnownMap$ = this.envSvc.wellKnown;
public apiMap$ = this.envSvc.env.pipe(
map((env) => {
return {
issuer: env.issuer,
adminServiceUrl: `${env.api}/admin/v1`,
mgmtServiceUrl: `${env.api}/management/v1`,
authServiceUrl: `${env.api}/auth/v1`,
};
}),
);
public samlMap$ = this.envSvc.env.pipe(
map((env) => {
return {
samlCertificateURL: `${env.issuer}/saml/v2/certificate`,
samlSSO: `${env.issuer}/saml/v2/SSO`,
samlSLO: `${env.issuer}/saml/v2/SLO`,
};
}),
);
public wellknownMap$ = this.envSvc.wellknown;
public oidcResponseTypes: OIDCResponseType[] = [
OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,

View File

@ -46,6 +46,7 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
import { IntegrateAppComponent } from './integrate/integrate.component';
import { OIDCConfigurationComponent } from 'src/app/components/oidc-configuration/oidc-configuration.component';
import { FrameworkChangeComponent } from 'src/app/components/framework-change/framework-change.component';
import { CopyRowComponent } from '../../../components/copy-row/copy-row.component';
@NgModule({
declarations: [
@ -99,6 +100,7 @@ import { FrameworkChangeComponent } from 'src/app/components/framework-change/fr
CodemirrorModule,
ChangesModule,
InfoSectionModule,
CopyRowComponent,
],
exports: [TranslateModule],
})

View File

@ -6,10 +6,9 @@
<mat-icon class="icon">info_outline</mat-icon>
</a>
</div>
<p
class="sub cnsl-secondary-text max-width-description"
[innerHTML]="'DESCRIPTIONS.PROJECTS.DESCRIPTION' | translate"
></p>
<p class="sub cnsl-secondary-text max-width-description">
{{ 'DESCRIPTIONS.PROJECTS.DESCRIPTION' | translate }}
</p>
<div class="projects-controls">
<div class="project-toggle-group">
<cnsl-nav-toggle

View File

@ -0,0 +1,103 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, from, map, Observable, of, switchMap } from 'rxjs';
import { ManagementService } from './mgmt.service';
import { AddCustomLoginPolicyRequest, AddCustomLoginPolicyResponse } from '../proto/generated/zitadel/management_pb';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { IDPOwnerType, Provider } from '../proto/generated/zitadel/idp_pb';
import { AdminService } from './admin.service';
import { catchError } from 'rxjs/operators';
import { LoginPolicy } from '../proto/generated/zitadel/policy_pb';
@Injectable({
providedIn: 'root',
})
export class ActivateIdpService {
constructor() {}
public activateIdp(
service: AdminService | ManagementService,
id: string,
owner?: IDPOwnerType,
policy?: LoginPolicy.AsObject,
): Observable<any> {
const isAdmin = service instanceof AdminService;
if (isAdmin) {
service.addIDPToLoginPolicy(id);
}
if (!isAdmin && owner !== IDPOwnerType.IDP_OWNER_TYPE_SYSTEM && owner !== IDPOwnerType.IDP_OWNER_TYPE_ORG) {
throw new Error('Must specify owner for management service');
}
return from(service.addIDPToLoginPolicy(id!, owner!)).pipe(
catchError((error) => {
if (isAdmin || error.code != 5) {
throw error;
}
// No org policy was found, so we create a new one
return from(policy ? of(policy) : from(service.getLoginPolicy()).pipe(map((policy) => policy.policy))).pipe(
switchMap((policy) => {
if (!policy?.isDefault) {
// There is already an org policy
throw error;
}
return from(this.addLoginPolicy(service, policy)).pipe(
switchMap(() => from(service.addIDPToLoginPolicy(id!, owner!))),
);
}),
);
}),
);
}
public addLoginPolicy(
service: ManagementService,
policy: LoginPolicy.AsObject,
): Promise<AddCustomLoginPolicyResponse.AsObject> {
const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(policy.allowExternalIdp);
mgmtreq.setAllowRegister(policy.allowRegister);
mgmtreq.setAllowUsernamePassword(policy.allowUsernamePassword);
mgmtreq.setForceMfa(policy.forceMfa);
mgmtreq.setPasswordlessType(policy.passwordlessType);
mgmtreq.setHidePasswordReset(policy.hidePasswordReset);
mgmtreq.setMultiFactorsList(policy.multiFactorsList);
mgmtreq.setSecondFactorsList(policy.secondFactorsList);
const pcl = new Duration()
.setSeconds(policy.passwordCheckLifetime?.seconds ?? 0)
.setNanos(policy.passwordCheckLifetime?.nanos ?? 0);
mgmtreq.setPasswordCheckLifetime(pcl);
const elcl = new Duration()
.setSeconds(policy.externalLoginCheckLifetime?.seconds ?? 0)
.setNanos(policy.externalLoginCheckLifetime?.nanos ?? 0);
mgmtreq.setExternalLoginCheckLifetime(elcl);
const misl = new Duration()
.setSeconds(policy.mfaInitSkipLifetime?.seconds ?? 0)
.setNanos(policy.mfaInitSkipLifetime?.nanos ?? 0);
mgmtreq.setMfaInitSkipLifetime(misl);
const sfcl = new Duration()
.setSeconds(policy.secondFactorCheckLifetime?.seconds ?? 0)
.setNanos(policy.secondFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setSecondFactorCheckLifetime(sfcl);
const mficl = new Duration()
.setSeconds(policy.multiFactorCheckLifetime?.seconds ?? 0)
.setNanos(policy.multiFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setMultiFactorCheckLifetime(mficl);
mgmtreq.setAllowDomainDiscovery(policy.allowDomainDiscovery);
mgmtreq.setIgnoreUnknownUsernames(policy.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(policy.defaultRedirectUri);
mgmtreq.setDisableLoginWithEmail(policy.disableLoginWithEmail);
mgmtreq.setDisableLoginWithPhone(policy.disableLoginWithPhone);
mgmtreq.setForceMfaLocalOnly(policy.forceMfaLocalOnly);
return service.addCustomLoginPolicy(mgmtreq);
}
}

View File

@ -34,14 +34,14 @@ export class EnvironmentService {
public admin!: AdminServiceClient;
private environment$: Observable<Environment>;
private wellKnown$: Observable<WellKnown>;
private wellknown$: Observable<WellKnown>;
constructor(
private http: HttpClient,
private exhaustedSvc: ExhaustedService,
) {
this.environment$ = this.createEnvironment();
this.wellKnown$ = this.createWellKnown(this.environment$);
this.wellknown$ = this.createWellKnown(this.environment$);
}
// env returns an `Observable<Environment>` that can be subscribed to whenever needed.
@ -51,10 +51,10 @@ export class EnvironmentService {
return this.environment$;
}
// wellKnown returns an `Observable<Environment>` that can be subscribed to whenever needed.
// wellknown returns an `Observable<Environment>` that can be subscribed to whenever needed.
// It makes the HTTP call exactly once and replays the cached result.
get wellKnown() {
return this.wellKnown$;
get wellknown() {
return this.wellknown$;
}
private createEnvironment() {

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Доставчици на идентичност",
"DESCRIPTION": "Създайте и активирайте външни доставчици на идентичност. Изберете известен доставчик или конфигурирайте друг OIDC, OAuth или SAML съвместим доставчик по ваш избор. Можете дори да използвате вашите съществуващи JWT токени като федерирани идентичности, като конфигурирате доставчик на идентичност с JWT."
"DESCRIPTION": "Създайте и активирайте външни доставчици на идентичност. Изберете известен доставчик или конфигурирайте друг OIDC, OAuth или SAML съвместим доставчик по ваш избор. Можете дори да използвате вашите съществуващи JWT токени като федерирани идентичности, като конфигурирате доставчик на идентичност с JWT.",
"NEXT": "Какво сега?",
"SAML": {
"TITLE": "Конфигурирайте вашия SAML доставчик на идентичност",
"DESCRIPTION": "ZITADEL е конфигуриран. Сега вашият SAML доставчик на идентичност се нуждае от конфигурация. Повечето доставчици ви позволяват просто да качите целия ZITADEL метаданни XML. Други доставчици ви молят да предоставите само някои отделни URL-и, като например entity ID (URL на метаданни), URL на услугата за потребление на твърдения (ACS) или URL за единично отписване."
},
"CALLBACK": {
"TITLE": "Конфигурирайте вашия {{ provider }} доставчик на идентичност",
"DESCRIPTION": "Преди да можете да конфигурирате ZITADEL, предайте този URL на вашия доставчик на идентичност, за да разрешите пренасочването на браузъра обратно към ZITADEL след аутентикация."
},
"JWT": {
"TITLE": "Използвайте JWT-и като федерирани идентичности",
"DESCRIPTION": "Доставчикът на идентичност JWT ви позволява да използвате вашите съществуващи JWT токени като федерирани идентичности. Тази функция е удобна, ако вече имате издател за JWT-и. С JWT IdP можете да използвате тези JWT-и за създаване и актуализиране на потребители в ZITADEL по лету."
},
"LDAP": {
"TITLE": "Конфигурирайте ZITADEL за връзка с вашия LDAP доставчик на идентичност",
"DESCRIPTION": "Предоставете детайлите за връзка с вашия LDAP сървър и конфигурирайте картографирането на вашите LDAP атрибути към атрибутите на ZITADEL."
},
"AUTOFILL": {
"TITLE": "Автоматично попълване на данни за потребителя",
"DESCRIPTION": "Използвайте действие за подобряване на потребителското изживяване. Можете да предварително попълните формуляра за регистрация на ZITADEL със стойности от доставчика на идентичност."
},
"ACTIVATE": {
"TITLE": "Активирайте IdP",
"DESCRIPTION": "Вашият IdP все още не е активен. Активирайте го, за да позволите на вашите потребители да влязат."
}
},
"PW_COMPLEXITY": {
"TITLE": "Сложност на паролата",
@ -460,7 +485,8 @@
"COMINGSOON": "Очаквайте скоро",
"TABLE": {
"SHOWUSER": "Покажи потребител {{value}}"
}
},
"DOWNLOAD": "Изтегляне"
},
"MEMBERROLES": {
"IAM_OWNER": "Има контрол върху цялата инстанция, включително всички организации",
@ -1499,7 +1525,8 @@
"DESCRIPTIONCREATEADMIN": "Потребителите могат да избират от наличните доставчици на идентичност по-долу.",
"DESCRIPTIONCREATEMGMT": "Потребителите могат да избират от наличните доставчици на идентичност по-долу. ",
"LIFETIME_INVALID": "Формулярът съдържа невалидни стойности.",
"SAVED": "Запазено успешно!"
"SAVED": "Запазено успешно!",
"PROVIDER_ADDED": "Доставчикът на идентичност е активиран."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Задайте вашите връзки за Политика за поверителност и Условия за ползване",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Poskytovatelé identity",
"DESCRIPTION": "Vytvořte a aktivujte externí poskytovatele identity. Vyberte známého poskytovatele nebo nakonfigurujte jakýkoliv jiný OIDC, OAuth nebo SAML kompatibilní poskytovatel podle vašeho výběru. Můžete dokonce použít vaše stávající JWT tokeny jako federované identity konfigurací JWT poskytovatele identity."
"DESCRIPTION": "Vytvořte a aktivujte externí poskytovatele identity. Vyberte známého poskytovatele nebo nakonfigurujte jakýkoliv jiný OIDC, OAuth nebo SAML kompatibilní poskytovatel podle vašeho výběru. Můžete dokonce použít vaše stávající JWT tokeny jako federované identity konfigurací JWT poskytovatele identity.",
"NEXT": "Co teď?",
"SAML": {
"TITLE": "Nakonfiguruj svého SAML Identity Provider",
"DESCRIPTION": "ZITADEL je nakonfigurován. Nyní je třeba nakonfigurovat tvého SAML Identity Provider. Většina poskytovatelů ti umožní nahrát celý ZITADEL metadata XML. Jiní poskytovatelé požadují, abys poskytl jen některé specifické URL, jako je například entity ID (metadata URL), URL Assertion Consumer Service (ACS) nebo Single Logout URL."
},
"CALLBACK": {
"TITLE": "Nakonfiguruj svého {{ provider }} Identity Provider",
"DESCRIPTION": "Před konfigurací ZITADEL, předej tuto URL svému Identity Provider, aby bylo možné povolit přesměrování prohlížeče zpět na ZITADEL po autentizaci."
},
"JWT": {
"TITLE": "Použij JWT jako federované identity",
"DESCRIPTION": "Identity Provider JWT ti umožňuje používat existující JWT tokeny jako federované identity. Tato funkce je praktická, pokud již máš vystavitele JWT. S JWT IdP můžeš tyto JWT použít k vytváření a aktualizaci uživatelů v ZITADEL na počkání."
},
"LDAP": {
"TITLE": "Nakonfiguruj ZITADEL pro připojení k tvému LDAP Identity Provider",
"DESCRIPTION": "Poskytni údaje pro připojení k tvému LDAP serveru a nakonfiguruj mapování tvých LDAP atributů na atributy ZITADEL."
},
"AUTOFILL": {
"TITLE": "Automatické vyplnění údajů uživatele",
"DESCRIPTION": "Vylepši zážitek svých uživatelů pomocí akce. Můžeš automaticky vyplnit registrační formulář ZITADEL hodnotami z identity provider."
},
"ACTIVATE": {
"TITLE": "Aktivuj IdP",
"DESCRIPTION": "Tvůj IdP ještě není aktivní. Aktivuj ho, aby tvoji uživatelé mohli přihlásit."
}
},
"PW_COMPLEXITY": {
"TITLE": "Složitost hesla",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Zobrazit uživatele {{value}}"
}
},
"DOWNLOAD": "Stáhnout"
},
"MEMBERROLES": {
"IAM_OWNER": "Má kontrolu nad celou instancí, včetně všech organizací",
@ -1506,7 +1532,8 @@
"DESCRIPTIONCREATEADMIN": "Uživatelé si mohou vybrat z dostupných poskytovatelů identity níže.",
"DESCRIPTIONCREATEMGMT": "Uživatelé si mohou vybrat z dostupných poskytovatelů identity níže. Poznámka: Můžete použít poskytovatele nastavené systémem, jakož i poskytovatele nastavené pouze pro vaši organizaci.",
"LIFETIME_INVALID": "Formulář obsahuje neplatné hodnoty.",
"SAVED": "Úspěšně uloženo!"
"SAVED": "Úspěšně uloženo!",
"PROVIDER_ADDED": "Poskytovatel identity aktivován."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Nastavte odkazy na vaše Zásady ochrany osobních údajů a Podmínky služby",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Identitätsanbieter",
"DESCRIPTION": "Erstelle und aktiviere externe Identitätsanbieter. Wähle einen bekannten Anbieter oder konfiguriere jeden anderen OIDC-, OAuth- oder SAML-kompatiblen Anbieter deiner Wahl. Du kannst sogar deine bestehenden JWT-Token als föderierte Identitäten verwenden, indem du einen JWT-Identitätsanbieter konfigurierst."
"DESCRIPTION": "Erstelle und aktiviere externe Identitätsanbieter. Wähle einen bekannten Anbieter aus oder konfiguriere einen anderen OIDC-, OAuth- oder SAML-kompatiblen Anbieter deiner Wahl. Du kannst sogar deine vorhandenen JWT-Tokens als föderierte Identitäten verwenden, indem du einen JWT-Identitätsanbieter konfigurierst.",
"NEXT": "Was nun?",
"SAML": {
"TITLE": "Konfiguriere deinen SAML-Identitätsanbieter",
"DESCRIPTION": "ZITADEL ist konfiguriert. Jetzt benötigt dein SAML-Identitätsanbieter eine Konfiguration. Die meisten Anbieter erlauben es dir, einfach die gesamte ZITADEL-Metadaten-XML hochzuladen. Andere Anbieter bitten dich, nur einige bestimmte URLs anzugeben, wie zum Beispiel die Entity-ID (Metadaten-URL), die Assertion Consumer Service (ACS)-URL oder die Single Logout-URL."
},
"CALLBACK": {
"TITLE": "Konfiguriere deinen {{ provider }}-Identitätsanbieter",
"DESCRIPTION": "Bevor du ZITADEL konfigurieren kannst, gib diese URL an deinen Identitätsanbieter weiter, um die Browserumleitung zurück zu ZITADEL nach der Authentifizierung zu ermöglichen."
},
"JWT": {
"TITLE": "Verwende JWTs als föderierte Identitäten",
"DESCRIPTION": "Der JWT-Identitätsanbieter ermöglicht es dir, deine vorhandenen JWT-Tokens als föderierte Identitäten zu nutzen. Diese Funktion ist praktisch, wenn du bereits einen Aussteller für JWTs hast. Mit einem JWT-IdP kannst du diese JWTs nutzen, um Benutzer in ZITADEL on-the-fly zu erstellen und zu aktualisieren."
},
"LDAP": {
"TITLE": "Konfiguriere ZITADEL, um eine Verbindung zu deinem LDAP-Identitätsanbieter herzustellen",
"DESCRIPTION": "Gib die Verbindungsdetails zu deinem LDAP-Server an und konfiguriere die Zuordnung deiner LDAP-Attribute zu ZITADEL-Attributen."
},
"AUTOFILL": {
"TITLE": "Benutzerdaten automatisch ausfüllen",
"DESCRIPTION": "Verwende eine Aktion, um das Benutzererlebnis zu verbessern. Du kannst das Registrierungsformular von ZITADEL mit Werten vom Identitätsanbieter vorab ausfüllen."
},
"ACTIVATE": {
"TITLE": "Aktiviere den IdP",
"DESCRIPTION": "Dein IdP ist noch nicht aktiv. Aktiviere ihn, um deinen Benutzern das Einloggen zu ermöglichen."
}
},
"PW_COMPLEXITY": {
"TITLE": "Passwortkomplexität",
@ -466,7 +491,8 @@
},
"TABLE": {
"SHOWUSER": "Zeige Benutzer {{value}}"
}
},
"DOWNLOAD": "Herunterladen"
},
"MEMBERROLES": {
"IAM_OWNER": "Hat die Kontrolle über die gesamte Instanz, einschließlich aller Organisationen",
@ -1505,7 +1531,8 @@
"DESCRIPTIONCREATEADMIN": "Nutzer können sich mit den verfügbaren Idps authentifizieren.",
"DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.",
"LIFETIME_INVALID": "Login Lifetimes sind ungültig.",
"SAVED": "Erfolgreich gespeichert."
"SAVED": "Erfolgreich gespeichert.",
"PROVIDER_ADDED": "Identitätsanbieter aktiviert."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Legen Sie Ihre Datenschutzrichtlinien und Nutzungsbedingungen fest",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Identity Providers",
"DESCRIPTION": "Create and activate external identity providers. Choose a well-known provider or configure any other OIDC, OAuth or SAML compatible provider of your choice. You can even use your existing JWT tokens as federated identities by configuring a JWT identity provider."
"DESCRIPTION": "Create and activate external identity providers. Choose a well-known provider or configure any other OIDC, OAuth or SAML compatible provider of your choice. You can even use your existing JWT tokens as federated identities by configuring a JWT identity provider.",
"NEXT": "What now?",
"SAML": {
"TITLE": "Configure your SAML Identity Provider",
"DESCRIPTION": "ZITADEL is configured. Now your SAML Identity Provider needs some configuration. Most providers allow you to just upload the whole ZITADEL metadata XML. Other providers ask you to provide some distinct URLs only, like for example the entity ID (metadata URL), the Assertion Consumer Service (ACS) URL or the Single Logout URL."
},
"CALLBACK": {
"TITLE": "Configure your {{ provider }} Identity Provider",
"DESCRIPTION": "Before you can configure ZITADEL, pass this URL to your Identity Provider to enable the browser redirection back to ZITADEL after authentication."
},
"JWT": {
"TITLE": "Use JWTs as federated Identities",
"DESCRIPTION": "The JWT Identity Provider enables you to use your existing JWT tokens as federated identities. This feature is handy if you already have an issuer for JWTs. With a JWT IdP, you can use these JWTs to create and update users in ZITADEL on-the-fly."
},
"LDAP": {
"TITLE": "Configure ZITADEL to connect to your LDAP Identity Provider",
"DESCRIPTION": "Provide the connection details to your LDAP server and configure the mapping of your LDAP attributes to ZITADEL attributes."
},
"AUTOFILL": {
"TITLE": "Autofill user data",
"DESCRIPTION": "Use an action to improve your users experience. You can pre-fill ZITADELs registration form with values from the identity provider."
},
"ACTIVATE": {
"TITLE": "Activate the IdP",
"DESCRIPTION": "Your IdP is not active yet. Activate it to allow your users to log in."
}
},
"PW_COMPLEXITY": {
"TITLE": "Password Complexity",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Show user {{value}}"
}
},
"DOWNLOAD": "Download"
},
"MEMBERROLES": {
"IAM_OWNER": "Has control over the whole instance, including all organizations",
@ -1506,7 +1532,8 @@
"DESCRIPTIONCREATEADMIN": "Users can choose from the available identity providers below.",
"DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organization only.",
"LIFETIME_INVALID": "Form contains invalid value(s).",
"SAVED": "Saved successfully!"
"SAVED": "Saved successfully!",
"PROVIDER_ADDED": "Identity provider activated."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Set your Privacy Policy and Terms of Service Links",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Proveedores de Identidad",
"DESCRIPTION": "Crea y activa proveedores de identidad externos. Elige un proveedor conocido o configura cualquier otro proveedor compatible con OIDC, OAuth o SAML de tu elección. Incluso puedes usar tus tokens JWT existentes como identidades federadas configurando un proveedor de identidad JWT."
"DESCRIPTION": "Crea y activa proveedores de identidad externos. Elige un proveedor conocido o configura cualquier otro proveedor compatible con OIDC, OAuth o SAML de tu elección. Incluso puedes usar tus tokens JWT existentes como identidades federadas configurando un proveedor de identidad JWT.",
"NEXT": "¿Qué ahora?",
"SAML": {
"TITLE": "Configura tu Proveedor de Identidad SAML",
"DESCRIPTION": "ZITADEL está configurado. Ahora tu Proveedor de Identidad SAML necesita algo de configuración. La mayoría de los proveedores te permiten subir todo el XML de metadatos de ZITADEL. Otros proveedores te piden que proporciones solo algunas URL distintas, como por ejemplo la ID de entidad (URL de metadatos), la URL del Servicio de Consumo de Aserción (ACS) o la URL de Cierre de Sesión Único."
},
"CALLBACK": {
"TITLE": "Configura tu Proveedor de Identidad {{ provider }}",
"DESCRIPTION": "Antes de que puedas configurar ZITADEL, pasa esta URL a tu Proveedor de Identidad para habilitar la redirección del navegador de vuelta a ZITADEL después de la autenticación."
},
"JWT": {
"TITLE": "Usa JWTs como identidades federadas",
"DESCRIPTION": "El Proveedor de Identidad JWT te permite usar tus tokens JWT existentes como identidades federadas. Esta característica es útil si ya tienes un emisor para JWTs. Con un IdP JWT, puedes usar estos JWTs para crear y actualizar usuarios en ZITADEL al vuelo."
},
"LDAP": {
"TITLE": "Configura ZITADEL para conectar con tu Proveedor de Identidad LDAP",
"DESCRIPTION": "Proporciona los detalles de conexión a tu servidor LDAP y configura el mapeo de tus atributos LDAP a los atributos de ZITADEL."
},
"AUTOFILL": {
"TITLE": "Autocompletar datos de usuario",
"DESCRIPTION": "Usa una acción para mejorar la experiencia de tus usuarios. Puedes pre-rellenar el formulario de registro de ZITADEL con valores del proveedor de identidad."
},
"ACTIVATE": {
"TITLE": "Activa el IdP",
"DESCRIPTION": "Tu IdP aún no está activo. Actívalo para permitir a tus usuarios iniciar sesión."
}
},
"PW_COMPLEXITY": {
"TITLE": "Complejidad de la Contraseña",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Mostrar usuario {{value}}"
}
},
"DOWNLOAD": "Descargar"
},
"MEMBERROLES": {
"IAM_OWNER": "Tiene control sobre toda la instancia, incluyendo todas las organizaciones",
@ -1507,7 +1533,8 @@
"DESCRIPTIONCREATEADMIN": "Los usuarios pueden elegir entre los siguientes proveedores de identidad disponibles.",
"DESCRIPTIONCREATEMGMT": "Los usuarios pueden elegir entre los siguientes proveedores de identidad. Nota: Puedes usar los proveedores integrados en el sistema así como sólo los proveedores configurados para tu organización.",
"LIFETIME_INVALID": "El formulario contiene valores no válidos.",
"SAVED": "¡Guardado con éxito!"
"SAVED": "¡Guardado con éxito!",
"PROVIDER_ADDED": "Proveedor de identidad activado."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Establece tu Política de privacidad y los enlaces a los Términos de Servicio (TDS)",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Fournisseurs d'Identité",
"DESCRIPTION": "Crée et active des fournisseurs d'identité externes. Choisis un fournisseur bien connu ou configure tout autre fournisseur compatible OIDC, OAuth ou SAML de ton choix. Tu peux même utiliser tes tokens JWT existants comme identités fédérées en configurant un fournisseur d'identité JWT."
"DESCRIPTION": "Crée et active des fournisseurs d'identité externes. Choisis un fournisseur bien connu ou configure tout autre fournisseur compatible OIDC, OAuth ou SAML de ton choix. Tu peux même utiliser tes tokens JWT existants comme identités fédérées en configurant un fournisseur d'identité JWT.",
"NEXT": "Et maintenant ?",
"SAML": {
"TITLE": "Configurez votre fournisseur d'identité SAML",
"DESCRIPTION": "ZITADEL est configuré. Maintenant, votre fournisseur d'identité SAML nécessite une configuration. La plupart des fournisseurs vous permettent de télécharger simplement l'intégralité du XML des métadonnées ZITADEL. D'autres fournisseurs vous demandent de fournir uniquement certains URL distincts, comme par exemple l'ID d'entité (URL des métadonnées), l'URL du service de consommation d'assertion (ACS) ou l'URL de déconnexion unique."
},
"CALLBACK": {
"TITLE": "Configurez votre fournisseur d'identité {{ provider }}",
"DESCRIPTION": "Avant de pouvoir configurer ZITADEL, passez cette URL à votre fournisseur d'identité pour permettre la redirection du navigateur vers ZITADEL après l'authentification."
},
"JWT": {
"TITLE": "Utilisez des JWT comme identités fédérées",
"DESCRIPTION": "Le fournisseur d'identité JWT vous permet d'utiliser vos jetons JWT existants comme identités fédérées. Cette fonctionnalité est pratique si vous avez déjà un émetteur de JWT. Avec un IdP JWT, vous pouvez utiliser ces JWT pour créer et mettre à jour des utilisateurs dans ZITADEL à la volée."
},
"LDAP": {
"TITLE": "Configurez ZITADEL pour se connecter à votre fournisseur d'identité LDAP",
"DESCRIPTION": "Fournissez les détails de connexion à votre serveur LDAP et configurez le mappage de vos attributs LDAP aux attributs ZITADEL."
},
"AUTOFILL": {
"TITLE": "Remplissage automatique des données utilisateur",
"DESCRIPTION": "Utilisez une action pour améliorer l'expérience de vos utilisateurs. Vous pouvez pré-remplir le formulaire d'inscription de ZITADEL avec des valeurs provenant du fournisseur d'identité."
},
"ACTIVATE": {
"TITLE": "Activez le fournisseur d'identité",
"DESCRIPTION": "Votre fournisseur d'identité n'est pas encore actif. Activez-le pour permettre à vos utilisateurs de se connecter."
}
},
"PW_COMPLEXITY": {
"TITLE": "Complexité du Mot de Passe",
@ -466,7 +491,8 @@
},
"TABLE": {
"SHOWUSER": "Afficher l'utilisateur{{value}}"
}
},
"DOWNLOAD": "Télécharger"
},
"MEMBERROLES": {
"IAM_OWNER": "A le contrôle de toute l'instance, y compris toutes les organisations",
@ -1505,7 +1531,8 @@
"DESCRIPTIONCREATEADMIN": "Les utilisateurs peuvent choisir parmi les fournisseurs d'identité disponibles ci-dessous.",
"DESCRIPTIONCREATEMGMT": "Les utilisateurs peuvent choisir parmi les fournisseurs d'identité disponibles ci-dessous. Note",
"LIFETIME_INVALID": "Le formulaire contient des valeurs non valides.",
"SAVED": "Enregistré avec succès !"
"SAVED": "Enregistré avec succès !",
"PROVIDER_ADDED": "Fournisseur d'identité activé."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Définissez vos liens vers la politique de confidentialité et les conditions de service",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Fornitori di Identità",
"DESCRIPTION": "Crea e attiva fornitori di identità esterni. Scegli un fornitore ben noto o configura qualsiasi altro fornitore compatibile con OIDC, OAuth o SAML della tua scelta. Puoi anche utilizzare i tuoi token JWT esistenti come identità federate configurando un fornitore di identità JWT."
"DESCRIPTION": "Crea e attiva fornitori di identità esterni. Scegli un fornitore ben noto o configura qualsiasi altro fornitore compatibile con OIDC, OAuth o SAML della tua scelta. Puoi anche utilizzare i tuoi token JWT esistenti come identità federate configurando un fornitore di identità JWT.",
"NEXT": "Cosa adesso?",
"SAML": {
"TITLE": "Configura il tuo Identity Provider SAML",
"DESCRIPTION": "ZITADEL è configurato. Ora è necessario configurare il tuo Identity Provider SAML. La maggior parte dei fornitori ti consente di caricare l'intero XML dei metadati di ZITADEL. Altri fornitori richiedono di fornire solo alcune URL distinte, come per esempio l'ID dell'entità (URL dei metadati), l'URL del Servizio di Consumo delle Asserzioni (ACS) o l'URL di Logout Singolo."
},
"CALLBACK": {
"TITLE": "Configura il tuo Identity Provider {{ provider }}",
"DESCRIPTION": "Prima di poter configurare ZITADEL, passa questa URL al tuo Identity Provider per abilitare il reindirizzamento del browser a ZITADEL dopo l'autenticazione."
},
"JWT": {
"TITLE": "Usa JWT come identità federate",
"DESCRIPTION": "Il Provider di Identità JWT ti permette di utilizzare i tuoi token JWT esistenti come identità federate. Questa funzionalità è utile se hai già un emittente per i JWT. Con un IdP JWT, puoi utilizzare questi JWT per creare e aggiornare utenti in ZITADEL al volo."
},
"LDAP": {
"TITLE": "Configura ZITADEL per connettersi al tuo Provider di Identità LDAP",
"DESCRIPTION": "Fornisci i dettagli della connessione al tuo server LDAP e configura il mapping dei tuoi attributi LDAP agli attributi di ZITADEL."
},
"AUTOFILL": {
"TITLE": "Compilazione automatica dei dati dell'utente",
"DESCRIPTION": "Usa un'azione per migliorare l'esperienza degli utenti. Puoi precompilare il modulo di registrazione di ZITADEL con i valori del provider di identità."
},
"ACTIVATE": {
"TITLE": "Attiva l'IdP",
"DESCRIPTION": "Il tuo IdP non è ancora attivo. Attivalo per permettere ai tuoi utenti di accedere."
}
},
"PW_COMPLEXITY": {
"TITLE": "Complessità della Password",
@ -465,7 +490,8 @@
},
"TABLE": {
"SHOWUSER": "Mostra utente {{value}}"
}
},
"DOWNLOAD": "Scarica"
},
"MEMBERROLES": {
"IAM_OWNER": "Ha il controllo sull'intera istanza, comprese tutte le organizzazioni",
@ -1505,7 +1531,8 @@
"DESCRIPTIONCREATEADMIN": "Gli utenti possono scegliere tra gli IDP disponibili qui sotto.",
"DESCRIPTIONCREATEMGMT": "Gli utenti possono scegliere tra gli IDP disponibili qui sotto. Nota: puoi usare i provider impostati nel sistema e quelli impostati della tua organizzazione.",
"LIFETIME_INVALID": "Login Lifetimes non sono validi",
"SAVED": "Salvato con successo!"
"SAVED": "Salvato con successo!",
"PROVIDER_ADDED": "Fornitore di identità attivato."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Imposta i tuoi link all'informativa sulla privacy e ai termini di servizio",

View File

@ -88,7 +88,33 @@
},
"IDPS": {
"TITLE": "IDプロバイダー",
"DESCRIPTION": "外部IDプロバイダーを作成してアクティブにします。よく知られたプロバイダーを選択するか、他のOIDC、OAuth、SAML互換プロバイダーを設定します。既存のJWTトークンを連合アイデンティティとして使用するために、JWT IDプロバイダーを設定することもできます。"
"DESCRIPTION": "外部IDプロバイダーを作成してアクティブにします。よく知られたプロバイダーを選択するか、他のOIDC、OAuth、SAML互換プロバイダーを設定します。既存のJWTトークンを連合アイデンティティとして使用するために、JWT IDプロバイダーを設定することもできます。",
"DESCRIPTION": "外部IDプロバイダーを作成して有効化します。よく知られているプロバイダーを選択するか、あるいはOIDC、OAuth、SAML互換の他のプロバイダーを自由に設定してください。既存のJWTトークンを、JWT IDプロバイダーを設定することで、フェデレーテッドアイデンティティとして使用することもできます。",
"NEXT": "次は?",
"SAML": {
"TITLE": "SAML IDプロバイダーを設定する",
"DESCRIPTION": "ZITADELは設定されました。次に、SAML IDプロバイダーの設定が必要です。ほとんどのプロバイダーでは、ZITADELのメタデータXML全体をアップロードするだけで済みます。他のプロバイダーでは、エンティティIDメタデータURL、Assertion Consumer Service (ACS) URL、またはシングルログアウトURLなど、特定のURLのみを提供するよう求められる場合があります。"
},
"CALLBACK": {
"TITLE": "{{ provider }} IDプロバイダーを設定する",
"DESCRIPTION": "ZITADELを設定する前に、このURLをIDプロバイダーに渡して、認証後にブラウザがZITADELにリダイレクトするようにしてください。"
},
"JWT": {
"TITLE": "JWTをフェデレーテッドアイデンティティとして使用する",
"DESCRIPTION": "JWT IDプロバイダーを使用すると、既存のJWTトークンをフェデレーテッドアイデンティティとして使用できます。JWTの発行者がすでにある場合に便利な機能です。JWT IdPを使用すると、これらのJWTを使用してZITADELでユーザーを即座に作成および更新できます。"
},
"LDAP": {
"TITLE": "LDAP IDプロバイダーに接続するためにZITADELを設定する",
"DESCRIPTION": "LDAPサーバーへの接続詳細を提供し、LDAP属性をZITADEL属性にマッピングする設定を行ってください。"
},
"AUTOFILL": {
"TITLE": "ユーザーデータの自動入力",
"DESCRIPTION": "アクションを使用してユーザーの体験を向上させます。IDプロバイダーからの値でZITADELの登録フォームを事前に入力することができます。"
},
"ACTIVATE": {
"TITLE": "IdPを有効にする",
"DESCRIPTION": "IdPはまだ有効ではありません。有効にして、ユーザーがログインできるようにしてください。"
}
},
"PW_COMPLEXITY": {
"TITLE": "パスワードの複雑さ",
@ -467,7 +493,8 @@
},
"TABLE": {
"SHOWUSER": "ユーザー {{value}} を表示する"
}
},
"DOWNLOAD": "ダウンロード"
},
"MEMBERROLES": {
"IAM_OWNER": "すべての組織を含むインスタンス全体を管理する権限を持ちます",
@ -1502,7 +1529,8 @@
"DESCRIPTIONCREATEADMIN": "ユーザーは、以下の利用可能なIDプロバイダーから選択できます。",
"DESCRIPTIONCREATEMGMT": "ユーザーは、以下の利用可能なIDプロバイダーから選択できます。注システムセットプロバイダーと、組織用に設定されたプロバイダーのみを使用できます。",
"LIFETIME_INVALID": "フォームに無効な値が含まれています。",
"SAVED": "正常に保存されました!"
"SAVED": "正常に保存されました!",
"PROVIDER_ADDED": "IDプロバイダーが有効化されました。"
},
"PRIVACY_POLICY": {
"DESCRIPTION": "プライバシーポリシーと利用規約リンクを設定します。",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Провајдери на идентитет",
"DESCRIPTION": "Креирајте и активирајте надворешни провајдери на идентитет. Изберете познат провајдер или конфигурирајте било кој друг OIDC, OAuth или SAML компатибилен провајдер по ваш избор. Можете дури и да ги користите вашите постоечки JWT токени како федеративни идентитети со конфигурирање на JWT провајдер на идентитет."
"DESCRIPTION": "Креирајте и активирајте надворешни провајдери на идентитет. Изберете познат провајдер или конфигурирајте било кој друг OIDC, OAuth или SAML компатибилен провајдер по ваш избор. Можете дури и да ги користите вашите постоечки JWT токени како федеративни идентитети со конфигурирање на JWT провајдер на идентитет.",
"NEXT": "Што сега?",
"SAML": {
"TITLE": "Конфигурирај го твојот SAML Identity Provider",
"DESCRIPTION": "ZITADEL е конфигуриран. Сега треба да се конфигурира твојот SAML Identity Provider. Повеќето провајдери ви овозможуваат да подигнете целиот ZITADEL metadata XML. Други провајдери бараат да обезбедите само некои посебни URL-а, како на пример entity ID (metadata URL), URL на Assertion Consumer Service (ACS) или URL за Single Logout."
},
"CALLBACK": {
"TITLE": "Конфигурирај го твојот {{ provider }} Identity Provider",
"DESCRIPTION": "Пред да можеш да го конфигурираш ZITADEL, проследи ја оваа URL на твојот Identity Provider за да овозможиш пренасочување на прелистувачот назад кон ZITADEL по аутентикација."
},
"JWT": {
"TITLE": "Користи JWT како федерирани идентитети",
"DESCRIPTION": "Identity Provider за JWT ти овозможува да ги користиш твоите постоечки JWT токени како федерирани идентитети. Оваа функција е корисна ако веќе имаш издавач за JWTs. Со JWT IdP, можеш да ги користиш овие JWT за да креираш и ажурираш корисници во ZITADEL во лет."
},
"LDAP": {
"TITLE": "Конфигурирај го ZITADEL за поврзување со твојот LDAP Identity Provider",
"DESCRIPTION": "Обезбеди детали за конекција до твојот LDAP сервер и конфигурирај го мапирањето на твоите LDAP атрибути кон атрибутите на ZITADEL."
},
"AUTOFILL": {
"TITLE": "Автоматско пополнување на податоци за корисникот",
"DESCRIPTION": "Користи акција за подобрување на искуството на твоите корисници. Можеш автоматски да го пополниш формуларот за регистрација на ZITADEL со вредности од identity provider."
},
"ACTIVATE": {
"TITLE": "Активирај го IdP",
"DESCRIPTION": "Твојот IdP сè уште не е активен. Активирај го за да овозможиш на твоите корисници да се најават."
}
},
"PW_COMPLEXITY": {
"TITLE": "Комплексност на лозинка",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Прикажи корисник {{value}}"
}
},
"DOWNLOAD": "Преземи"
},
"MEMBERROLES": {
"IAM_OWNER": "Има контрола врз целата инстанца, вклучувајќи ги сите организации",
@ -1507,7 +1533,8 @@
"DESCRIPTIONCREATEADMIN": "Корисниците можат да избираат од достапните IDPs подолу.",
"DESCRIPTIONCREATEMGMT": "Корисниците можат да избираат од достапните IDPs подолу. Забелешка: Можете да користите системски поставени IDPs, како и IDPs поставени само за вашата организација.",
"LIFETIME_INVALID": "Формуларот содржи неважечки вредности.",
"SAVED": "Успешно зачувано!"
"SAVED": "Успешно зачувано!",
"PROVIDER_ADDED": "Провајдерот на идентитет е активиран."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Поставете линкови кон вашите Политика за приватност и Услови за користење",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Identiteitsproviders",
"DESCRIPTION": "Maak en activeer externe identiteitsproviders. Kies een bekende provider of configureer een andere OIDC, OAuth of SAML compatibele provider naar keuze. Je kunt zelfs je bestaande JWT-tokens gebruiken als gefedereerde identiteiten door een JWT-identiteitsprovider te configureren."
"DESCRIPTION": "Maak en activeer externe identiteitsproviders. Kies een bekende provider of configureer een andere OIDC, OAuth of SAML compatibele provider naar keuze. Je kunt zelfs je bestaande JWT-tokens gebruiken als gefedereerde identiteiten door een JWT-identiteitsprovider te configureren.",
"NEXT": "Wat nu?",
"SAML": {
"TITLE": "Configureer je SAML-identiteitsprovider",
"DESCRIPTION": "ZITADEL is geconfigureerd. Nu heeft je SAML-identiteitsprovider enige configuratie nodig. De meeste providers staan je toe om gewoon de volledige ZITADEL-metadata XML te uploaden. Andere providers vragen je om enkele specifieke URL's te verstrekken, zoals bijvoorbeeld de entiteits-ID (metadata-URL), de Assertion Consumer Service (ACS) URL of de Single Logout URL."
},
"CALLBACK": {
"TITLE": "Configureer je {{ provider }}-identiteitsprovider",
"DESCRIPTION": "Voordat je ZITADEL kunt configureren, geef deze URL door aan je identiteitsprovider om de browseromleiding terug naar ZITADEL na authenticatie mogelijk te maken."
},
"JWT": {
"TITLE": "Gebruik JWT's als gefedereerde identiteiten",
"DESCRIPTION": "De JWT-identiteitsprovider stelt je in staat om je bestaande JWT-tokens te gebruiken als gefedereerde identiteiten. Deze functie is handig als je al een uitgever voor JWT's hebt. Met een JWT IdP kun je deze JWT's gebruiken om gebruikers in ZITADEL on-the-fly te creëren en bij te werken."
},
"LDAP": {
"TITLE": "Configureer ZITADEL om te verbinden met je LDAP-identiteitsprovider",
"DESCRIPTION": "Geef de verbindingsdetails voor je LDAP-server op en configureer de toewijzing van je LDAP-attributen aan ZITADEL-attributen."
},
"AUTOFILL": {
"TITLE": "Gebruikersgegevens automatisch invullen",
"DESCRIPTION": "Gebruik een actie om de ervaring van je gebruikers te verbeteren. Je kunt het registratieformulier van ZITADEL vooraf invullen met waarden van de identiteitsprovider."
},
"ACTIVATE": {
"TITLE": "Activeer de IdP",
"DESCRIPTION": "Je IdP is nog niet actief. Activeer het om je gebruikers in te laten loggen."
}
},
"PW_COMPLEXITY": {
"TITLE": "Wachtwoordcomplexiteit",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Toon gebruiker {{value}}"
}
},
"DOWNLOAD": "Download"
},
"MEMBERROLES": {
"IAM_OWNER": "Heeft controle over de hele instantie, inclusief alle organisaties",
@ -1506,7 +1532,8 @@
"DESCRIPTIONCREATEADMIN": "Gebruikers kunnen kiezen uit de beschikbare identiteitsproviders hieronder.",
"DESCRIPTIONCREATEMGMT": "Gebruikers kunnen kiezen uit de beschikbare identiteitsproviders hieronder. Opmerking: U kunt systeem ingestelde providers gebruiken evenals providers die alleen voor uw organisatie zijn ingesteld.",
"LIFETIME_INVALID": "Formulier bevat ongeldige waarde(n).",
"SAVED": "Succesvol opgeslagen!"
"SAVED": "Succesvol opgeslagen!",
"PROVIDER_ADDED": "Identiteitsprovider geactiveerd."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Stel uw Privacybeleid en Algemene Voorwaarden Links in",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Dostawcy tożsamości",
"DESCRIPTION": "Utwórz i aktywuj zewnętrznych dostawców tożsamości. Wybierz znanego dostawcę lub skonfiguruj dowolnego innego dostawcę zgodnego z OIDC, OAuth lub SAML według własnego wyboru. Możesz nawet użyć istniejących tokenów JWT jako tożsamości federacyjnych, konfigurując dostawcę tożsamości JWT."
"DESCRIPTION": "Utwórz i aktywuj zewnętrznych dostawców tożsamości. Wybierz znanego dostawcę lub skonfiguruj dowolnego innego dostawcę zgodnego z OIDC, OAuth lub SAML według własnego wyboru. Możesz nawet użyć istniejących tokenów JWT jako tożsamości federacyjnych, konfigurując dostawcę tożsamości JWT.",
"NEXT": "Co teraz?",
"SAML": {
"TITLE": "Skonfiguruj swojego dostawcę tożsamości SAML",
"DESCRIPTION": "ZITADEL jest skonfigurowany. Teraz potrzebna jest konfiguracja twojego dostawcy tożsamości SAML. Większość dostawców pozwala na załadowanie całego pliku metadanych ZITADEL XML. Inni dostawcy wymagają podania tylko niektórych konkretnych adresów URL, takich jak na przykład identyfikator podmiotu (URL metadanych), URL usługi odbiorczej asercji (ACS) lub URL wylogowania pojedynczego."
},
"CALLBACK": {
"TITLE": "Skonfiguruj swojego dostawcę tożsamości {{ provider }}",
"DESCRIPTION": "Zanim będziesz mógł skonfigurować ZITADEL, przekaż ten URL do swojego dostawcy tożsamości, aby umożliwić przekierowanie przeglądarki z powrotem do ZITADEL po uwierzytelnieniu."
},
"JWT": {
"TITLE": "Użyj JWT jako federacyjnych tożsamości",
"DESCRIPTION": "Dostawca tożsamości JWT umożliwia używanie istniejących tokenów JWT jako federacyjnych tożsamości. Ta funkcjonalność jest przydatna, jeśli masz już wydawcę tokenów JWT. Dzięki IdP JWT możesz używać tych JWT do tworzenia i aktualizowania użytkowników w ZITADEL na bieżąco."
},
"LDAP": {
"TITLE": "Skonfiguruj ZITADEL do połączenia z twoim dostawcą tożsamości LDAP",
"DESCRIPTION": "Podaj szczegóły połączenia z twoim serwerem LDAP i skonfiguruj mapowanie atrybutów LDAP do atrybutów ZITADEL."
},
"AUTOFILL": {
"TITLE": "Automatyczne wypełnianie danych użytkownika",
"DESCRIPTION": "Użyj akcji, aby poprawić doświadczenie użytkowników. Możesz wstępnie wypełnić formularz rejestracyjny ZITADEL wartościami z dostawcy tożsamości."
},
"ACTIVATE": {
"TITLE": "Aktywuj IdP",
"DESCRIPTION": "Twój IdP nie jest jeszcze aktywny. Aktywuj go, aby umożliwić użytkownikom logowanie."
}
},
"PW_COMPLEXITY": {
"TITLE": "Złożoność hasła",
@ -466,7 +491,8 @@
},
"TABLE": {
"SHOWUSER": "Pokaż użytkownika {{value}}"
}
},
"DOWNLOAD": "Pobierz"
},
"MEMBERROLES": {
"IAM_OWNER": "Ma kontrolę nad całą instancją, włącznie z wszystkimi organizacjami",
@ -1505,7 +1531,8 @@
"DESCRIPTIONCREATEADMIN": "Użytkownicy mogą wybrać z dostępnych poniżej dostawców tożsamości.",
"DESCRIPTIONCREATEMGMT": "Użytkownicy mogą wybrać z dostępnych poniżej dostawców tożsamości. Uwaga: Możesz korzystać z dostawców ustawionych przez system lub tylko dla twojej organizacji.",
"LIFETIME_INVALID": "Formularz zawiera nieprawidłowe wartości.",
"SAVED": "Pomyślnie zapisano!"
"SAVED": "Pomyślnie zapisano!",
"PROVIDER_ADDED": "Dostawca tożsamości aktywowany."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Ustaw swoje linki polityki prywatności i warunków korzystania",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Provedores de Identidade",
"DESCRIPTION": "Crie e ative provedores de identidade externos. Escolha um provedor conhecido ou configure qualquer outro provedor compatível com OIDC, OAuth ou SAML de sua escolha. Você pode até usar seus tokens JWT existentes como identidades federadas configurando um provedor de identidade JWT."
"DESCRIPTION": "Crie e ative provedores de identidade externos. Escolha um provedor conhecido ou configure qualquer outro provedor compatível com OIDC, OAuth ou SAML de sua escolha. Você pode até usar seus tokens JWT existentes como identidades federadas configurando um provedor de identidade JWT.",
"NEXT": "E agora?",
"SAML": {
"TITLE": "Configure seu Provedor de Identidade SAML",
"DESCRIPTION": "O ZITADEL está configurado. Agora, seu Provedor de Identidade SAML precisa de alguma configuração. A maioria dos provedores permite que você simplesmente faça o upload do XML de metadados do ZITADEL completo. Outros provedores pedem que você forneça apenas alguns URLs específicos, como por exemplo a ID da entidade (URL de metadados), a URL do Serviço de Consumidor de Afirmação (ACS) ou a URL de Logout Único."
},
"CALLBACK": {
"TITLE": "Configure seu {{ provider }} Provedor de Identidade",
"DESCRIPTION": "Antes de poder configurar o ZITADEL, passe este URL para o seu Provedor de Identidade para permitir o redirecionamento do navegador de volta para o ZITADEL após a autenticação."
},
"JWT": {
"TITLE": "Use JWTs como Identidades Federadas",
"DESCRIPTION": "O Provedor de Identidade JWT permite que você use seus tokens JWT existentes como identidades federadas. Este recurso é útil se você já tem um emissor para JWTs. Com um IdP JWT, você pode usar esses JWTs para criar e atualizar usuários no ZITADEL em tempo real."
},
"LDAP": {
"TITLE": "Configure o ZITADEL para conectar ao seu Provedor de Identidade LDAP",
"DESCRIPTION": "Forneça os detalhes de conexão para o seu servidor LDAP e configure o mapeamento dos seus atributos LDAP para atributos do ZITADEL."
},
"AUTOFILL": {
"TITLE": "Preenchimento automático de dados do usuário",
"DESCRIPTION": "Use uma ação para melhorar a experiência dos seus usuários. Você pode pré-preencher o formulário de registro do ZITADEL com valores do provedor de identidade."
},
"ACTIVATE": {
"TITLE": "Ative o IdP",
"DESCRIPTION": "Seu IdP ainda não está ativo. Ative-o para permitir que seus usuários façam login."
}
},
"PW_COMPLEXITY": {
"TITLE": "Complexidade da Senha",
@ -467,7 +492,8 @@
},
"TABLE": {
"SHOWUSER": "Mostrar usuário {{value}}"
}
},
"DOWNLOAD": "Baixar"
},
"MEMBERROLES": {
"IAM_OWNER": "Tem controle sobre toda a instância, incluindo todas as organizações",
@ -1507,7 +1533,8 @@
"DESCRIPTIONCREATEADMIN": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo.",
"DESCRIPTIONCREATEMGMT": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo. Observação: você também pode usar provedores definidos pelo sistema e provedores definidos apenas para sua organização.",
"LIFETIME_INVALID": "O formulário contém valores inválidos.",
"SAVED": "Salvo com sucesso!"
"SAVED": "Salvo com sucesso!",
"PROVIDER_ADDED": "Provedor de identidade ativado."
},
"PRIVACY_POLICY": {
"DESCRIPTION": "Defina os links para a sua Política de Privacidade e Termos de Serviço",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "Поставщики идентификации",
"DESCRIPTION": "Создайте и активируйте внешних поставщиков идентификации. Выберите известного поставщика или настройте любого другого совместимого с OIDC, OAuth или SAML поставщика по вашему выбору. Вы даже можете использовать ваши существующие JWT токены как федеративные идентификаторы, настроив поставщика идентификации JWT."
"DESCRIPTION": "Создайте и активируйте внешних поставщиков идентификации. Выберите известного поставщика или настройте любого другого совместимого с OIDC, OAuth или SAML поставщика по вашему выбору. Вы даже можете использовать ваши существующие JWT токены как федеративные идентификаторы, настроив поставщика идентификации JWT.",
"NEXT": "Что дальше?",
"SAML": {
"TITLE": "Настройте вашего провайдера идентификации SAML",
"DESCRIPTION": "ZITADEL настроен. Теперь вашему провайдеру идентификации SAML требуется настройка. Большинство провайдеров позволяют просто загрузить полный XML метаданных ZITADEL. Другие провайдеры просят предоставить только некоторые конкретные URL, например, ID сущности (URL метаданных), URL Службы Потребителя Утверждений (ACS) или URL Единого Выхода."
},
"CALLBACK": {
"TITLE": "Настройте вашего {{ provider }} провайдера идентификации",
"DESCRIPTION": "Прежде чем вы сможете настроить ZITADEL, передайте этот URL вашему провайдеру идентификации, чтобы разрешить перенаправление браузера обратно в ZITADEL после аутентификации."
},
"JWT": {
"TITLE": "Используйте JWT как федеративные идентификаторы",
"DESCRIPTION": "Провайдер идентификации JWT позволяет вам использовать ваши существующие JWT-токены как федеративные идентификаторы. Эта функция удобна, если у вас уже есть выпускающий центр для JWT. С помощью JWT IdP вы можете использовать эти JWT для создания и обновления пользователей в ZITADEL на лету."
},
"LDAP": {
"TITLE": "Настройте ZITADEL для подключения к вашему провайдеру идентификации LDAP",
"DESCRIPTION": "Укажите детали подключения к вашему серверу LDAP и настройте сопоставление ваших атрибутов LDAP с атрибутами ZITADEL."
},
"AUTOFILL": {
"TITLE": "Автозаполнение данных пользователя",
"DESCRIPTION": "Используйте действие для улучшения пользовательского опыта. Вы можете предварительно заполнить форму регистрации ZITADEL данными от провайдера идентификации."
},
"ACTIVATE": {
"TITLE": "Активируйте IdP",
"DESCRIPTION": "Ваш IdP еще не активирован. Активируйте его, чтобы ваши пользователи могли войти."
}
},
"PW_COMPLEXITY": {
"TITLE": "Сложность пароля",
@ -466,7 +491,8 @@
},
"TABLE": {
"SHOWUSER": "Показать пользователя {{value}}"
}
},
"DOWNLOAD": "Скачать"
},
"MEMBERROLES": {
"IAM_OWNER": "Имеет контроль над всем экземпляром, включая все организации",
@ -1562,7 +1588,8 @@
"ADVANCED": "Расширенный",
"LIFETIMEDURATIONS": "Срок действия логина",
"LIFETIME_INVALID": "Форма содержит недопустимые значения.",
"SAVED": "Успешно сохранено!"
"SAVED": "Успешно сохранено!",
"PROVIDER_ADDED": "Поставщик идентификации активирован."
},
"PRIVACY_POLICY": {
"TITLE": "Политика конфиденциальности и Пользовательское соглашение",

View File

@ -88,7 +88,32 @@
},
"IDPS": {
"TITLE": "身份提供商",
"DESCRIPTION": "创建并激活外部身份提供商。选择一个知名提供商或根据您的选择配置任何其他兼容OIDC、OAuth或SAML的提供商。您甚至可以通过配置JWT身份提供商使用您现有的JWT令牌作为联合身份。"
"DESCRIPTION": "创建并激活外部身份提供商。选择一个知名提供商或根据您的选择配置任何其他兼容OIDC、OAuth或SAML的提供商。您甚至可以通过配置JWT身份提供商使用您现有的JWT令牌作为联合身份。",
"NEXT": "下一步?",
"SAML": {
"TITLE": "配置你的SAML身份提供商",
"DESCRIPTION": "ZITADEL已配置。现在需要配置你的SAML身份提供商。大多数提供商允许你直接上传整个ZITADEL元数据XML。其他提供商可能仅要求你提供一些特定的URL例如实体ID元数据URL、断言消费服务ACSURL或单点登出URL。"
},
"CALLBACK": {
"TITLE": "配置你的{{ provider }}身份提供商",
"DESCRIPTION": "在你能配置ZITADEL之前将这个URL传递给你的身份提供商以便在认证后启用浏览器重定向回到ZITADEL。"
},
"JWT": {
"TITLE": "将JWT用作联合身份",
"DESCRIPTION": "JWT身份提供商使你能够将现有的JWT令牌用作联合身份。如果你已经有一个JWT发行者这个功能非常有用。使用JWT IdP你可以利用这些JWT即时创建和更新ZITADEL中的用户。"
},
"LDAP": {
"TITLE": "配置ZITADEL以连接到你的LDAP身份提供商",
"DESCRIPTION": "提供你的LDAP服务器的连接细节并配置你的LDAP属性到ZITADEL属性的映射。"
},
"AUTOFILL": {
"TITLE": "自动填充用户数据",
"DESCRIPTION": "使用一个操作来改善用户体验。你可以使用身份提供商的值预填充ZITADEL的注册表单。"
},
"ACTIVATE": {
"TITLE": "激活IdP",
"DESCRIPTION": "你的IdP尚未激活。激活它以允许你的用户登录。"
}
},
"PW_COMPLEXITY": {
"TITLE": "密码复杂度",
@ -466,7 +491,8 @@
},
"TABLE": {
"SHOWUSER": "Show user {{value}}"
}
},
"DOWNLOAD": "下载"
},
"MEMBERROLES": {
"IAM_OWNER": "控制整个实例,包括所有组织",
@ -1504,7 +1530,8 @@
"DESCRIPTIONCREATEADMIN": "用户可以从以下可用的身份提供者中进行选择。",
"DESCRIPTIONCREATEMGMT": "用户可以从以下可用的身份提供者中进行选择。注意:您可为系统设置提供者也可以仅为您的组织单独设置提供者。",
"LIFETIME_INVALID": "表单包含无效值。",
"SAVED": "保存成功!"
"SAVED": "保存成功!",
"PROVIDER_ADDED": "身份提供商已激活。"
},
"PRIVACY_POLICY": {
"DESCRIPTION": "设置您的隐私政策和服务条款链接",

View File

@ -73,6 +73,8 @@
@import 'src/app/modules/create-layout/create-layout.component.scss';
@import 'src/app/modules/domains/domain-verification/domain-verification.component.scss';
@import './styles/codemirror.scss';
@import 'src/app/components/copy-row/copy-row.component.scss';
@import 'src/app/modules/providers/provider-next/provider-next.component.scss';
@mixin component-themes($theme) {
@include cnsl-color-theme($theme);
@ -149,4 +151,6 @@
@include contact-theme($theme);
@include app-integrate-theme($theme);
@include domain-verification-theme($theme);
@include copy-row-theme($theme);
@include provider-next-theme($theme);
}

View File

@ -1,9 +1,6 @@
The login policy can be configured on two levels. Once in the default settings and this can be overwritten for each organization.
The only difference is where you configure it. Go either to the settings page of a specific organization or to the default settings page.
Default Settings: $YOUR-DOMAIN/ui/console/instance?id=general
Organization: Choose the organization in the menu and go to $YOUR-DOMAIN/ui/console/org-settings?id=login
1. Go to the Settings
- To allow external IdP logins by default, go to your instance default settings at `$YOUR-DOMAIN/ui/console/instance?id=general`
- To allow external IdP logins on an organization, go to `$YOUR-DOMAIN/ui/console/org-settings?id=login` and ensure you have the right org context.
2. Modify your login policy in the menu "Login Behavior and Security"
3. Enable the attribute "External Login allowed"

View File

@ -1,8 +0,0 @@
The Generic OIDC Provider allows you to configure any OIDC compliant identity providers.
The following information you need to fill out by yourself:
<li><b>Name</b> {props.name}</li>
<li><b>Issuer</b> {props.issuer}</li>
<li><b>Client-ID</b> {props.clientid}</li>
<li><b>Scopes</b>: (openid, profile, email is preconfigured)</li>

View File

@ -12,6 +12,15 @@ import TestSetup from './_test_setup.mdx';
<Intro provider="Apple"/>
## Open the Apple Identity Provider Template
<IDPsOverview templates="Apple"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it in the Apple service later.
![Apple Provider](/img/guides/zitadel_apple_create_provider.png)
## Apple Configuration
### Register a new App
@ -32,13 +41,10 @@ import TestSetup from './_test_setup.mdx';
6. Choose the previously created App in the Primary App ID List
7. Add your custom domain in the domains and subdomains field
- Example domain for `https://acme-gzoe4x.zitadel.cloud` would look like this: `acme-gzoe4x.zitadel.cloud`
8. Add the redirect uri in the Return URLs
- {your-domain}/ui/login/login/externalidp/callback/form
- Example redirect url for the domain `https://acme-gzoe4x.zitadel.cloud` would look like this: `https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback/form`
8. [Paste the ZITADEL Callback URL you copied before](#open-the-apple-identity-provider-template) to the Return URLs
![Apple Service](/img/guides/apple_service_create.png)
### Register a new key
1. Go to the keys list of your Apple Developer Account: [https://developer.apple.com/account/resources/authkeys/list](https://developer.apple.com/account/resources/authkeys/list)
@ -50,34 +56,29 @@ import TestSetup from './_test_setup.mdx';
## ZITADEL Configuration
### Add custom login policy
Go back [to the Apple provider template you opened before in ZITADEL](#open-the-apple-identity-provider-template).
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Apple"/>
### Create a new Apple Provider
1. Add the Client ID, this is the identifier of the service you created in your Apple Account
1. Add the Client ID, this is [the identifier of the service you created in your Apple Account](#register-a-new-service)
2. Fill the Team ID, you can find it when you login to your Apple Developer account, in your membership
3. Enter the Key ID and upload the Private Key you previously created
3. Enter the [Key ID and upload the Private Key you created before](#register-a-new-key)
You can configure the following settings if you like, a useful default will be filled if you don't change anything:
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `name` and `email` are prefilled. This information will be taken to create/update the user within ZITADEL.
<GeneralConfigDescription provider_account="Apple account" />
![Apple Provider](/img/guides/zitadel_apple_create_provider.png)
### Activate IdP
<Activate/>
![Activate the Apple Provider](/img/guides/zitadel_activate_apple.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your Apple login"/>

View File

@ -12,6 +12,15 @@ import Activate from './_activate.mdx';
<Intro provider="Entra ID (former Azure Active Directory)"/>
## Open the Microsoft Identity Provider Template
<IDPsOverview templates="Microsoft"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it in the Entra ID Client later.
![Azure Provider](/img/guides/zitadel_azure_provider2.png)
## Entra ID Configuration
You need to have access to an Entra ID Tenant. If you do not yet have one follow [this guide from Microsoft](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-create-new-tenant) to create one for free.
@ -20,10 +29,9 @@ You need to have access to an Entra ID Tenant. If you do not yet have one follow
1. Browse to the [App registration menus create dialog](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/CreateApplicationBlade/quickStartType~/null/isMSAApp~/false) to create a new app.
2. Give the application a name and choose who should be able to login (Single-Tenant, Multi-Tenant, Personal Accounts, etc.) This setting will also have an impact on how to configure the provider later on in ZITADEL.
3. Choose "Web" in the redirect uri field and add the URL:
- {your-domain}/ui/login/login/externalidp/callback
- Example redirect url for the domain `https://acme-gzoe4x.zitadel.cloud` would look like this: `https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
5. Save the Application (client) ID and the Directory (tenant) ID from the detail page
3. Choose "Web" in the redirect uri field and [paste the ZITADEL Callback URL you copied before](#open-the-microsoft-identity-provider-template).
4. Save the Application (client) ID and the Directory (tenant) ID from the detail page
5. Register the application
![Azure App Registration](/img/guides/azure_app_registration.png)
@ -61,20 +69,11 @@ To be able to get all the information that ZITADEL needs, you have to configure
## ZITADEL Configuration
### Add custom login policy
Go back [to the Microsoft provider template you opened before in ZITADEL](#open-the-microsoft-identity-provider-template).
Add the [client ID and secret created before on your Entra ID application](#add-client-secret).
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Microsoft"/>
### Create a new Entra ID Provider
The Microsoft template has everything you need preconfigured.
You only have to add the client ID and secret, you have created in the step before.
You can configure the following settings if you like, a useful default will be filled if you don't change anything:
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This information will be taken to create/update the user within ZITADEL. Make sure to also add `User.Read`. ZITADEL ensures that at least `openid` and `User.Read` scopes are always sent.
@ -94,14 +93,16 @@ In this case, configure "Accounts in any organizational directory and personal M
<GeneralConfigDescription provider_account="Microsoft account" />
![Azure Provider](/img/guides/zitadel_azure_provider2.png)
### Activate IdP
<Activate/>
![Activate Entra ID](/img/guides/zitadel_activate_azure.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your Microsoft login"/>

View File

@ -49,15 +49,11 @@ Configure the sign-on method of the app.
## ZITADEL Configuration
### Add custom login policy
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="SAML SP"/>
### Create a new SAML SP
### Create a new SAML Service Provider (SP)
Now we configure the identity provider on ZITADEL.
@ -72,24 +68,40 @@ Now we configure the identity provider on ZITADEL.
![Azure SAML App Creation](/img/guides/zitadel_azure_saml_provider.png)
## Configure Basic SAML Configuration
After you created the SAML SP in ZITADEL, you can copy the URLs you need to configure in your Entra ID application.
![Azure SAML App URLs](/img/guides/zitadel_azure_saml_provider_urls.png)
1. Go to Microsoft Entra > Manage > Single sign-on
2. Edit the "Basic SAML Configuration"
3. **Identifier (Entity ID)**: Paste the *ZITADEL Metadata URL*.
4. **Reply URL (Assertion Consumer Service URL)**: Paste the *ZITADEL ACS Login Form URL*
5. **Sign on URL**: Paste the *ZITADEL ACS Login Form URL*
5. **Logout URL**: Optionally paste the *ZITADEL Single Logout URL*
6. Click Save
:::info
You can ignore the ZITADEL ACS Intent API URL for now.
This is relevant if you want to [programmatically sign users in at ZITADEL via a SAML Service Provider](/guides/integrate/login-ui/external-login).
:::
![Azure Entra configuration overview](/img/guides/azure_saml_overview2.png)
## Enable the Microsoft Entra Button in ZITADELs Login Page
Go back to ZITADEL and activate the IdP.
### Activate IdP
<Activate/>
![Activate Azure SAML Provider](/img/guides/zitadel_activate_azure_saml.png)
## Configure Basic SAML Configuration
### Ensure your Login Policy allows External IDPs
Now we set the links in Microsoft Entra
1. Go to Microsoft Entra > Manage > Single sign-on
2. Edit the "Basic SAML Configuration"
3. Enter the following URL in "Identifier (Entity ID)": `<YOUR-ZITADEL-DOMAIN>/idps/<IDP-ID>/saml/metadata`
4. Enter the following URL in "Reply URL (Assertion Consumer Service URL)": `<YOUR-ZITADEL-DOMAIN>/ui/login/login/externalidp/saml/acs`
5. Enter the following URL in "Sign on URL": `<YOUR-ZITADEL-DOMAIN>/ui/login/login/externalidp/saml/acs`
6. Hit Save
![Azure Entra configuration overview](/img/guides/azure_saml_overview2.png)
<CustomLoginPolicy/>
## Test the setup

View File

@ -14,28 +14,29 @@ import PrefillAction from './_prefill_action.mdx';
<Intro provider="GitHub"/>
## Open the GitHub Identity Provider Template
<IDPsOverview templates="GitHub or GitHub Enterprise Server"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it to the GitHub OAuth application later.
![GitHub Provider](/img/guides/zitadel_github_create_provider.png)
## GitHub Configuration
### Register a new application
For **GitHub** browse to the [Register a new OAuth application](https://github.com/settings/applications/new). You can find this link withing [Settings](https://github.com/settings/profile) - [Developer Settings](https://github.com/settings/apps) - - [OAuth Apps](https://github.com/settings/developers).
For **GitHub Enterprise** go to your GitHub Enterprise home page and then to Settings - Developer Settings - OAuth Apps - Register a new application/New OAuth App
Fill in the application name and homepage URL.
You have to add the authorization callback URL, where GitHub should redirect, after the user has authenticated themselves.
In this example our test instance has the domain `https://acme-gzoe4x.zitadel.cloud`.
This results in the following authorization callback URL:
`https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
:::info
To adapt this for you setup just replace the domain
:::
1. Create a new OAuth application
- For **GitHub** browse to the [Register a new OAuth application](https://github.com/settings/applications/new). You can find this link withing [Settings](https://github.com/settings/profile) - [Developer Settings](https://github.com/settings/apps) - - [OAuth Apps](https://github.com/settings/developers).
- For **GitHub Enterprise** go to your GitHub Enterprise home page and then to Settings - Developer Settings - OAuth Apps - Register a new application/New OAuth App
2. Fill in the application name and homepage URL.
3. [Paste the ZITADEL Callback URL you copied before](#open-the-github-identity-provider-template) to the Authorization callback URL field.
4. Click "Register application".
![Register an OAuth application](/img/guides/github_oauth_app_registration.png)
### Client ID and Secret
### Client ID and secret
After clicking "Register application", you see the detail page of the application you have just created.
Copy the client ID directly from the detail page.
@ -46,33 +47,28 @@ Make sure to save the secret, as you will not be able to show it again.
## ZITADEL Configuration
### Add custom login policy
Go back [to the GitHub provider template you opened before in ZITADEL](#open-the-github-identity-provider-template).
Add the [client ID and secret you created before when you registered your GitHub OAuth application](#client-id-and-secret).
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="GitHub or GitHub Enterprise"/>
### Create a new GitHub Provider
The GitHub provider templates have everything you need preconfigured. You only have to add the client ID and secret, you have created in the step before.
You can configure the following settings if you like, a useful default will be filled if you don't change anything:
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This information is used to create and/or update the user within ZITADEL. ZITADEL ensures that at least the `openid`-scope is always sent.
This information is used to create and/or update the user within ZITADEL.
ZITADEL ensures that at least the `openid`-scope is always sent.
<GeneralConfigDescription provider_account="GitHub account" />
![GitHub Provider](/img/guides/zitadel_github_create_provider.png)
### Activate IdP
<Activate/>
![Activate the GitHub](/img/guides/zitadel_activate_github.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your GitHub login"/>

View File

@ -14,30 +14,28 @@ import PrefillAction from './_prefill_action.mdx';
<Intro provider="GitLab"/>
## Open the GitLab Identity Provider Template
<IDPsOverview templates="GitLab or GitLab Self Hosted"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it to the GitLab Application later.
![GitLab Provider](/img/guides/zitadel_gitlab_create_provider.png)
## GitLab Configuration
### Register a new application
1. Login to [gitlab.com](https://gitlab.com)
1. Login to [gitlab.com](https://gitlab.com) or to your GitLab self-hosted instance
2. Select [Edit Profile](https://gitlab.com/-/profile)
3. Click on [Applications](https://gitlab.com/-/profile/applications) in the side navigation
For **GitLab Self-Hosted** go to your GitLab self-hosted instance and follow the same steps as for GitLab.
Fill in the application name.
You have to add the redirect URI, where GitLab should redirect, after the user has authenticated.
In this example our test instance has the domain `https://acme-gzoe4x.zitadel.cloud`.
This results in the following redirect URI:
`https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
:::info
To adapt this for you setup just replace the domain
:::
4. Fill in the application name
5. [Paste the ZITADEL Callback URL you copied before](#open-the-gitlab-identity-provider-template) to the Redirect URI field.
![Register an OAuth application](/img/guides/gitlab_app_registration.png)
### Client ID and Secret
### Client ID and secret
After clicking "Save application", you will see the detail page of the application you have just created.
To be able to connect GitLab to ZITADEL you will need a client ID and a client secret.
@ -47,33 +45,28 @@ Save the ID and the Secret, you will not be able to copy the secret again, if yo
## ZITADEL Configuration
### Add custom login policy
Go back [to the GitLab provider template you opened before in ZITADEL](#open-the-gitlab-identity-provider-template).
Add the [client ID and secret you created before when you registered your GitLab application](#client-id-and-secret).
<CustomLoginPolicy/>
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
### Go to the IdP Providers Overview
<IDPsOverview templates="GitLab or GitLab Self Hosted"/>
### Create a new GitLab Provider
The GitLab provider templates have everything you need preconfigured.
Add the client ID and secret you have created in the Gitlab Application.
You can configure the following settings if you like, a useful default will be filled if you don't change anything:
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled. This informations will be taken to create/update the user within ZITADEL. ZITADEL ensures that at least the `openid`-scope is always sent.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This informations will be taken to create/update the user within ZITADEL.
ZITADEL ensures that at least the `openid`-scope is always sent.
<GeneralConfigDescription provider_account="GitLab account" />
![GitLab Provider](/img/guides/zitadel_gitlab_create_provider.png)
### Activate IdP
<Activate/>
![Activate the GitLab](/img/guides/zitadel_activate_gitlab.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your GitLab login"/>

View File

@ -12,6 +12,15 @@ import TestSetup from './_test_setup.mdx';
<Intro provider="Google"/>
## Open the Google Identity Provider Template
<IDPsOverview templates="Google"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it in the Google Cloud Platform later.
![Google Provider](/img/guides/zitadel_google_create_provider.png)
## Google Configuration
### Register a new client
@ -19,45 +28,40 @@ import TestSetup from './_test_setup.mdx';
1. Go to the Google Cloud Platform and choose your project: [https://console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials)
2. Click on "+ CREATE CREDENTIALS" and choose "OAuth client ID"
3. Choose "Web application" as application type and give a name
4. Add the redirect uri
- {your-domain}/ui/login/login/externalidp/callback
- Example redirect url for the domain `https://acme-gzoe4x.zitadel.cloud` would look like this: `https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
5. Save the Client ID and Client secret
4. [Paste the ZITADEL Callback URL you copied before](#open-the-google-identity-provider-template) to the Authorised redirect URIs
![Google OAuth App Registration](/img/guides/google_oauth_app_registration.png)
### Client ID and secret
You will need the Client ID and Client secret to configure the Google Identity Provider in ZITADEL.
![Google Client ID and Secret](/img/guides/google_client_id_secret.png)
## ZITADEL Configuration
### Add custom login policy
Go back [to the Google provider template you opened before in ZITADEL](#open-the-google-identity-provider-template).
Add the [client ID and secret created before on your Google App](#client-id-and-client-secret).
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Google"/>
### Create a new Google Provider
The Google provider template has everything you need preconfigured.
Add the client ID and secret created before on your Google App.
You can configure the following settings if you like, a useful default will be filled if you don't change anything:
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled. This information will be taken to create/update the user within ZITADEL. ZITADEL ensures that at least the `openid`-scope is always sent.
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This information will be taken to create/update the user within ZITADEL.
ZITADEL ensures that at least the `openid`-scope is always sent.
<GeneralConfigDescription provider_account="Google account" />
![Google Provider](/img/guides/zitadel_google_create_provider.png)
### Activate IdP
<Activate/>
![Activate the Google Provider](/img/guides/zitadel_activate_google.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your Google login"/>

View File

@ -8,12 +8,20 @@ import GeneralConfigDescription from './_general_config_description.mdx';
import Intro from './_intro.mdx';
import CustomLoginPolicy from './_custom_login_policy.mdx';
import IDPsOverview from './_idps_overview.mdx';
import GenericOIDC from './_generic_oidc.mdx';
import Activate from './_activate.mdx';
import TestSetup from './_test_setup.mdx';
<Intro provider="Keycloak"/>
## Open the Generic OIDC Provider Template
<IDPsOverview templates="Generic OIDC"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it in the Keycloak Client later.
![Generic OIDC Provider](/img/guides/zitadel_generic_oidc_create_provider.png)
## Keycloak Configuration
### Register a new client
@ -22,9 +30,7 @@ import TestSetup from './_test_setup.mdx';
2. Click on "Create Client"
3. Choose OpenID Connect as Client Type and give your client an ID
4. Enable Client authentication and the standard flow and direct access grants as authentication flow
5. Add the valid redirect URIs
- {your-domain}/ui/login/login/externalidp/callback
- Example redirect url for the domain `https://acme-gzoe4x.zitadel.cloud` would look like this: `https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
5. [Paste the ZITADEL Callback URL you copied before](#open-the-generic-oidc-provider-template) to the redirect URIs
6. Go to the credentials tab and copy the secret
![Add new OIDC Client in Keycloak](/img/guides/keycloak_add_client.png)
@ -32,22 +38,17 @@ import TestSetup from './_test_setup.mdx';
## ZITADEL configuration
### Add custom login policy
1. Go back [to the Generic OIDC Provider template you opened before in ZITADEL](#open-the-generic-oidc-provider-template).
2. Add the [client ID and secret created before on your Keycloak Client](#register-a-new-client).
3. Give the provider a name, e.g. Keycloak. This name will be displayed on the login screen button.
4. Add the issuer URL of your Keycloak realm with the path /auth/realms/$REALM, e.g. `https://lemur-0.cloud-iam.com/auth/realms/acme`
<CustomLoginPolicy/>
### Go to the IdP providers overview
<IDPsOverview templates="Generic OIDC"/>
### Create a new generic OIDC provider
<GenericOIDC
name=": e.g. Keycloak"
issuer=": The domain where your Keycloak can be reached with the path /auth/realms/$REALM, Example: https://lemur-0.cloud-iam.com/auth/realms/acme"
clientid=": Client id from the client previously created in your Keycloak account"
/>
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This information will be taken to create/update the user within ZITADEL.
ZITADEL ensures that at least the `openid`-scope is always sent.
<GeneralConfigDescription provider_account="Keycloak account" />
@ -59,11 +60,14 @@ import TestSetup from './_test_setup.mdx';
![Activate the Keycloak Provider](/img/guides/zitadel_activate_keycloak.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your Keycloak login"/>
![Keycloak Button](/img/guides/zitadel_login_keycloak.png)
![Keycloak Login](/img/guides/keycloak_login.png)

View File

@ -19,10 +19,6 @@ import TestSetup from './_test_setup.mdx';
## ZITADEL Configuration
### Add custom login policy
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Active Directory / LDAP"/>
@ -66,6 +62,10 @@ Otherwise, your users passwords are sent in clear text through the wire.
![Activate the LDAP Provider](/img/guides/zitadel_activate_ldap.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="ZITADELs LDAP login"/>

View File

@ -27,10 +27,6 @@ fill in the URL when creating the SAML SP in ZITADEL.
## ZITADEL configuration
### Add custom login policy
<CustomLoginPolicy/>
### Go to the IdP providers overview
<IDPsOverview templates="SAML SP"/>
@ -56,11 +52,14 @@ They are available under [https://{CUSTOMDOMAIN}/idps/{ID of the provider in ZIT
![Activate the SAML SP Provider](/img/guides/zitadel_activate_saml.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your SAML SP login"/>
![SAML SP Button](/img/guides/zitadel_login_saml.png)
![SAML SP Login](/img/guides/mocksaml_login.png)

View File

@ -8,13 +8,21 @@ import GeneralConfigDescription from './_general_config_description.mdx';
import Intro from './_intro.mdx';
import CustomLoginPolicy from './_custom_login_policy.mdx';
import IDPsOverview from './_idps_overview.mdx';
import GenericOIDC from './_generic_oidc.mdx';
import Activate from './_activate.mdx';
import PrefillAction from './_prefill_action.mdx';
import TestSetup from './_test_setup.mdx';
<Intro provider="OKTA"/>
## Open the Generic OIDC Provider Template
<IDPsOverview templates="Generic OIDC"/>
Click on the ZITADEL Callback URL to copy it to your clipboard.
You will have to paste it in the OKTA application later.
![Generic OIDC Provider](/img/guides/zitadel_generic_oidc_create_provider.png)
## OKTA Configuration
### Register a new client
@ -22,47 +30,42 @@ import TestSetup from './_test_setup.mdx';
1. Login to your OKTA Account and go to the applications list: <OKTA-DOMAIN/admin/apps/active>
2. Click on "Create App Integration" and choose "OIDC - OpenID Connect"
3. Choose Web application as Application type and give a name
4. Add the Sign-in redirect uris
- {your-domain}/ui/login/login/externalidp/callback
- Example redirect url for the domain `https://acme-gzoe4x.zitadel.cloud` would look like this: `https://acme-gzoe4x.zitadel.cloud/ui/login/login/externalidp/callback`
5. Save clientid and client secret
4. [Paste the ZITADEL Callback URL you copied before](#open-the-generic-oidc-provider-template) to the Sign-in redirect URIs
5. Save the clientid and client secret
![Add new OIDC Application in OKTA](/img/guides/okta_add_app.png)
## ZITADEL Configuration
### Add custom login policy
1. Go back [to the Generic OIDC Provider template you opened before in ZITADEL](#open-the-generic-oidc-provider-template).
2. Add the [client ID and secret created before on your Web application](#register-a-new-client).
3. Give the provider a name, e.g. OKTA. This name will be displayed on the login screen button.
4. Add the issuer URL of your OKTA account, e.g. `https://trial-1925566.okta.com`
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Generic OIDC"/>
### Create a new Generic OIDC Provider
<GenericOIDC
name=": e.g. OKTA"
issuer=": The domain of your OKTA account, Example: https://trial-1925566.okta.com"
clientid=": Client id from the application previously created in your OKTA account"
/>
You can optionally configure the following settings.
A useful default will be filled if you don't change anything.
**Scopes**: The scopes define which scopes will be sent to the provider, `openid`, `profile`, and `email` are prefilled.
This information will be taken to create/update the user within ZITADEL.
ZITADEL ensures that at least the `openid`-scope is always sent.
<GeneralConfigDescription provider_account="OKTA account" />
![OKTA Provider](/img/guides/zitadel_okta_create_provider.png)
### Activate IdP
<Activate/>
![Activate the OKTA Provider](/img/guides/zitadel_activate_okta.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="your OKTA login"/>
<!-- TODO: Image highlights GitHub -->
<!-- TODO: Image highlights Google -->
![OKTA Button](/img/guides/zitadel_login_okta.png)
![OKTA Login](/img/guides/okta_login.png)

View File

@ -16,10 +16,6 @@ import TestSetup from './_test_setup.mdx';
## ZITADEL Configuration
### Add custom login policy
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="SAML SP"/>
@ -30,8 +26,6 @@ To be able to create the application in OKTA we need the provider id from ZITADE
1. Create a new SAML SP with a name and a random text in the Metadata Xml field.
We will fill that as soon as we have done the configuration in OKTA.
2. Save Configuration
3. Open up the detail of the configuration and copy the provider ID from the browser URL:
`$CUSTOM-DOMAIN/ui/console/org/provider/saml/$PROVIDER-ID`
As an alternative you can add the SAML identity provider through the API, either on the default settings or on a specific organization:
- [Add Default SAML Identity Provider](/docs/apis/resources/admin/admin-service-add-saml-provider)
@ -39,6 +33,10 @@ As an alternative you can add the SAML identity provider through the API, either
![OKTA Provider Empty](/img/guides/zitadel_okta_saml_provider_empty.png)
After you created the SAML SP in ZITADEL, you can copy the URLs you need to configure in your OKTA application.
![OKTA SAML App URLs](/img/guides/zitadel_okta_saml_provider_urls.png)
## OKTA Configuration
### Register a new client
@ -46,10 +44,9 @@ As an alternative you can add the SAML identity provider through the API, either
1. Log in to your OKTA Account and go to the applications list: <OKTA-DOMAIN/admin/apps/active>
2. Click on "Create App Integration" and choose "SAML 2.0"
3. Give the application a name
4. Fill the configuration as following (Replace `your-domain` and `saml-idp-id` with your data):
- Single sign-on URL `{your-domain}/ui/login/login/externalidp/saml/acs`
- Audience URI (SP Entity ID): `{your-domain}/idps/{saml-idp-id}/saml/metadata`
- Example redirect url for the domain `https://acme.zitadel.cloud` would look like this: `https://acme.zitadel.cloud/idps/257372385775925924/saml/metadata`
4. Click on the ZITADEL URLs that your SAML IDP shows since you created it in ZITADEL and paste them accordingly:
- **Single sign-on URL**: Paste the *ZITADEL ACS Login Form URL*.
- **Audience URI (SP Entity ID)**: Paste the *ZITADEL Metadata URL*
5. Save the configuration
6. Copy the metadata URL from the details
@ -85,7 +82,7 @@ You are now finished with the configuration in OKTA and you can switch back to y
### Add Metadata Xml
Add the metadata URL you have saved before from OKTA to the Metadata URL.
Add [the metadata URL you have saved before from OKTA](#register-a-new-client) to the Metadata URL.
As soon as you have saved the provider, and you have a look at the detail you should now see the Metadata Xml field filled.
If you prefer changing the configuration through the API you can update the SAML provider on the default settings or a specific organization:
@ -104,13 +101,9 @@ You can also fill the optional fields if needed:
![Activate the OKTA Provider](/img/guides/zitadel_activate_okta_saml.png)
### Add Action to map user attributes
### Ensure your Login Policy allows External IDPs
<PrefillAction fields="username, firstname, lastname, email and email verified" provider="OKTA"/>
```js reference
https://github.com/zitadel/actions/blob/main/examples/okta_saml_prefil_register_form.js
```
<CustomLoginPolicy/>
## Test the setup
@ -119,3 +112,12 @@ https://github.com/zitadel/actions/blob/main/examples/okta_saml_prefil_register_
![OKTA Button](/img/guides/zitadel_login_okta.png)
![OKTA Login](/img/guides/okta_login.png)
### Add Action to map user attributes
<PrefillAction fields="username, firstname, lastname, email and email verified" provider="OKTA"/>
```js reference
https://github.com/zitadel/actions/blob/main/examples/okta_saml_prefil_register_form.js
```

View File

@ -150,10 +150,6 @@ ldapadd -x -h localhost -D "cn=admin,dc=example,dc=com" -f example.ldif -w 'Pass
## ZITADEL Configuration
### Add custom login policy
<CustomLoginPolicy/>
### Go to the IdP Providers Overview
<IDPsOverview templates="Active Directory / LDAP"/>
@ -195,6 +191,10 @@ Fill in the template fields with the exact values listed below. The fields are d
![Activate the LDAP Provider](/img/guides/zitadel_activate_ldap.png)
### Ensure your Login Policy allows External IDPs
<CustomLoginPolicy/>
## Test the setup
<TestSetup loginscreen="ZITADELs LDAP login"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB