123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- import { useEffect, useState } from "react";
- import { t } from "i18next";
- import {
- decryptAndStoreToken,
- generateAndSaveIntermediateKeyAttributes,
- generateLoginSubKey,
- saveKeyInSessionStore,
- } from "@ente/shared/crypto/helpers";
- import {
- LS_KEYS,
- clearData,
- getData,
- setData,
- } from "@ente/shared/storage/localStorage";
- import {
- SESSION_KEYS,
- getKey,
- removeKey,
- setKey,
- } from "@ente/shared/storage/sessionStorage";
- import { PAGES } from "../constants/pages";
- import { generateSRPSetupAttributes } from "../services/srp";
- import { logoutUser } from "../services/user";
- import { VerticallyCentered } from "@ente/shared/components/Container";
- import EnteSpinner from "@ente/shared/components/EnteSpinner";
- 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 VerifyMasterPasswordForm, {
- VerifyMasterPasswordFormProps,
- } from "@ente/shared/components/VerifyMasterPasswordForm";
- import { getAccountsURL } from "@ente/shared/network/api";
- import {
- isFirstLogin,
- setIsFirstLogin,
- } from "@ente/shared/storage/localStorage/helpers";
- import { KeyAttributes, User } from "@ente/shared/user/types";
- import isElectron from "is-electron";
- import { getSRPAttributes } from "../api/srp";
- import { configureSRP, loginViaSRP } from "../services/srp";
- import { SRPAttributes } from "../types/srp";
- // import { APPS, getAppName } from '@ente/shared/apps';
- import { APP_HOMES } from "@ente/shared/apps/constants";
- import { PageProps } from "@ente/shared/apps/types";
- import ComlinkCryptoWorker from "@ente/shared/crypto";
- import { B64EncryptionResult } from "@ente/shared/crypto/types";
- import ElectronAPIs from "@ente/shared/electron";
- import { CustomError } from "@ente/shared/error";
- import { addLocalLog } from "@ente/shared/logging";
- import { logError } from "@ente/shared/sentry";
- import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
- export default function Credentials({
- appContext,
- router,
- appName,
- }: PageProps) {
- const [srpAttributes, setSrpAttributes] = useState<SRPAttributes>();
- const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
- const [user, setUser] = useState<User>();
- useEffect(() => {
- const main = async () => {
- const user: User = getData(LS_KEYS.USER);
- if (!user?.email) {
- router.push(PAGES.ROOT);
- return;
- }
- setUser(user);
- let key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
- if (!key && isElectron()) {
- try {
- key = await ElectronAPIs.getEncryptionKey();
- } catch (e) {
- logError(e, "getEncryptionKey failed");
- }
- if (key) {
- await saveKeyInSessionStore(
- SESSION_KEYS.ENCRYPTION_KEY,
- key,
- true,
- );
- }
- }
- if (key) {
- router.push(APP_HOMES.get(appName));
- return;
- }
- const kekEncryptedAttributes: B64EncryptionResult = getKey(
- SESSION_KEYS.KEY_ENCRYPTION_KEY,
- );
- const keyAttributes: KeyAttributes = getData(
- LS_KEYS.KEY_ATTRIBUTES,
- );
- if (kekEncryptedAttributes && keyAttributes) {
- removeKey(SESSION_KEYS.KEY_ENCRYPTION_KEY);
- const cryptoWorker = await ComlinkCryptoWorker.getInstance();
- const kek = await cryptoWorker.decryptB64(
- kekEncryptedAttributes.encryptedData,
- kekEncryptedAttributes.nonce,
- kekEncryptedAttributes.key,
- );
- const key = await cryptoWorker.decryptB64(
- keyAttributes.encryptedKey,
- keyAttributes.keyDecryptionNonce,
- kek,
- );
- useMasterPassword(key, kek, keyAttributes);
- return;
- }
- if (keyAttributes) {
- if (
- (!user?.token && !user?.encryptedToken) ||
- (keyAttributes && !keyAttributes.memLimit)
- ) {
- clearData();
- router.push(PAGES.ROOT);
- return;
- }
- setKeyAttributes(keyAttributes);
- return;
- }
- const srpAttributes: SRPAttributes = getData(
- LS_KEYS.SRP_ATTRIBUTES,
- );
- if (srpAttributes) {
- setSrpAttributes(srpAttributes);
- } else {
- router.push(PAGES.ROOT);
- }
- };
- main();
- appContext.showNavBar(true);
- }, []);
- const getKeyAttributes: VerifyMasterPasswordFormProps["getKeyAttributes"] =
- async (kek: string) => {
- try {
- const cryptoWorker = await ComlinkCryptoWorker.getInstance();
- const {
- keyAttributes,
- encryptedToken,
- token,
- id,
- twoFactorSessionID,
- passkeySessionID,
- } = await loginViaSRP(srpAttributes, kek);
- setIsFirstLogin(true);
- if (passkeySessionID) {
- const sessionKeyAttributes =
- await cryptoWorker.generateKeyAndEncryptToB64(kek);
- setKey(
- SESSION_KEYS.KEY_ENCRYPTION_KEY,
- sessionKeyAttributes,
- );
- const user = getData(LS_KEYS.USER);
- setData(LS_KEYS.USER, {
- ...user,
- passkeySessionID,
- isTwoFactorEnabled: true,
- isTwoFactorPasskeysEnabled: true,
- });
- InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.ROOT);
- window.location.href = `${getAccountsURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
- window.location.origin
- }/passkeys/finish`;
- return;
- } else if (twoFactorSessionID) {
- const sessionKeyAttributes =
- await cryptoWorker.generateKeyAndEncryptToB64(kek);
- setKey(
- SESSION_KEYS.KEY_ENCRYPTION_KEY,
- sessionKeyAttributes,
- );
- const user = getData(LS_KEYS.USER);
- setData(LS_KEYS.USER, {
- ...user,
- twoFactorSessionID,
- isTwoFactorEnabled: true,
- });
- router.push(PAGES.TWO_FACTOR_VERIFY);
- throw Error(CustomError.TWO_FACTOR_ENABLED);
- } else {
- const user = getData(LS_KEYS.USER);
- setData(LS_KEYS.USER, {
- ...user,
- token,
- encryptedToken,
- id,
- isTwoFactorEnabled: false,
- });
- setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
- return keyAttributes;
- }
- } catch (e) {
- if (e.message !== CustomError.TWO_FACTOR_ENABLED) {
- logError(e, "getKeyAttributes failed");
- }
- throw e;
- }
- };
- const useMasterPassword: VerifyMasterPasswordFormProps["callback"] = async (
- key,
- kek,
- keyAttributes,
- passphrase,
- ) => {
- try {
- if (isFirstLogin() && passphrase) {
- await generateAndSaveIntermediateKeyAttributes(
- passphrase,
- keyAttributes,
- key,
- );
- }
- await saveKeyInSessionStore(SESSION_KEYS.ENCRYPTION_KEY, key);
- await decryptAndStoreToken(keyAttributes, key);
- try {
- let srpAttributes: SRPAttributes = getData(
- LS_KEYS.SRP_ATTRIBUTES,
- );
- if (!srpAttributes) {
- srpAttributes = await getSRPAttributes(user.email);
- if (srpAttributes) {
- setData(LS_KEYS.SRP_ATTRIBUTES, srpAttributes);
- }
- }
- addLocalLog(() => `userSRPSetupPending ${!srpAttributes}`);
- if (!srpAttributes) {
- const loginSubKey = await generateLoginSubKey(kek);
- const srpSetupAttributes =
- await generateSRPSetupAttributes(loginSubKey);
- await configureSRP(srpSetupAttributes);
- }
- } catch (e) {
- logError(e, "migrate to srp failed");
- }
- const redirectURL = InMemoryStore.get(MS_KEYS.REDIRECT_URL);
- InMemoryStore.delete(MS_KEYS.REDIRECT_URL);
- router.push(redirectURL ?? APP_HOMES.get(appName));
- } catch (e) {
- logError(e, "useMasterPassword failed");
- }
- };
- const redirectToRecoverPage = () => router.push(PAGES.RECOVER);
- if (!keyAttributes && !srpAttributes) {
- return (
- <VerticallyCentered>
- <EnteSpinner />
- </VerticallyCentered>
- );
- }
- return (
- <VerticallyCentered>
- <FormPaper style={{ minWidth: "320px" }}>
- <FormPaperTitle>{t("PASSWORD")}</FormPaperTitle>
- <VerifyMasterPasswordForm
- buttonText={t("VERIFY_PASSPHRASE")}
- callback={useMasterPassword}
- user={user}
- keyAttributes={keyAttributes}
- getKeyAttributes={getKeyAttributes}
- srpAttributes={srpAttributes}
- />
- <FormPaperFooter style={{ justifyContent: "space-between" }}>
- <LinkButton onClick={redirectToRecoverPage}>
- {t("FORGOT_PASSWORD")}
- </LinkButton>
- <LinkButton onClick={logoutUser}>
- {t("CHANGE_EMAIL")}
- </LinkButton>
- </FormPaperFooter>
- </FormPaper>
- </VerticallyCentered>
- );
- }
|