feat: specify login UI version on instance and apps (#9071)

# Which Problems Are Solved

To be able to migrate or test the new login UI, admins might want to
(temporarily) switch individual apps.
At a later point admin might want to make sure all applications use the
new login UI.

# How the Problems Are Solved

- Added a feature flag `` on instance level to require all apps to use
the new login and provide an optional base url.
- if the flag is enabled, all (OIDC) applications will automatically use
the v2 login.
  - if disabled, applications can decide based on their configuration
- Added an option on OIDC apps to use the new login UI and an optional
base url.
- Removed the requirement to use `x-zitadel-login-client` to be
redirected to the login V2 and retrieve created authrequest and link
them to SSO sessions.
- Added a new "IAM_LOGIN_CLIENT" role to allow management of users,
sessions, grants and more without `x-zitadel-login-client`.

# Additional Changes

None

# Additional Context

closes https://github.com/zitadel/zitadel/issues/8702
This commit is contained in:
Livio Spring
2024-12-19 10:37:46 +01:00
committed by GitHub
parent b5e92a6144
commit 50d2b26a28
89 changed files with 1670 additions and 321 deletions

View File

@@ -147,6 +147,22 @@
>
{{ 'APP.OIDC.REFRESHTOKEN' | translate }}
</mat-checkbox>
<mat-checkbox
*ngIf="loginV2"
color="primary"
class="rt"
[formControl]="loginV2"
name="loginV2"
matTooltip="{{ 'APP.LOGINV2DESC' | translate }}"
>
{{ 'APP.LOGINV2.USEV2' | translate }}
</mat-checkbox>
<cnsl-form-field class="app-formfield">
<cnsl-label>{{ 'APP.LOGINV2.BASEURL' | translate }}</cnsl-label>
<input cnslInput formControlName="loginV2BaseURL" />
</cnsl-form-field>
</div>
</div>

View File

@@ -1,6 +1,6 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit, ViewEncapsulation, signal } from '@angular/core';
import { Component, OnDestroy, OnInit, signal } from '@angular/core';
import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog';
@@ -21,6 +21,9 @@ import {
APIConfig,
App,
AppState,
LoginV1,
LoginV2,
LoginVersion,
OIDCAppType,
OIDCAuthMethodType,
OIDCConfig,
@@ -50,8 +53,8 @@ import {
getAuthMethodFromPartialConfig,
getPartialConfigFromAuthMethod,
IMPLICIT_METHOD,
PKCE_METHOD,
PK_JWT_METHOD,
PKCE_METHOD,
POST_METHOD,
} from '../authmethods';
import { AuthMethodDialogComponent } from './auth-method-dialog/auth-method-dialog.component';
@@ -182,6 +185,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public currentSetting: string | undefined = this.settingsList[0].id;
public isNew = signal<boolean>(false);
constructor(
private envSvc: EnvironmentService,
public translate: TranslateService,
@@ -203,6 +207,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
grantTypesList: [{ value: [], disabled: true }],
appType: [{ value: '', disabled: true }],
authMethodType: [{ value: '', disabled: true }],
loginV2: [{ value: false, disabled: true }],
loginV2BaseURL: [{ value: '', disabled: true }],
});
this.oidcTokenForm = this.fb.group({
@@ -430,6 +436,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcTokenForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig?.loginVersion?.loginV1) {
this.oidcForm.controls['loginV2'].setValue(false);
} else if (this.app.oidcConfig?.loginVersion?.loginV2) {
this.oidcForm.controls['loginV2'].setValue(true);
this.oidcForm.controls['loginV2BaseURL'].setValue(this.app.oidcConfig.loginVersion.loginV2.baseUri);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
this.oidcTokenForm.patchValue(this.app.oidcConfig);
@@ -655,6 +667,15 @@ export class AppDetailComponent implements OnInit, OnDestroy {
req.setAuthMethodType(this.app.oidcConfig.authMethodType);
req.setGrantTypesList(this.app.oidcConfig.grantTypesList);
req.setAppType(this.app.oidcConfig.appType);
const login = new LoginVersion();
if (this.loginV2?.value) {
const loginV2 = new LoginV2();
loginV2.setBaseUri(this.loginV2BaseURL?.value);
login.setLoginV2(loginV2);
} else {
login.setLoginV1(new LoginV1());
}
req.setLoginVersion(login);
// token
req.setAccessTokenType(this.app.oidcConfig.accessTokenType);
@@ -839,6 +860,14 @@ export class AppDetailComponent implements OnInit, OnDestroy {
return this.oidcForm.get('authMethodType');
}
public get loginV2(): FormControl<boolean> | null {
return this.oidcForm.get('loginV2') as FormControl<boolean>;
}
public get loginV2BaseURL(): AbstractControl | null {
return this.oidcForm.get('loginV2BaseURL');
}
public get apiAuthMethodType(): AbstractControl | null {
return this.apiForm.get('authMethodType') as UntypedFormControl;
}

View File

@@ -2537,6 +2537,10 @@
"CLIENTSECRETREGENERATED": "генерирана клиентска тайна.",
"DELETED": "Приложението е изтрито.",
"CONFIGCHANGED": "Открити са промени!"
},
"LOGINV2": {
"USEV2": "Използвайте новия интерфейс за вход",
"BASEURL": "Персонализиран основен URL адрес за новия интерфейс за вход"
}
},
"GENDERS": {

View File

@@ -2550,6 +2550,10 @@
"CLIENTSECRETREGENERATED": "klient tajemství regenerováno.",
"DELETED": "Aplikace smazána.",
"CONFIGCHANGED": "Zjištěny změny!"
},
"LOGINV2": {
"USEV2": "Použít nové uživatelské rozhraní pro přihlášení",
"BASEURL": "Vlastní základní adresa URL pro nové uživatelské rozhraní pro přihlášení"
}
},
"GENDERS": {

View File

@@ -2541,6 +2541,10 @@
"CLIENTSECRETREGENERATED": "Client Secret generiert.",
"DELETED": "App gelöscht.",
"CONFIGCHANGED": "Konfigurationsänderung entdeckt."
},
"LOGINV2": {
"USEV2": "Neue Login-Benutzeroberfläche verwenden",
"BASEURL": "Benutzerdefinierte Basis-URL für die neue Login-Benutzeroberfläche"
}
},
"GENDERS": {

View File

@@ -2566,6 +2566,10 @@
"CLIENTSECRETREGENERATED": "client secret generated.",
"DELETED": "App deleted.",
"CONFIGCHANGED": "Changes detected!"
},
"LOGINV2": {
"USEV2": "Use new Login UI",
"BASEURL": "Custom base URL for the new Login UI"
}
},
"GENDERS": {

View File

@@ -2538,6 +2538,10 @@
"CLIENTSECRETREGENERATED": "secreto del cliente generado.",
"DELETED": "App borrada.",
"CONFIGCHANGED": "¡Cambios detectados!"
},
"LOGINV2": {
"USEV2": "Usar la nueva interfaz de usuario de inicio de sesión",
"BASEURL": "URL base personalizada para la nueva interfaz de usuario de inicio de sesión"
}
},
"GENDERS": {

View File

@@ -2542,6 +2542,10 @@
"CLIENTSECRETREGENERATED": "secret client généré.",
"DELETED": "Application supprimée.",
"CONFIGCHANGED": "Changements détectés !"
},
"LOGINV2": {
"USEV2": "Utiliser la nouvelle interface utilisateur de connexion",
"BASEURL": "URL de base personnalisée pour la nouvelle interface utilisateur de connexion"
}
},
"GENDERS": {

View File

@@ -2564,6 +2564,10 @@
"CLIENTSECRETREGENERATED": "Az ügyfél titok generálva.",
"DELETED": "Az app törölve.",
"CONFIGCHANGED": "Változások észlelve!"
},
"LOGINV2": {
"USEV2": "Új bejelentkezési felhasználói felület használata",
"BASEURL": "Egyéni alapértelmezett URL az új bejelentkezési felhasználói felülethez"
}
},
"GENDERS": {

View File

@@ -2255,6 +2255,10 @@
"CLIENTSECRETREGENERATED": "rahasia klien dihasilkan.",
"DELETED": "Aplikasi dihapus.",
"CONFIGCHANGED": "Perubahan terdeteksi!"
},
"LOGINV2": {
"USEV2": "Gunakan UI Login baru",
"BASEURL": "URL dasar kustom untuk UI Login baru"
}
},
"GENDERS": { "0": "Tidak dikenal", "1": "Perempuan", "2": "Pria", "3": "Lainnya" },

