diff --git a/web/packages/accounts/services/user.ts b/web/packages/accounts/services/user.ts index 6f5d55f85e..f02eabf71c 100644 --- a/web/packages/accounts/services/user.ts +++ b/web/packages/accounts/services/user.ts @@ -61,13 +61,6 @@ export interface UpdatedKey { opsLimit: number; } -export interface RecoveryKey { - masterKeyEncryptedWithRecoveryKey: string; - masterKeyDecryptionNonce: string; - recoveryKeyEncryptedWithMasterKey: string; - recoveryKeyDecryptionNonce: string; -} - /** * Ask remote to send a OTP / OTT to the given email to verify that the user has * access to it. Subsequent the app will pass this OTT back via the @@ -311,10 +304,28 @@ export const enableTwoFactor = async (req: EnableTwoFactorRequest) => }), ); -export const setRecoveryKey = async (token: string, recoveryKey: RecoveryKey) => - HTTPService.put( - await apiURL("/users/recovery-key"), - recoveryKey, - undefined, - { "X-Auth-Token": token }, +export interface RecoveryKeyAttributes { + masterKeyEncryptedWithRecoveryKey: string; + masterKeyDecryptionNonce: string; + recoveryKeyEncryptedWithMasterKey: string; + recoveryKeyDecryptionNonce: string; +} + +/** + * Update the encrypted recovery key attributes for the logged in user. + * + * In practice, this is not expected to be called and is meant as a rare + * fallback for very old accounts created prior to recovery key related + * attributes being assigned on account setup. Even for these, it'll be called + * only once. + */ +export const putUserRecoveryKeyAttributes = async ( + recoveryKeyAttributes: RecoveryKeyAttributes, +) => + ensureOk( + await fetch(await apiURL("/users/recovery-key"), { + method: "PUT", + headers: await authenticatedRequestHeaders(), + body: JSON.stringify(recoveryKeyAttributes), + }), ); diff --git a/web/packages/shared/crypto/helpers.ts b/web/packages/shared/crypto/helpers.ts index 7d9cdd66b3..e57ae944d7 100644 --- a/web/packages/shared/crypto/helpers.ts +++ b/web/packages/shared/crypto/helpers.ts @@ -1,9 +1,8 @@ -import { setRecoveryKey } from "ente-accounts/services/user"; +import { putUserRecoveryKeyAttributes } from "ente-accounts/services/user"; import { sharedCryptoWorker } from "ente-base/crypto"; import log from "ente-base/log"; import { masterKeyFromSession } from "ente-base/session"; import { getData, setData, setLSUser } from "ente-shared/storage/localStorage"; -import { getToken } from "ente-shared/storage/localStorage/helpers"; import { type SessionKey, setKey } from "ente-shared/storage/sessionStorage"; import { getActualKey } from "ente-shared/user"; import type { KeyAttributes } from "ente-shared/user/types"; @@ -141,10 +140,16 @@ export const getRecoveryKey = async () => { } }; -// Used only for legacy users for whom we did not generate recovery keys during -// sign up -// @returns a new base64 encoded recovery key. -async function createNewRecoveryKey() { +/** + * Generate a new recovery key, tell remote about it, update our local state, + * and then return it. + * + * This function will be used only for legacy users for whom we did not generate + * recovery keys during sign up. + * + * @returns a new base64 encoded recovery key. + */ +const createNewRecoveryKey = async () => { const masterKey = await getActualKey(); const existingAttributes = getData("keyAttributes"); @@ -159,22 +164,23 @@ async function createNewRecoveryKey() { recoveryKey, masterKey, ); + const recoveryKeyAttributes = { masterKeyEncryptedWithRecoveryKey: encryptedMasterKey.encryptedData, masterKeyDecryptionNonce: encryptedMasterKey.nonce, recoveryKeyEncryptedWithMasterKey: encryptedRecoveryKey.encryptedData, recoveryKeyDecryptionNonce: encryptedRecoveryKey.nonce, }; - await setRecoveryKey(getToken(), recoveryKeyAttributes); - const updatedKeyAttributes = Object.assign( - existingAttributes, - recoveryKeyAttributes, - ); - setData("keyAttributes", updatedKeyAttributes); + await putUserRecoveryKeyAttributes(recoveryKeyAttributes); + + setData("keyAttributes", { + ...existingAttributes, + ...recoveryKeyAttributes, + }); return recoveryKey; -} +}; /** * Decrypt the {@link encryptedChallenge} sent by remote during the delete