From 1953a38bac509d153d5558514ac8e3d651e3f68a Mon Sep 17 00:00:00 2001 From: Simon Heys Date: Wed, 29 Mar 2023 18:54:51 +0100 Subject: [PATCH 1/2] chore: poc --- .../src/shared/shield/backend/time.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 packages/extension/src/shared/shield/backend/time.ts diff --git a/packages/extension/src/shared/shield/backend/time.ts b/packages/extension/src/shared/shield/backend/time.ts new file mode 100644 index 000000000..a2a931b05 --- /dev/null +++ b/packages/extension/src/shared/shield/backend/time.ts @@ -0,0 +1,54 @@ +import urlJoin from "url-join" + +import { ARGENT_API_BASE_URL } from "../../api/constants" +import { fetcherWithArgentApiHeaders } from "../../api/fetcher" + +interface GetTimeResponse { + time: number +} + +export const getBackendTime = async () => { + try { + const fetcher = fetcherWithArgentApiHeaders() + const { time } = await fetcher( + urlJoin(ARGENT_API_BASE_URL, `time`), + ) + return time + } catch (error) { + throw new Error("failed to request email verification") + } +} + +let _backendTimeSkew: number + +export const getBackendTimeSkew = async () => { + if (_backendTimeSkew !== undefined) { + return _backendTimeSkew + } + const timeStart = new Date().getTime() + const backendTime = await getBackendTime() + const timeNow = new Date().getTime() + const responseTime = (timeNow - timeStart) / 2 + const backendTimeNow = backendTime * 1000 + responseTime + const backendTimeSkew = backendTimeNow - timeNow + if (_backendTimeSkew) { + console.log("Predicted backendTime", timeStart + backendTimeSkew) + console.log("Predicted backendTimeNow", timeNow + backendTimeSkew) + } + _backendTimeSkew = backendTimeSkew + console.log({ + timeStart, + backendTime: backendTime * 1000, + timeNow, + responseTime, + backendTimeNow, + backendTimeSkew, + }) + return _backendTimeSkew +} + +export const getBackendTimeNow = async () => { + const timeNow = new Date().getTime() + const backendTimeSkew = await getBackendTimeSkew() + return timeNow + backendTimeSkew +} From 3e78103b2faf21cd6256358357a6c875a00d7a92 Mon Sep 17 00:00:00 2001 From: Simon Heys Date: Thu, 30 Mar 2023 14:33:27 +0100 Subject: [PATCH 2/2] feat: implement in jwt --- .../src/shared/shield/backend/time.ts | 43 ++++++++----------- packages/extension/src/shared/shield/jwt.ts | 6 ++- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/extension/src/shared/shield/backend/time.ts b/packages/extension/src/shared/shield/backend/time.ts index a2a931b05..74541be6c 100644 --- a/packages/extension/src/shared/shield/backend/time.ts +++ b/packages/extension/src/shared/shield/backend/time.ts @@ -7,7 +7,9 @@ interface GetTimeResponse { time: number } -export const getBackendTime = async () => { +/** backend time - at the time the response was generated - in seconds (aka 'epoch') - */ + +export const getBackendTimeSeconds = async () => { try { const fetcher = fetcherWithArgentApiHeaders() const { time } = await fetcher( @@ -15,40 +17,31 @@ export const getBackendTime = async () => { ) return time } catch (error) { - throw new Error("failed to request email verification") + throw new Error("failed to request time") } } -let _backendTimeSkew: number +/** determine skew (difference) between local time and backend time */ export const getBackendTimeSkew = async () => { - if (_backendTimeSkew !== undefined) { - return _backendTimeSkew - } const timeStart = new Date().getTime() - const backendTime = await getBackendTime() + const backendTimeSeconds = await getBackendTimeSeconds() const timeNow = new Date().getTime() + /** average how long it took for one hop client -> server, or server -> client */ const responseTime = (timeNow - timeStart) / 2 - const backendTimeNow = backendTime * 1000 + responseTime + /** approximate what backend time should be right now */ + const backendTimeNow = backendTimeSeconds * 1000 + responseTime + /** determine skew (difference) between local time and backend time */ const backendTimeSkew = backendTimeNow - timeNow - if (_backendTimeSkew) { - console.log("Predicted backendTime", timeStart + backendTimeSkew) - console.log("Predicted backendTimeNow", timeNow + backendTimeSkew) - } - _backendTimeSkew = backendTimeSkew - console.log({ - timeStart, - backendTime: backendTime * 1000, - timeNow, - responseTime, - backendTimeNow, - backendTimeSkew, - }) - return _backendTimeSkew + return backendTimeSkew } -export const getBackendTimeNow = async () => { - const timeNow = new Date().getTime() +/** determine the expected backend time right now in seconds (aka 'epoch') */ + +export const getBackendTimeNowSeconds = async () => { const backendTimeSkew = await getBackendTimeSkew() - return timeNow + backendTimeSkew + const timeNow = new Date().getTime() + const backendTimeNow = timeNow + backendTimeSkew + /** conver to epoch */ + return Math.floor(backendTimeNow / 1000) } diff --git a/packages/extension/src/shared/shield/jwt.ts b/packages/extension/src/shared/shield/jwt.ts index 232f804e4..b65a56f9e 100644 --- a/packages/extension/src/shared/shield/jwt.ts +++ b/packages/extension/src/shared/shield/jwt.ts @@ -6,6 +6,7 @@ import { generateKeyPair, } from "jose" +import { getBackendTimeNowSeconds } from "./backend/time" import { idb } from "./idb" /** important that signingKey stays not 'extractable' from browser */ @@ -45,9 +46,12 @@ export const generateJwt = async () => { const publicJwk = await exportJWK(publicKey) const thumbprint = await calculateJwkThumbprint(publicJwk) + /** set issuer time from backend in case of discrepancy with local machine time */ + const backendTimeNowSeconds = await getBackendTimeNowSeconds() + const jwt = await new SignJWT({}) .setProtectedHeader({ alg, jwk: publicJwk }) - .setIssuedAt() + .setIssuedAt(backendTimeNowSeconds) .setIssuer("kid:" + thumbprint) .setExpirationTime("5m") .sign(privateKey)