diff --git a/console/angular.json b/console/angular.json
index 278498ccd7..5564b2c428 100644
--- a/console/angular.json
+++ b/console/angular.json
@@ -63,7 +63,7 @@
{
"type": "initial",
"maximumWarning": "8mb",
- "maximumError": "9mb"
+ "maximumError": "10mb"
},
{
"type": "anyComponentStyle",
diff --git a/console/package.json b/console/package.json
index fcf3a4bbf8..2c1d38da1b 100644
--- a/console/package.json
+++ b/console/package.json
@@ -24,6 +24,8 @@
"@angular/platform-browser-dynamic": "^16.2.5",
"@angular/router": "^16.2.5",
"@angular/service-worker": "^16.2.5",
+ "@connectrpc/connect": "^2.0.0",
+ "@connectrpc/connect-web": "^2.0.0",
"@ctrl/ngx-codemirror": "^6.1.0",
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
@@ -31,6 +33,8 @@
"@grpc/grpc-js": "^1.11.2",
"@netlify/framework-info": "^9.8.13",
"@ngx-translate/core": "^15.0.0",
+ "@zitadel/client": "^1.0.6",
+ "@zitadel/proto": "^1.0.3",
"angular-oauth2-oidc": "^15.0.1",
"angularx-qrcode": "^16.0.0",
"buffer": "^6.0.3",
diff --git a/console/src/app/app.component.html b/console/src/app/app.component.html
index 5b31b33dc4..18c5c72501 100644
--- a/console/src/app/app.component.html
+++ b/console/src/app/app.component.html
@@ -1,5 +1,5 @@
-
+
{
// We use navigateByUrl as our urls may have queryParams
- this.router.navigateByUrl(currentUrl);
+ this.router.navigateByUrl(currentUrl).then();
});
}
@@ -283,18 +283,16 @@ export class AppComponent implements OnDestroy {
this.translate.addLangs(supportedLanguages);
this.translate.setDefaultLang(fallbackLanguage);
- this.authService.userSubject.pipe(takeUntil(this.destroy$)).subscribe((userprofile) => {
- if (userprofile) {
- const cropped = navigator.language.split('-')[0] ?? fallbackLanguage;
- const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage;
+ this.authService.user.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((userprofile) => {
+ const cropped = navigator.language.split('-')[0] ?? fallbackLanguage;
+ const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage;
- const lang = userprofile?.human?.profile?.preferredLanguage.match(supportedLanguagesRegexp)
- ? userprofile.human.profile?.preferredLanguage
- : fallbackLang;
- this.translate.use(lang);
- this.language = lang;
- this.document.documentElement.lang = lang;
- }
+ const lang = userprofile?.human?.profile?.preferredLanguage.match(supportedLanguagesRegexp)
+ ? userprofile.human.profile?.preferredLanguage
+ : fallbackLang;
+ this.translate.use(lang);
+ this.language = lang;
+ this.document.documentElement.lang = lang;
});
}
@@ -308,7 +306,7 @@ export class AppComponent implements OnDestroy {
}
private setFavicon(theme: string): void {
- this.authService.labelpolicy.pipe(takeUntil(this.destroy$)).subscribe((lP) => {
+ this.authService.labelpolicy$.pipe(startWith(undefined), takeUntil(this.destroy$)).subscribe((lP) => {
if (theme === 'dark-theme' && lP?.iconUrlDark) {
// Check if asset url is stable, maybe it was deleted but still wasn't applied
fetch(lP.iconUrlDark).then((response) => {
diff --git a/console/src/app/components/features/features.component.html b/console/src/app/components/features/features.component.html
index e663569210..fdd397084a 100644
--- a/console/src/app/components/features/features.component.html
+++ b/console/src/app/components/features/features.component.html
@@ -403,6 +403,34 @@
'SETTING.FEATURES.OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION' | translate
}}
+
+
+
{{ 'SETTING.FEATURES.CONSOLEUSEV2USERAPI' | translate }}
+
+
+
+
+ {{ 'SETTING.FEATURES.STATES.DISABLED' | translate }}
+
+
+
+
+ {{ 'SETTING.FEATURES.STATES.ENABLED' | translate }}
+
+
+
+
+
{{
+ 'SETTING.FEATURES.CONSOLEUSEV2USERAPI_DESCRIPTION' | translate
+ }}
+
diff --git a/console/src/app/components/features/features.component.ts b/console/src/app/components/features/features.component.ts
index 327e9d2792..0f8ff761f6 100644
--- a/console/src/app/components/features/features.component.ts
+++ b/console/src/app/components/features/features.component.ts
@@ -16,13 +16,14 @@ import { InfoSectionModule } from 'src/app/modules/info-section/info-section.mod
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { Event } from 'src/app/proto/generated/zitadel/event_pb';
import { Source } from 'src/app/proto/generated/zitadel/feature/v2beta/feature_pb';
-import {
- GetInstanceFeaturesResponse,
- SetInstanceFeaturesRequest,
-} from 'src/app/proto/generated/zitadel/feature/v2beta/instance_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { FeatureService } from 'src/app/services/feature.service';
import { ToastService } from 'src/app/services/toast.service';
+import {
+ GetInstanceFeaturesResponse,
+ SetInstanceFeaturesRequest,
+} from '../../proto/generated/zitadel/feature/v2/instance_pb';
+import { withIdentifier } from 'codelyzer/util/astQuery';
enum ToggleState {
ENABLED = 'ENABLED',
@@ -39,6 +40,7 @@ type ToggleStates = {
oidcTokenExchange?: FeatureState;
actions?: FeatureState;
oidcSingleV1SessionTermination?: FeatureState;
+ consoleUseV2UserApi?: FeatureState;
};
@Component({
@@ -142,6 +144,7 @@ export class FeaturesComponent implements OnDestroy {
);
changed = true;
}
+ req.setConsoleUseV2UserApi(this.toggleStates?.consoleUseV2UserApi?.state === ToggleState.ENABLED);
if (changed) {
this.featureService
@@ -232,6 +235,10 @@ export class FeaturesComponent implements OnDestroy {
? ToggleState.ENABLED
: ToggleState.DISABLED,
},
+ consoleUseV2UserApi: {
+ source: this.featureData.consoleUseV2UserApi?.source || Source.SOURCE_INSTANCE,
+ state: this.featureData.consoleUseV2UserApi?.enabled ? ToggleState.ENABLED : ToggleState.DISABLED,
+ },
};
});
}
diff --git a/console/src/app/directives/has-role/has-role.directive.ts b/console/src/app/directives/has-role/has-role.directive.ts
index b58e1f3a10..9ba21c1dd2 100644
--- a/console/src/app/directives/has-role/has-role.directive.ts
+++ b/console/src/app/directives/has-role/has-role.directive.ts
@@ -1,18 +1,17 @@
-import { Directive, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core';
-import { Subject, takeUntil } from 'rxjs';
+import { DestroyRef, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({
selector: '[cnslHasRole]',
})
-export class HasRoleDirective implements OnDestroy {
- private destroy$: Subject = new Subject();
+export class HasRoleDirective {
private hasView: boolean = false;
@Input() public set hasRole(roles: string[] | RegExp[] | undefined) {
if (roles && roles.length > 0) {
this.authService
.isAllowed(roles)
- .pipe(takeUntil(this.destroy$))
+ .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((isAllowed) => {
if (isAllowed && !this.hasView) {
if (this.viewContainerRef.length !== 0) {
@@ -38,10 +37,6 @@ export class HasRoleDirective implements OnDestroy {
private authService: GrpcAuthService,
protected templateRef: TemplateRef,
protected viewContainerRef: ViewContainerRef,
+ private readonly destroyRef: DestroyRef,
) {}
-
- ngOnDestroy(): void {
- this.destroy$.next();
- this.destroy$.complete();
- }
}
diff --git a/console/src/app/modules/accounts-card/accounts-card.component.ts b/console/src/app/modules/accounts-card/accounts-card.component.ts
index 617a41bf6d..2676a5bcf5 100644
--- a/console/src/app/modules/accounts-card/accounts-card.component.ts
+++ b/console/src/app/modules/accounts-card/accounts-card.component.ts
@@ -4,6 +4,7 @@ import { AuthConfig } from 'angular-oauth2-oidc';
import { Session, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
import { AuthenticationService } from 'src/app/services/authentication.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
+import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'cnsl-accounts-card',
@@ -18,6 +19,8 @@ export class AccountsCardComponent implements OnInit {
public sessions: Session.AsObject[] = [];
public loadingUsers: boolean = false;
public UserState: any = UserState;
+ private labelpolicy = toSignal(this.userService.labelpolicy$, { initialValue: undefined });
+
constructor(
public authService: AuthenticationService,
private router: Router,
@@ -68,7 +71,7 @@ export class AccountsCardComponent implements OnInit {
}
public logout(): void {
- const lP = JSON.stringify(this.userService.labelpolicy.getValue());
+ const lP = JSON.stringify(this.labelpolicy());
localStorage.setItem('labelPolicyOnSignout', lP);
this.authService.signout();
diff --git a/console/src/app/modules/footer/footer.component.html b/console/src/app/modules/footer/footer.component.html
index 26d863d129..b9eda2d7db 100644
--- a/console/src/app/modules/footer/footer.component.html
+++ b/console/src/app/modules/footer/footer.component.html
@@ -1,6 +1,6 @@