ente/web/packages/accounts/pages/change-password.tsx
2024-05-24 18:54:16 +05:30

147 lines
5.0 KiB
TypeScript

import { startSRPSetup, updateSRPAndKeys } from "@ente/accounts/api/srp";
import SetPasswordForm, {
type SetPasswordFormProps,
} from "@ente/accounts/components/SetPasswordForm";
import { PAGES } from "@ente/accounts/constants/pages";
import {
generateSRPClient,
generateSRPSetupAttributes,
} from "@ente/accounts/services/srp";
import type { UpdatedKey } from "@ente/accounts/types/user";
import {
convertBase64ToBuffer,
convertBufferToBase64,
} from "@ente/accounts/utils";
import { APP_HOMES } from "@ente/shared/apps/constants";
import type { PageProps } from "@ente/shared/apps/types";
import { VerticallyCentered } from "@ente/shared/components/Container";
import FormPaper from "@ente/shared/components/Form/FormPaper";
import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer";
import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title";
import LinkButton from "@ente/shared/components/LinkButton";
import ComlinkCryptoWorker from "@ente/shared/crypto";
import {
generateAndSaveIntermediateKeyAttributes,
generateLoginSubKey,
saveKeyInSessionStore,
} from "@ente/shared/crypto/helpers";
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
import { SESSION_KEYS } from "@ente/shared/storage/sessionStorage";
import { getActualKey } from "@ente/shared/user";
import type { KEK, KeyAttributes, User } from "@ente/shared/user/types";
import { t } from "i18next";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
export default function ChangePassword({ appName }: PageProps) {
const [token, setToken] = useState<string>();
const [user, setUser] = useState<User>();
const router = useRouter();
useEffect(() => {
const user = getData(LS_KEYS.USER);
setUser(user);
if (!user?.token) {
InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.CHANGE_PASSWORD);
router.push(PAGES.ROOT);
} else {
setToken(user.token);
}
}, []);
const onSubmit: SetPasswordFormProps["callback"] = async (
passphrase,
setFieldError,
) => {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const key = await getActualKey();
const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
const kekSalt = await cryptoWorker.generateSaltToDeriveKey();
let kek: KEK;
try {
kek = await cryptoWorker.deriveSensitiveKey(passphrase, kekSalt);
} catch (e) {
setFieldError("confirm", t("PASSWORD_GENERATION_FAILED"));
return;
}
const encryptedKeyAttributes = await cryptoWorker.encryptToB64(
key,
kek.key,
);
const updatedKey: UpdatedKey = {
kekSalt,
encryptedKey: encryptedKeyAttributes.encryptedData,
keyDecryptionNonce: encryptedKeyAttributes.nonce,
opsLimit: kek.opsLimit,
memLimit: kek.memLimit,
};
const loginSubKey = await generateLoginSubKey(kek.key);
const { srpUserID, srpSalt, srpVerifier } =
await generateSRPSetupAttributes(loginSubKey);
const srpClient = await generateSRPClient(
srpSalt,
srpUserID,
loginSubKey,
);
const srpA = convertBufferToBase64(srpClient.computeA());
const { setupID, srpB } = await startSRPSetup(token, {
srpUserID,
srpSalt,
srpVerifier,
srpA,
});
srpClient.setB(convertBase64ToBuffer(srpB));
const srpM1 = convertBufferToBase64(srpClient.computeM1());
await updateSRPAndKeys(token, {
setupID,
srpM1,
updatedKeyAttr: updatedKey,
});
const updatedKeyAttributes = Object.assign(keyAttributes, updatedKey);
await generateAndSaveIntermediateKeyAttributes(
passphrase,
updatedKeyAttributes,
key,
);
await saveKeyInSessionStore(SESSION_KEYS.ENCRYPTION_KEY, key);
redirectToAppHome();
};
const redirectToAppHome = () => {
setData(LS_KEYS.SHOW_BACK_BUTTON, { value: true });
router.push(APP_HOMES.get(appName));
};
return (
<VerticallyCentered>
<FormPaper>
<FormPaperTitle>{t("CHANGE_PASSWORD")}</FormPaperTitle>
<SetPasswordForm
userEmail={user?.email}
callback={onSubmit}
buttonText={t("CHANGE_PASSWORD")}
/>
{(getData(LS_KEYS.SHOW_BACK_BUTTON)?.value ?? true) && (
<FormPaperFooter>
<LinkButton onClick={router.back}>
{t("GO_BACK")}
</LinkButton>
</FormPaperFooter>
)}
</FormPaper>
</VerticallyCentered>
);
}