From 326f7d647e6edb891a0a1124c95be0de303ae1ae Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 21 Jun 2024 12:56:54 +0530 Subject: [PATCH] validity --- web/packages/accounts/services/session.ts | 81 ++++++++++++----------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/web/packages/accounts/services/session.ts b/web/packages/accounts/services/session.ts index 0688f69fad..7d47ce452c 100644 --- a/web/packages/accounts/services/session.ts +++ b/web/packages/accounts/services/session.ts @@ -1,7 +1,12 @@ import { authenticatedRequestHeaders } from "@/next/http"; import { apiOrigin } from "@ente/shared/network/api"; +import { LS_KEYS } from "@ente/shared/storage/localStorage"; import type { KeyAttributes } from "@ente/shared/user/types"; +type SessionValidity = + | { status: "invalid" } + | { status: "valid"; updatedKeyAttributes: KeyAttributes | undefined }; + /** * [Note: Handle password changes] * @@ -9,9 +14,9 @@ import type { KeyAttributes } from "@ente/shared/user/types"; * update our local state so that we use the latest password for verification. * * There is a straightforward way of doing this by always making a blocking API - * call before showing this page, however that would add latency to the 99% user - * experience (of normal unlocks) for the 1% case (they've changed their - * password elsewhere). + * call before showing the password unlock page, however that would add latency + * to the 99% user experience (of normal unlocks) for the 1% case (they've + * changed their password elsewhere). * * Another alternative would be to non-blockingly check if their password has * changed (e.g. by comparing the remote and local SRP attributes), and if so, @@ -35,46 +40,48 @@ import type { KeyAttributes } from "@ente/shared/user/types"; * It does not take any parameters because it reads the current state (key * attributes) from local storage. * - * @returns true if the session is valid, false if the session is invalid, and - * the (new) remote {@link KeyAttributes} if they've changed. - * - * @throws Exceptions if something goes wrong (it doesn't attempt to swallow - * failures, it is upto the caller to decide how to deal with failures in - * determining session validity). + * @returns status "invalid" if the current token has been invalidated, "valid" + * otherwise. In case the {@link KeyAttributes} returned by remote are different + * from the ones we have locally, then the {@link updatedKeyAttributes} property + * will also be set alongwith the valid {@link status} in the result. */ -export const checkSessionValidity = async (): Promise< - boolean | KeyAttributes -> => { - // const user = getData(LS_KEYS.USER); - // if (!user?.email) return "invalid"; - - // try { - // const serverAttributes = await getSRPAttributes(email); - // // (Arbitrarily) compare the salt to figure out if something changed - // // (salt will always change on password changes). - // if (serverAttributes?.kekSalt !== localSRPAttributes.kekSalt) - // return true; /* password indeed did change */ - // return false; - // } catch (e) { - // // Ignore errors here. In rare cases, the stars may align and cause the - // // API calls to fail in that 1 case where the user indeed changed their - // // password, but we also don't want to start logging people out for - // // harmless transient issues like network errors. - // log.error("Failed to compare SRP attributes", e); - // return false; - // } - await getSessionValidity(); - return true; -}; - -const getSessionValidity = async () => { +export const checkSessionValidity = async (): Promise => { const url = `${apiOrigin()}/users/session-validity/v2`; const res = await fetch(url, { headers: authenticatedRequestHeaders(), }); if (!res.ok) { - if (res.status == 401) return false; /* session is no longer valid */ + if (res.status == 401) + return { status: "invalid" }; /* session is no longer valid */ else throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`); } - return true; + // See if the response contains keyAttributes (they might not for older + // deployments). + const json = await res.json(); + if ( + "keyAttributes" in json && + typeof json.keyAttributes == "object" && + json.keyAttributes !== null + ) { + // Assume it is a `KeyAttributes`. + // + // Enhancement: Convert this to a zod validation. + const remoteKeyAttributes = json.keyAttributes as KeyAttributes; + // See if it is different from the one we have locally (if we have + // something locally). + const localKeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); + if (localKeyAttributes) { + // The kekSalt will be different if the key attributes change. + if (remoteKeyAttributes.kekSalt != localKeyAttributes.kekSalt) { + // The token is still valid, but the key attributes have + // changed. + return { + status: "valid", + updatedKeyAttributes: remoteKeyAttributes, + }; + } + } + } + // The token is still valid, but AFAWK, the key attributes are still the same. + return { status: "valid" }; };