From 4c2de33cf1fdad354150243ea62330d5df28ed26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 09:48:35 +0000 Subject: [PATCH] feat(core/auth): add TimeSyncDateTimeProvider for angular-oauth2-oidc clock drift correction --- lib/core/src/lib/auth/oidc/auth.module.ts | 4 +- lib/core/src/lib/auth/oidc/public-api.ts | 1 + .../oidc/time-sync-date-time-provider.spec.ts | 73 +++++++++++++++++++ .../auth/oidc/time-sync-date-time-provider.ts | 40 ++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 lib/core/src/lib/auth/oidc/time-sync-date-time-provider.spec.ts create mode 100644 lib/core/src/lib/auth/oidc/time-sync-date-time-provider.ts diff --git a/lib/core/src/lib/auth/oidc/auth.module.ts b/lib/core/src/lib/auth/oidc/auth.module.ts index 9d767c60ad..fb10bb78e9 100644 --- a/lib/core/src/lib/auth/oidc/auth.module.ts +++ b/lib/core/src/lib/auth/oidc/auth.module.ts @@ -16,7 +16,7 @@ */ import { inject, ModuleWithProviders, NgModule, InjectionToken, provideAppInitializer, EnvironmentProviders, Provider } from '@angular/core'; -import { AUTH_CONFIG, OAuthStorage, provideOAuthClient } from 'angular-oauth2-oidc'; +import { AUTH_CONFIG, DateTimeProvider, OAuthStorage, provideOAuthClient } from 'angular-oauth2-oidc'; import { AuthenticationService } from '../services/authentication.service'; import { AuthModuleConfig, AUTH_MODULE_CONFIG } from './auth-config'; import { authConfigFactory, AuthConfigService } from './auth-config.service'; @@ -28,6 +28,7 @@ import { StorageService } from '../../common/services/storage.service'; import { provideRouter } from '@angular/router'; import { AUTH_ROUTES } from './auth.routes'; import { Authentication, AuthenticationInterceptor } from '@alfresco/adf-core/auth'; +import { TimeSyncDateTimeProvider } from './time-sync-date-time-provider'; export const JWT_STORAGE_SERVICE = new InjectionToken('JWT_STORAGE_SERVICE', { providedIn: 'root', @@ -54,6 +55,7 @@ export function provideCoreAuth(config: AuthModuleConfig = { useHash: false }): provideOAuthClient(), provideRouter(AUTH_ROUTES), { provide: OAuthStorage, useFactory: oauthStorageFactory }, + { provide: DateTimeProvider, useClass: TimeSyncDateTimeProvider }, AuthenticationService, { provide: AUTH_CONFIG, diff --git a/lib/core/src/lib/auth/oidc/public-api.ts b/lib/core/src/lib/auth/oidc/public-api.ts index a8fa4a2f6b..3b3425ef16 100644 --- a/lib/core/src/lib/auth/oidc/public-api.ts +++ b/lib/core/src/lib/auth/oidc/public-api.ts @@ -22,3 +22,4 @@ export * from './redirect-auth.service'; export * from './view/authentication-confirmation/authentication-confirmation.component'; export * from './oidc-authentication.service'; export * from './web-crypto-jwks-validation-handler'; +export * from './time-sync-date-time-provider'; diff --git a/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.spec.ts b/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.spec.ts new file mode 100644 index 0000000000..777e0d818f --- /dev/null +++ b/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.spec.ts @@ -0,0 +1,73 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestBed } from '@angular/core/testing'; +import { TimeSyncDateTimeProvider } from './time-sync-date-time-provider'; +import { TimeSyncService } from '../services/time-sync.service'; + +describe('TimeSyncDateTimeProvider', () => { + let provider: TimeSyncDateTimeProvider; + let timeSyncServiceSpy: jasmine.SpyObj; + + beforeEach(() => { + timeSyncServiceSpy = jasmine.createSpyObj('TimeSyncService', ['getCorrectedNow']); + + TestBed.configureTestingModule({ + providers: [TimeSyncDateTimeProvider, { provide: TimeSyncService, useValue: timeSyncServiceSpy }] + }); + + provider = TestBed.inject(TimeSyncDateTimeProvider); + }); + + describe('now', () => { + it('should return corrected timestamp from TimeSyncService', () => { + const correctedTime = 1728911640000; + timeSyncServiceSpy.getCorrectedNow.and.returnValue(correctedTime); + + expect(provider.now()).toBe(correctedTime); + }); + + it('should delegate to TimeSyncService.getCorrectedNow', () => { + timeSyncServiceSpy.getCorrectedNow.and.returnValue(0); + + provider.now(); + + expect(timeSyncServiceSpy.getCorrectedNow).toHaveBeenCalled(); + }); + }); + + describe('new', () => { + it('should return a Date object based on corrected timestamp', () => { + const correctedTime = 1728911640000; + timeSyncServiceSpy.getCorrectedNow.and.returnValue(correctedTime); + + const result = provider.new(); + + expect(result).toBeInstanceOf(Date); + expect(result.getTime()).toBe(correctedTime); + }); + + it('should return a Date reflecting server-synchronized time', () => { + const correctedTime = 1728911640000; // (GMT): Monday, October 14, 2024 1:14:00 PM + timeSyncServiceSpy.getCorrectedNow.and.returnValue(correctedTime); + + const result = provider.new(); + + expect(result.toISOString()).toBe('2024-10-14T13:14:00.000Z'); + }); + }); +}); diff --git a/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.ts b/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.ts new file mode 100644 index 0000000000..4e591013e8 --- /dev/null +++ b/lib/core/src/lib/auth/oidc/time-sync-date-time-provider.ts @@ -0,0 +1,40 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable, inject } from '@angular/core'; +import { DateTimeProvider } from 'angular-oauth2-oidc'; +import { TimeSyncService } from '../services/time-sync.service'; + +/** + * Custom DateTimeProvider for angular-oauth2-oidc that uses the + * TimeSyncService to provide clock-drift-corrected timestamps. + * + * This ensures token validation within the OAuth library uses the + * server-synchronized time rather than the potentially drifted local clock. + */ +@Injectable() +export class TimeSyncDateTimeProvider extends DateTimeProvider { + private readonly timeSyncService = inject(TimeSyncService); + + now(): number { + return this.timeSyncService.getCorrectedNow(); + } + + new(): Date { + return new Date(this.timeSyncService.getCorrectedNow()); + } +}