Skip to content

Commit 089ca52

Browse files
Merge pull request #2940 from appwrite/fix/fingerprint-server-time-sync
fix: sync fingerprint timestamp with server clock
2 parents 352fb53 + 65f29e7 commit 089ca52

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

src/lib/helpers/fingerprint.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@ import { env } from '$env/dynamic/public';
33
const SECRET = env.PUBLIC_CONSOLE_FINGERPRINT_KEY ?? '';
44
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
55

6+
/** Cached server timestamp and the local time it was fetched at, for interpolation. */
7+
let serverTimeCache: { serverSecs: number; fetchedAtMs: number } | null = null;
8+
9+
/**
10+
* Cache the server's clock so fingerprint timestamps always align with the
11+
* backend's clock, regardless of local clock drift.
12+
*
13+
* @param serverTimeSecs - the server's unix timestamp in seconds
14+
* (e.g. parsed from a response Date header)
15+
*/
16+
export function syncServerTime(serverTimeSecs: number): void {
17+
if (serverTimeCache) return;
18+
serverTimeCache = { serverSecs: serverTimeSecs, fetchedAtMs: Date.now() };
19+
}
20+
21+
function getServerTimestamp(): number {
22+
if (!serverTimeCache) {
23+
return Math.floor(Date.now() / 1000);
24+
}
25+
const elapsedSecs = Math.floor((Date.now() - serverTimeCache.fetchedAtMs) / 1000);
26+
return serverTimeCache.serverSecs + elapsedSecs;
27+
}
28+
629
async function sha256(message: string): Promise<string> {
730
if (!crypto?.subtle) {
831
console.warn('crypto.subtle unavailable, fingerprinting disabled');
@@ -204,7 +227,7 @@ export async function generateFingerprintToken(): Promise<string> {
204227

205228
const signals: BrowserSignals = {
206229
...staticSignals,
207-
timestamp: Math.floor(Date.now() / 1000)
230+
timestamp: getServerTimestamp()
208231
};
209232

210233
const payload = JSON.stringify(signals);

src/routes/(console)/+layout.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Platform, Query } from '@appwrite.io/console';
66
import { makePlansMap } from '$lib/helpers/billing';
77
import { plansInfo as plansInfoStore } from '$lib/stores/billing';
88
import { normalizeConsoleVariables } from '$lib/helpers/domains';
9+
import { syncServerTime } from '$lib/helpers/fingerprint';
910

1011
export const load: LayoutLoad = async ({ depends, parent }) => {
1112
const { organizations, plansInfo } = await parent();
@@ -28,7 +29,14 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
2829
plansArrayPromise,
2930
fetch(`${endpoint}/health/version`, {
3031
headers: { 'X-Appwrite-Project': project as string }
31-
}).then((response) => response.json() as { version?: string }),
32+
}).then((response) => {
33+
const dateHeader = response.headers.get('Date');
34+
const parsed = dateHeader ? new Date(dateHeader).getTime() : NaN;
35+
if (Number.isFinite(parsed)) {
36+
syncServerTime(Math.floor(parsed / 1000));
37+
}
38+
return response.json() as { version?: string };
39+
}),
3240
sdk.forConsole.console.variables()
3341
]);
3442

0 commit comments

Comments
 (0)