View File

@@ -2542,6 +2542,10 @@
"CLIENTSECRETREGENERATED": "Client secret generato.",
"DELETED": "App rimossa con successo.",
"CONFIGCHANGED": "Modifiche alla configurazione rilevate"
},
"LOGINV2": {
"USEV2": "Utilizza la nuova interfaccia utente di accesso",
"BASEURL": "URL base personalizzato per la nuova interfaccia utente di accesso"
}
},
"GENDERS": {

View File

@@ -2532,6 +2532,10 @@
"CLIENTSECRETREGENERATED": "クライアントシークレットが生成されました。",
"DELETED": "アプリが削除されました。",
"CONFIGCHANGED": "変更を検出しました!"
},
"LOGINV2": {
"USEV2": "新しいログインUIを使用する",
"BASEURL": "新しいログインUIのカスタムベースURL"
}
},
"GENDERS": {

View File

@@ -2538,6 +2538,10 @@
"CLIENTSECRETREGENERATED": "Клиентската тајна е генерирана.",
"DELETED": "Апликацијата е избришана.",
"CONFIGCHANGED": "Детектирани промени!"
},
"LOGINV2": {
"USEV2": "Користете нов интерфејс за најава",
"BASEURL": "Прилагоден основен URL за новиот интерфејс за најава"
}
},
"GENDERS": {

View File

@@ -2557,6 +2557,10 @@
"CLIENTSECRETREGENERATED": "client geheim gegenereerd.",
"DELETED": "App verwijderd.",
"CONFIGCHANGED": "Wijzigingen gedetecteerd!"
},
"LOGINV2": {
"USEV2": "Nieuwe login-gebruikersinterface gebruiken",
"BASEURL": "Aangepaste basis-URL voor de nieuwe login-gebruikersinterface"
}
},
"GENDERS": {

View File

@@ -2541,6 +2541,10 @@
"CLIENTSECRETREGENERATED": "Sekret klienta został wygenerowany.",
"DELETED": "Aplikacja została usunięta.",
"CONFIGCHANGED": "Wykryto zmiany!"
},
"LOGINV2": {
"USEV2": "Użyj nowego interfejsu użytkownika logowania",
"BASEURL": "Niestandardowy podstawowy adres URL dla nowego interfejsu użytkownika logowania"
}
},
"GENDERS": {

View File

@@ -2537,6 +2537,10 @@
"CLIENTSECRETREGENERATED": "segredo do cliente gerado.",
"DELETED": "Aplicativo excluído.",
"CONFIGCHANGED": "Alterações detectadas!"
},
"LOGINV2": {
"USEV2": "Usar a nova interface de usuário de login",
"BASEURL": "URL base personalizado para a nova interface de usuário de login"
}
},
"GENDERS": {

View File

@@ -2649,6 +2649,10 @@
"CLIENTSECRETREGENERATED": "Клиентский ключ сгенерирован.",
"DELETED": "Приложение удалено.",
"CONFIGCHANGED": "Обнаружены изменения!"
},
"LOGINV2": {
"USEV2": "Использовать новый интерфейс входа",
"BASEURL": "Настраиваемый базовый URL для нового интерфейса входа"
}
},
"GENDERS": {

View File

@@ -2570,6 +2570,10 @@
"CLIENTSECRETREGENERATED": "Klienthemlighet genererad.",
"DELETED": "App raderad.",
"CONFIGCHANGED": "Ändringar upptäckta!"
},
"LOGINV2": {
"USEV2": "Använd nya inloggningsgränssnittet",
"BASEURL": "Anpassad bas-URL för det nya inloggningsgränssnittet"
}
},
"GENDERS": {

View File

@@ -2541,6 +2541,10 @@
"CLIENTSECRETREGENERATED": "客户端秘钥已生成。",
"DELETED": "应用已删除。",
"CONFIGCHANGED": "检测到变化!"
},
"LOGINV2": {
"USEV2": "使用新的登录UI",
"BASEURL": "新的登录UI的自定义基本URL"
}
},
"GENDERS": {