mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2940 from appwrite/fix/fingerprint-server-time-sync
fix: sync fingerprint timestamp with server clock
This commit is contained in:
@@ -3,6 +3,29 @@ import { env } from '$env/dynamic/public';
|
||||
const SECRET = env.PUBLIC_CONSOLE_FINGERPRINT_KEY ?? '';
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
||||
|
||||
/** Cached server timestamp and the local time it was fetched at, for interpolation. */
|
||||
let serverTimeCache: { serverSecs: number; fetchedAtMs: number } | null = null;
|
||||
|
||||
/**
|
||||
* Cache the server's clock so fingerprint timestamps always align with the
|
||||
* backend's clock, regardless of local clock drift.
|
||||
*
|
||||
* @param serverTimeSecs - the server's unix timestamp in seconds
|
||||
* (e.g. parsed from a response Date header)
|
||||
*/
|
||||
export function syncServerTime(serverTimeSecs: number): void {
|
||||
if (serverTimeCache) return;
|
||||
serverTimeCache = { serverSecs: serverTimeSecs, fetchedAtMs: Date.now() };
|
||||
}
|
||||
|
||||
function getServerTimestamp(): number {
|
||||
if (!serverTimeCache) {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
const elapsedSecs = Math.floor((Date.now() - serverTimeCache.fetchedAtMs) / 1000);
|
||||
return serverTimeCache.serverSecs + elapsedSecs;
|
||||
}
|
||||
|
||||
async function sha256(message: string): Promise<string> {
|
||||
if (!crypto?.subtle) {
|
||||
console.warn('crypto.subtle unavailable, fingerprinting disabled');
|
||||
@@ -204,7 +227,7 @@ export async function generateFingerprintToken(): Promise<string> {
|
||||
|
||||
const signals: BrowserSignals = {
|
||||
...staticSignals,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
timestamp: getServerTimestamp()
|
||||
};
|
||||
|
||||
const payload = JSON.stringify(signals);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Platform, Query } from '@appwrite.io/console';
|
||||
import { makePlansMap } from '$lib/helpers/billing';
|
||||
import { plansInfo as plansInfoStore } from '$lib/stores/billing';
|
||||
import { normalizeConsoleVariables } from '$lib/helpers/domains';
|
||||
import { syncServerTime } from '$lib/helpers/fingerprint';
|
||||
|
||||
export const load: LayoutLoad = async ({ depends, parent }) => {
|
||||
const { organizations, plansInfo } = await parent();
|
||||
@@ -28,7 +29,14 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
|
||||
plansArrayPromise,
|
||||
fetch(`${endpoint}/health/version`, {
|
||||
headers: { 'X-Appwrite-Project': project as string }
|
||||
}).then((response) => response.json() as { version?: string }),
|
||||
}).then((response) => {
|
||||
const dateHeader = response.headers.get('Date');
|
||||
const parsed = dateHeader ? new Date(dateHeader).getTime() : NaN;
|
||||
if (Number.isFinite(parsed)) {
|
||||
syncServerTime(Math.floor(parsed / 1000));
|
||||
}
|
||||
return response.json() as { version?: string };
|
||||
}),
|
||||
sdk.forConsole.console.variables()
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user