recover.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { t } from "i18next";
  2. import { useEffect, useState } from "react";
  3. import { sendOtt } from "@ente/accounts/api/user";
  4. import { PAGES } from "@ente/accounts/constants/pages";
  5. import { APP_HOMES } from "@ente/shared/apps/constants";
  6. import { PageProps } from "@ente/shared/apps/types";
  7. import { VerticallyCentered } from "@ente/shared/components/Container";
  8. import FormPaper from "@ente/shared/components/Form/FormPaper";
  9. import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer";
  10. import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title";
  11. import LinkButton from "@ente/shared/components/LinkButton";
  12. import SingleInputForm, {
  13. SingleInputFormProps,
  14. } from "@ente/shared/components/SingleInputForm";
  15. import ComlinkCryptoWorker from "@ente/shared/crypto";
  16. import {
  17. decryptAndStoreToken,
  18. saveKeyInSessionStore,
  19. } from "@ente/shared/crypto/helpers";
  20. import { logError } from "@ente/shared/sentry";
  21. import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
  22. import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
  23. import { SESSION_KEYS, getKey } from "@ente/shared/storage/sessionStorage";
  24. import { KeyAttributes, User } from "@ente/shared/user/types";
  25. const bip39 = require("bip39");
  26. // mobile client library only supports english.
  27. bip39.setDefaultWordlist("english");
  28. export default function Recover({ appContext, router, appName }: PageProps) {
  29. const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
  30. useEffect(() => {
  31. const user: User = getData(LS_KEYS.USER);
  32. const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
  33. const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
  34. if (!user?.email) {
  35. router.push(PAGES.ROOT);
  36. return;
  37. }
  38. if (!user?.encryptedToken && !user?.token) {
  39. sendOtt(appName, user.email);
  40. InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.RECOVER);
  41. router.push(PAGES.VERIFY);
  42. return;
  43. }
  44. if (!keyAttributes) {
  45. router.push(PAGES.GENERATE);
  46. } else if (key) {
  47. router.push(APP_HOMES.get(appName));
  48. } else {
  49. setKeyAttributes(keyAttributes);
  50. }
  51. appContext.showNavBar(true);
  52. }, []);
  53. const recover: SingleInputFormProps["callback"] = async (
  54. recoveryKey: string,
  55. setFieldError,
  56. ) => {
  57. try {
  58. recoveryKey = recoveryKey
  59. .trim()
  60. .split(" ")
  61. .map((part) => part.trim())
  62. .filter((part) => !!part)
  63. .join(" ");
  64. // check if user is entering mnemonic recovery key
  65. if (recoveryKey.indexOf(" ") > 0) {
  66. if (recoveryKey.split(" ").length !== 24) {
  67. throw new Error("recovery code should have 24 words");
  68. }
  69. recoveryKey = bip39.mnemonicToEntropy(recoveryKey);
  70. }
  71. const cryptoWorker = await ComlinkCryptoWorker.getInstance();
  72. const masterKey = await cryptoWorker.decryptB64(
  73. keyAttributes.masterKeyEncryptedWithRecoveryKey,
  74. keyAttributes.masterKeyDecryptionNonce,
  75. await cryptoWorker.fromHex(recoveryKey),
  76. );
  77. await saveKeyInSessionStore(SESSION_KEYS.ENCRYPTION_KEY, masterKey);
  78. await decryptAndStoreToken(keyAttributes, masterKey);
  79. setData(LS_KEYS.SHOW_BACK_BUTTON, { value: false });
  80. router.push(PAGES.CHANGE_PASSWORD);
  81. } catch (e) {
  82. logError(e, "password recovery failed");
  83. setFieldError(t("INCORRECT_RECOVERY_KEY"));
  84. }
  85. };
  86. const showNoRecoveryKeyMessage = () => {
  87. appContext.setDialogBoxAttributesV2({
  88. title: t("SORRY"),
  89. close: {},
  90. content: t("NO_RECOVERY_KEY_MESSAGE"),
  91. });
  92. };
  93. return (
  94. <VerticallyCentered>
  95. <FormPaper>
  96. <FormPaperTitle>{t("RECOVER_ACCOUNT")}</FormPaperTitle>
  97. <SingleInputForm
  98. callback={recover}
  99. fieldType="text"
  100. placeholder={t("RECOVERY_KEY_HINT")}
  101. buttonText={t("RECOVER")}
  102. disableAutoComplete
  103. />
  104. <FormPaperFooter style={{ justifyContent: "space-between" }}>
  105. <LinkButton onClick={showNoRecoveryKeyMessage}>
  106. {t("NO_RECOVERY_KEY")}
  107. </LinkButton>
  108. <LinkButton onClick={router.back}>
  109. {t("GO_BACK")}
  110. </LinkButton>
  111. </FormPaperFooter>
  112. </FormPaper>
  113. </VerticallyCentered>
  114. );
  115. }