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.
This commit is contained in:
copilot-swe-agent[bot]
2026-07-03 17:27:39 +00:00
committed by GitHub
parent 3424c53da5
commit 41c0bdc346
2 changed files with 25 additions and 7 deletions
@@ -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);
@@ -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),