This commit is contained in:
Manav Rathi
2025-07-03 07:42:08 +05:30
parent ef7ff0b186
commit 21fb4e6a03
3 changed files with 49 additions and 41 deletions

View File

@@ -1,7 +1,11 @@
import { VerifyMasterPasswordForm } from "ente-accounts/components/VerifyMasterPasswordForm";
import { getData } from "ente-accounts/services/accounts-db";
import { checkSessionValidity } from "ente-accounts/services/session";
import type { KeyAttributes, User } from "ente-accounts/services/user";
import {
ensureLocalUser,
ensureSavedKeyAttributes,
type KeyAttributes,
type LocalUser,
} from "ente-accounts/services/user";
import {
TitledMiniDialog,
type MiniDialogAttributes,
@@ -30,9 +34,11 @@ export const AuthenticateUser: React.FC<AuthenticateUserProps> = ({
onClose,
onAuthenticate,
}) => {
const { logout, showMiniDialog, onGenericError } = useBaseContext();
const [user, setUser] = useState<User>();
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
const { logout, showMiniDialog } = useBaseContext();
const [user, setUser] = useState<LocalUser | undefined>();
const [keyAttributes, setKeyAttributes] = useState<
KeyAttributes | undefined
>(undefined);
// This is a altered version of the check we do on the password verification
// screen, except here it don't try to overwrite local state and instead
@@ -56,30 +62,8 @@ export const AuthenticateUser: React.FC<AuthenticateUserProps> = ({
}, [logout, showMiniDialog]);
useEffect(() => {
const main = async () => {
try {
const user = getData("user");
if (!user) {
throw Error("User not found");
}
setUser(user);
const keyAttributes = getData("keyAttributes");
if (
(!user?.token && !user?.encryptedToken) ||
(keyAttributes && !keyAttributes.memLimit)
) {
throw Error("User not logged in");
} else if (!keyAttributes) {
throw Error("Key attributes not found");
} else {
setKeyAttributes(keyAttributes);
}
} catch (e) {
onClose();
onGenericError(e);
}
};
main();
setUser(ensureLocalUser());
setKeyAttributes(ensureSavedKeyAttributes());
}, []);
useEffect(() => {
@@ -88,6 +72,9 @@ export const AuthenticateUser: React.FC<AuthenticateUserProps> = ({
if (open) void validateSession();
}, [open, validateSession]);
// They'll be read from disk shortly.
if (!user && !keyAttributes) return <></>;
return (
<TitledMiniDialog
open={open}
@@ -96,7 +83,7 @@ export const AuthenticateUser: React.FC<AuthenticateUserProps> = ({
title={t("password")}
>
<VerifyMasterPasswordForm
user={user}
userEmail={user.email}
keyAttributes={keyAttributes}
submitButtonTitle={t("authenticate")}
onVerify={() => {

View File

@@ -3,7 +3,7 @@ import {
srpVerificationUnauthorizedErrorMessage,
type SRPAttributes,
} from "ente-accounts/services/srp";
import type { KeyAttributes, User } from "ente-accounts/services/user";
import type { KeyAttributes } from "ente-accounts/services/user";
import { LoadingButton } from "ente-base/components/mui/LoadingButton";
import { ShowHidePasswordInputAdornment } from "ente-base/components/mui/PasswordInputAdornment";
import { decryptBox, deriveKey } from "ente-base/crypto";
@@ -15,9 +15,9 @@ import { twoFactorEnabledErrorMessage } from "./utils/second-factor-choice";
export interface VerifyMasterPasswordFormProps {
/**
* The user whose password we're trying to verify.
* The email of the user whose password we're trying to verify.
*/
user: User | undefined;
userEmail: string;
/**
* The user's key attributes.
*/
@@ -45,7 +45,7 @@ export interface VerifyMasterPasswordFormProps {
*/
srpAttributes?: SRPAttributes;
/**
* The title of the submit button no the form.
* The title of the submit button on the form.
*/
submitButtonTitle: string;
/**
@@ -76,11 +76,16 @@ export interface VerifyMasterPasswordFormProps {
/**
* A form with a text field that can be used to ask the user to verify their
* password.
*
* We use it both during the initial authentication (the "/credentials" page,
* shown when logging in, or reopening the web app in a new tab), and when the
* user is trying to perform a sensitive action when already logged in and
* having a session (the {@link AuthenticateUser} component).
*/
export const VerifyMasterPasswordForm: React.FC<
VerifyMasterPasswordFormProps
> = ({
user,
userEmail,
keyAttributes,
srpAttributes,
getKeyAttributes,
@@ -196,7 +201,7 @@ export const VerifyMasterPasswordForm: React.FC<
name="email"
autoComplete="username"
type="email"
value={user?.email}
value={userEmail}
/>
<TextField
name="password"

View File

@@ -64,6 +64,14 @@ import { useCallback, useEffect, useState } from "react";
/**
* A page that allows the user to authenticate using their password.
*
* It is shown in two cases:
*
* - Initial authentication, when the user is logging in on to a new client.
*
* - Subsequent reauthentication, when the user opens the web app in a new tab.
* Such a tab won't have the user's master key in session storage, so we ask
* the user to reauthenticate using their password.
*/
const Page: React.FC = () => {
const { logout, showMiniDialog } = useBaseContext();
@@ -286,6 +294,12 @@ const Page: React.FC = () => {
void router.push(unstashRedirect() ?? appHomeRoute);
};
const userEmail = user?.email;
if (!userEmail) {
return <LoadingIndicator />;
}
if (!keyAttributes && !srpAttributes) {
return <LoadingIndicator />;
}
@@ -320,12 +334,14 @@ const Page: React.FC = () => {
// possibility using types.
return (
<AccountsPageContents>
<PasswordHeader caption={user?.email} />
<PasswordHeader caption={userEmail} />
<VerifyMasterPasswordForm
user={user}
keyAttributes={keyAttributes}
getKeyAttributes={getKeyAttributes}
srpAttributes={srpAttributes}
{...{
userEmail,
keyAttributes,
getKeyAttributes,
srpAttributes,
}}
submitButtonTitle={t("sign_in")}
onVerify={handleVerifyMasterPassword}
/>