From 22930ade66d24a15337e3ae4777e253be45a7d56 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Wed, 27 May 2026 10:24:28 +0200 Subject: [PATCH 1/2] Fixed default dashboard not opening after OAuth2 login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OAuth2 redirect to root URL (/?accessToken=...&refreshToken=...) authenticates the user in a single selectUserReady emission, which skip(1) in app.component was unconditionally swallowing — gotoDefaultPlace never fired and the user landed on /home instead of their default dashboard. Same bug for ?username=&password= URL logins. Deep-link OAuth2 (with prev_uri cookie) is preserved by the pathname === '/' guard. --- ui-ngx/src/app/app.component.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index dcb5ec1e1f..df2191438e 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -92,6 +92,14 @@ export class AppComponent { } setupAuth() { + // URL-token login (OAuth2 redirect to root) emits selectUserReady once; + // skip(1) would swallow it and gotoDefaultPlace would never fire. + const params = new URLSearchParams(window.location.search); + const hasRootUrlAuth = window.location.pathname === '/' && ( + !!params.get('accessToken') || + (!!params.get('username') && !!params.get('password')) + ); + this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { @@ -102,7 +110,7 @@ export class AppComponent { } this.notifyUserLang(userLang); }), - skip(1), + skip(hasRootUrlAuth ? 0 : 1), ).subscribe((data) => { this.authService.gotoDefaultPlace(data.isAuthenticated); }); From f7dd528765f7f46b3072d7e285af38d363fda01d Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Mon, 6 Jul 2026 15:48:32 +0200 Subject: [PATCH 2/2] Moved root-URL login redirect handling into AuthService Reverted app.component to plain skip(1). AuthService.loadUser now flags a root-URL login (accessToken, username+password, or publicId at pathname '/') and reloadUser drives gotoDefaultPlace once the user resolves. This also covers /?publicId= root logins. Deep links (pathname !== '/') keep opening the requested page. --- ui-ngx/src/app/app.component.ts | 10 +--------- ui-ngx/src/app/core/auth/auth.service.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index df2191438e..dcb5ec1e1f 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -92,14 +92,6 @@ export class AppComponent { } setupAuth() { - // URL-token login (OAuth2 redirect to root) emits selectUserReady once; - // skip(1) would swallow it and gotoDefaultPlace would never fire. - const params = new URLSearchParams(window.location.search); - const hasRootUrlAuth = window.location.pathname === '/' && ( - !!params.get('accessToken') || - (!!params.get('username') && !!params.get('password')) - ); - this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { @@ -110,7 +102,7 @@ export class AppComponent { } this.notifyUserLang(userLang); }), - skip(hasRootUrlAuth ? 0 : 1), + skip(1), ).subscribe((data) => { this.authService.gotoDefaultPlace(data.isAuthenticated); }); diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 744cb92534..d7342cf537 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -73,6 +73,12 @@ export class AuthService { private refreshTokenSubject: ReplaySubject = null; private jwtHelper = new JwtHelperService(); + // Set in loadUser when the user arrives via a root-URL login (OAuth2 redirect, + // or username/password/publicId in the query). Such logins resolve during + // bootstrap and produce a single selectUserReady emission, so gotoDefaultPlace + // is driven here rather than from the app.component subscription. + private urlAuthRedirect = false; + private static _storeGet(key) { return localStorage.getItem(key); } @@ -102,10 +108,18 @@ export class AuthService { (authPayload) => { this.notifyAuthenticated(authPayload); this.notifyUserLoaded(true); + if (this.urlAuthRedirect) { + this.urlAuthRedirect = false; + this.gotoDefaultPlace(true); + } }, () => { this.notifyUnauthenticated(); this.notifyUserLoaded(true); + if (this.urlAuthRedirect) { + this.urlAuthRedirect = false; + this.gotoDefaultPlace(false); + } } ); } @@ -303,6 +317,9 @@ export class AuthService { const username = this.utils.getQueryParam('username'); const password = this.utils.getQueryParam('password'); const loginError = this.utils.getQueryParam('loginError'); + // Deep links (pathname !== '/') must open the requested page, not the default dashboard. + this.urlAuthRedirect = window.location.pathname === '/' && + !!(publicId || accessToken || (username && password)); if (publicId) { return this.publicLogin(publicId).pipe( mergeMap((response) => {