diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 72394087bb..dc7d9d3092 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -44,7 +44,7 @@ import type { KeyAttributes, User } from "@ente/shared/user/types"; import { Stack } from "@mui/material"; import { t } from "i18next"; import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { getSRPAttributes } from "../api/srp"; import { PAGES } from "../constants/pages"; import { @@ -72,8 +72,10 @@ const Page: React.FC = ({ appContext }) => { const router = useRouter(); - const showSessionExpiredDialog = () => - setDialogBoxAttributesV2(sessionExpiredDialogAttributes(logout)); + const showSessionExpiredDialog = useCallback( + () => setDialogBoxAttributesV2(sessionExpiredDialogAttributes(logout)), + [setDialogBoxAttributesV2, logout], + ); useEffect(() => { const main = async () => { @@ -114,13 +116,10 @@ const Page: React.FC = ({ appContext }) => { LS_KEYS.SRP_ATTRIBUTES, ); - if (srpAttributes) { - const email = user.email; - if (email) { - void didPasswordChangeElsewhere(email, srpAttributes).then( - (changed) => changed && showSessionExpiredDialog(), - ); - } + if (srpAttributes && user?.email) { + void didPasswordChangeElsewhere(user.email, srpAttributes).then( + (changed) => changed && showSessionExpiredDialog(), + ); } if (kekEncryptedAttributes && keyAttributes) { @@ -280,6 +279,22 @@ const Page: React.FC = ({ appContext }) => { } }; + const handleIncorrectPassword = useCallback(() => { + // We've already checked this when the page was opened. But the user + // might've had a tab open from earlier and switch back to it after + // changing their password, and then try to enter their new password + // there. In those cases, the page-load version of this check wouldn't + // get to run in the new changed condition. + // + // To cover such cases, we redo the check whenever an incorrect password + // is entered. + if (srpAttributes && user?.email) { + void didPasswordChangeElsewhere(user.email, srpAttributes).then( + (changed) => changed && showSessionExpiredDialog(), + ); + } + }, [srpAttributes, user, showSessionExpiredDialog]); + if (!keyAttributes && !srpAttributes) { return ( @@ -332,6 +347,7 @@ const Page: React.FC = ({ appContext }) => { keyAttributes={keyAttributes} getKeyAttributes={getKeyAttributes} srpAttributes={srpAttributes} + onIncorrectPassword={handleIncorrectPassword} /> diff --git a/web/packages/shared/components/VerifyMasterPasswordForm.tsx b/web/packages/shared/components/VerifyMasterPasswordForm.tsx index b1ec1bbf44..473918b832 100644 --- a/web/packages/shared/components/VerifyMasterPasswordForm.tsx +++ b/web/packages/shared/components/VerifyMasterPasswordForm.tsx @@ -29,6 +29,12 @@ export interface VerifyMasterPasswordFormProps { */ getKeyAttributes?: (kek: string) => Promise; srpAttributes?: SRPAttributes; + /** + * Called when the user enters an incorrect password. + * + * Optional. + */ + onIncorrectPassword?: () => void; } export default function VerifyMasterPasswordForm({ @@ -39,6 +45,7 @@ export default function VerifyMasterPasswordForm({ buttonText, submitButtonProps, getKeyAttributes, + onIncorrectPassword, }: VerifyMasterPasswordFormProps) { const verifyPassphrase: SingleInputFormProps["callback"] = async ( passphrase, @@ -98,6 +105,7 @@ export default function VerifyMasterPasswordForm({ break; case CustomError.INCORRECT_PASSWORD: setFieldError(t("INCORRECT_PASSPHRASE")); + onIncorrectPassword && onIncorrectPassword(); break; default: setFieldError(`${t("UNKNOWN_ERROR")} ${e.message}`);