From 41c0bdc346b8fcf2b1ed77284d422a26791ddc5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:27:39 +0000 Subject: [PATCH] fix: resync clock before invalidating tokens at startup At startup, when the first OAuth event fires, the service checks if the access token is valid. Previously it would immediately clear storage if the token appeared invalid. This could cause false negatives when the local clock was out of sync (e.g. VM/Citrix environments). Now the service first re-syncs the clock offset via TimeSyncService, then re-evaluates the token validity. Storage is only cleared if the token is still invalid after the corrected time check. --- .../auth/oidc/redirect-auth.service.spec.ts | 12 +++++++++++ .../lib/auth/oidc/redirect-auth.service.ts | 20 ++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/core/src/lib/auth/oidc/redirect-auth.service.spec.ts b/lib/core/src/lib/auth/oidc/redirect-auth.service.spec.ts index e440e184eb..aeb5690208 100644 --- a/lib/core/src/lib/auth/oidc/redirect-auth.service.spec.ts +++ b/lib/core/src/lib/auth/oidc/redirect-auth.service.spec.ts @@ -172,6 +172,18 @@ describe('RedirectAuthService', () => { expect(mockOAuthStorage.removeItem).not.toHaveBeenCalled(); }); + it('should NOT remove auth items if token becomes valid after clock resync', () => { + oauthServiceSpy.getAccessToken.and.returnValue('fake-access-token'); + oauthServiceSpy.hasValidAccessToken.and.returnValues(false, true); + + (mockOAuthStorage.removeItem as any).calls.reset(); + + oauthEvents$.next(new OAuthSuccessEvent('discovery_document_loaded')); + + expect(timeSyncServiceSpy.syncClockOffset).toHaveBeenCalled(); + expect(mockOAuthStorage.removeItem).not.toHaveBeenCalled(); + }); + it('should call syncClockOffset when the discovery document has loaded', async () => { ensureDiscoveryDocumentSpy.and.resolveTo(true); diff --git a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts index e7fde40532..f7b1e24fd4 100644 --- a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts +++ b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts @@ -250,14 +250,20 @@ export class RedirectAuthService extends AuthService { error: () => {} }); - this.oauthService.events.pipe(take(1)).subscribe(() => { - if (this.oauthService.getAccessToken() && !this.oauthService.hasValidAccessToken()) { - if (this.oauthService.showDebugInformation) { - this._oauthLogger.warn('Access token not valid. Removing all auth items from storage'); + this.oauthService.events + .pipe( + take(1), + filter(() => !!this.oauthService.getAccessToken() && !this.oauthService.hasValidAccessToken()), + switchMap(() => this._timeSyncService.syncClockOffset()) + ) + .subscribe(() => { + if (!this.oauthService.hasValidAccessToken()) { + if (this.oauthService.showDebugInformation) { + this._oauthLogger.warn('Access token not valid after clock resync. Removing all auth items from storage'); + } + this.AUTH_STORAGE_ITEMS.map((item: string) => this._oauthStorage.removeItem(item)); } - this.AUTH_STORAGE_ITEMS.map((item: string) => this._oauthStorage.removeItem(item)); - } - }); + }); this.onLogin = this.authenticated$.pipe( filter((authenticated) => authenticated),