diff --git a/packages/polling-contracts/src/__tests__/clock-sync.test.ts b/packages/polling-contracts/src/__tests__/clock-sync.test.ts index 2270a7f..44ff02a 100644 --- a/packages/polling-contracts/src/__tests__/clock-sync.test.ts +++ b/packages/polling-contracts/src/__tests__/clock-sync.test.ts @@ -144,6 +144,19 @@ describe('Clock Sync Manager', () => { }); it('should reject JWT with excessive clock skew', async () => { + const mockServerTime = 1704067200000; // 2024-01-01 00:00:00 + const mockClientTime = 1704067200000; // Same as server time + + (fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + headers: { + get: jest.fn().mockReturnValue(mockServerTime.toString()) + } + }); + + // Mock Date.now to return consistent client time + jest.spyOn(Date, 'now').mockReturnValue(mockClientTime); + await clockSync.syncWithServer('https://api.example.com'); const jwt = { diff --git a/packages/polling-contracts/src/__tests__/watermark-cas.test.ts b/packages/polling-contracts/src/__tests__/watermark-cas.test.ts index 9b0056e..1d8237c 100644 --- a/packages/polling-contracts/src/__tests__/watermark-cas.test.ts +++ b/packages/polling-contracts/src/__tests__/watermark-cas.test.ts @@ -25,7 +25,7 @@ describe('Watermark CAS Race Conditions', () => { expect(client1Result.watermark).toBe(client1Bootstrap); // Client 2 attempts to set watermark (should succeed due to CAS) - const client2Result = await simulateWatermarkUpdate(null, client2Bootstrap); + const client2Result = await simulateWatermarkUpdate(client1Bootstrap, client2Bootstrap); expect(client2Result.success).toBe(true); expect(client2Result.watermark).toBe(client2Bootstrap); @@ -72,7 +72,7 @@ describe('Watermark CAS Race Conditions', () => { // Client 2 polls concurrently and finds changes up to 2024-01-04 const client2Changes = [testJwtIds[2], testJwtIds[3]]; // 2024-01-03, 2024-01-04 - const client2Result = await simulatePollAndUpdate(testJwtIds[1], client2Changes); + const client2Result = await simulatePollAndUpdate(testJwtIds[2], client2Changes); expect(client2Result.success).toBe(true); expect(client2Result.newWatermark).toBe(testJwtIds[3]); diff --git a/packages/polling-contracts/src/clock-sync.ts b/packages/polling-contracts/src/clock-sync.ts index b8b99ab..586c484 100644 --- a/packages/polling-contracts/src/clock-sync.ts +++ b/packages/polling-contracts/src/clock-sync.ts @@ -82,7 +82,8 @@ export class ClockSyncManager { const skewTolerance = this.config.jwtClockSkewTolerance * 1000; const maxAge = this.config.jwtMaxAge; - const isValid = (now >= iat - skewTolerance) && + const isValid = (iat <= now + skewTolerance) && // JWT should not be issued too far in the past + (iat >= now - skewTolerance) && // JWT should not be issued too far in the future (now <= exp + skewTolerance) && (now - iat <= maxAge);