mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
web: verify encryption key during password reset
This commit is contained in:
@@ -30,7 +30,6 @@ import { ErrorText } from "../components/error-text";
|
|||||||
import { EVENTS, User } from "@notesnook/core";
|
import { EVENTS, User } from "@notesnook/core";
|
||||||
import { RecoveryKeyDialog } from "../dialogs/recovery-key-dialog";
|
import { RecoveryKeyDialog } from "../dialogs/recovery-key-dialog";
|
||||||
import { strings } from "@notesnook/intl";
|
import { strings } from "@notesnook/intl";
|
||||||
import { useKeyStore } from "../interfaces/key-store";
|
|
||||||
|
|
||||||
type RecoveryMethodType = "key" | "reset";
|
type RecoveryMethodType = "key" | "reset";
|
||||||
type RecoveryMethodsFormData = Record<string, unknown>;
|
type RecoveryMethodsFormData = Record<string, unknown>;
|
||||||
@@ -105,8 +104,8 @@ function useAuthenticateUser({
|
|||||||
code,
|
code,
|
||||||
userId
|
userId
|
||||||
}: {
|
}: {
|
||||||
code: string;
|
code?: string;
|
||||||
userId: string;
|
userId?: string;
|
||||||
}) {
|
}) {
|
||||||
const [isAuthenticating, setIsAuthenticating] = useState(true);
|
const [isAuthenticating, setIsAuthenticating] = useState(true);
|
||||||
const [user, setUser] = useState<User>();
|
const [user, setUser] = useState<User>();
|
||||||
@@ -114,6 +113,10 @@ function useAuthenticateUser({
|
|||||||
async function authenticateUser() {
|
async function authenticateUser() {
|
||||||
setIsAuthenticating(true);
|
setIsAuthenticating(true);
|
||||||
try {
|
try {
|
||||||
|
if (!code || !userId) {
|
||||||
|
throw new Error("Missing code or userId in query params.");
|
||||||
|
}
|
||||||
|
|
||||||
const accessToken = await db.tokenManager.getAccessToken();
|
const accessToken = await db.tokenManager.getAccessToken();
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
await db.tokenManager.getAccessTokenFromAuthorizationCode(
|
await db.tokenManager.getAccessTokenFromAuthorizationCode(
|
||||||
@@ -181,7 +184,7 @@ function Recovery(props: RecoveryProps) {
|
|||||||
}}
|
}}
|
||||||
variant={"body"}
|
variant={"body"}
|
||||||
>
|
>
|
||||||
{strings.authenticatedAs(user?.email)}
|
{user?.email ? strings.authenticatedAs(user.email) : ""}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@@ -334,16 +337,25 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
|||||||
subtitle: strings.keyRecoveryProgressDesc()
|
subtitle: strings.keyRecoveryProgressDesc()
|
||||||
}}
|
}}
|
||||||
onSubmit={async (form) => {
|
onSubmit={async (form) => {
|
||||||
|
const user = await db.user.getUser();
|
||||||
|
if (!user) throw new Error(strings.notLoggedIn());
|
||||||
|
|
||||||
const recoveryKey = form.recoveryKey;
|
const recoveryKey = form.recoveryKey;
|
||||||
if (recoveryKey.length < 40) {
|
if (
|
||||||
|
!(await db
|
||||||
|
.storage()
|
||||||
|
.encrypt({ key: recoveryKey, salt: user.salt }, "test")
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false))
|
||||||
|
) {
|
||||||
throw new Error(strings.invalidRecoveryKey());
|
throw new Error(strings.invalidRecoveryKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
setProgress(0);
|
await db.user.verifyEncryptionKey({
|
||||||
|
key: recoveryKey,
|
||||||
|
salt: user.salt
|
||||||
|
});
|
||||||
|
|
||||||
const user = await db.user.getUser();
|
|
||||||
if (!user) throw new Error(strings.notLoggedIn());
|
|
||||||
await useKeyStore.getState().setValue("userEncryptionKey", recoveryKey);
|
|
||||||
navigate("new", form);
|
navigate("new", form);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -365,7 +377,10 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
|||||||
>
|
>
|
||||||
{strings.back()}
|
{strings.back()}
|
||||||
</Button>
|
</Button>
|
||||||
<SubmitButton text={strings.startAccountRecovery()} sx={{ flex: 1, mt: 0 }} />
|
<SubmitButton
|
||||||
|
text={strings.startAccountRecovery()}
|
||||||
|
sx={{ flex: 1, mt: 0 }}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -405,8 +420,12 @@ function NewPassword(props: BaseRecoveryComponentProps<"new">) {
|
|||||||
subtitle: strings.resetPasswordWait()
|
subtitle: strings.resetPasswordWait()
|
||||||
}}
|
}}
|
||||||
onSubmit={async (form) => {
|
onSubmit={async (form) => {
|
||||||
try {
|
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
|
const user = await db.user.getUser();
|
||||||
|
if (!user) throw new Error(strings.notLoggedIn());
|
||||||
|
|
||||||
|
if (!formData?.recoveryKey)
|
||||||
|
throw new Error("Recovery key is required to reset password.");
|
||||||
|
|
||||||
if (form.password !== form.confirmPassword)
|
if (form.password !== form.confirmPassword)
|
||||||
throw new Error("Passwords do not match.");
|
throw new Error("Passwords do not match.");
|
||||||
@@ -414,20 +433,15 @@ function NewPassword(props: BaseRecoveryComponentProps<"new">) {
|
|||||||
if (formData?.userResetRequired && !(await db.user.resetUser()))
|
if (formData?.userResetRequired && !(await db.user.resetUser()))
|
||||||
throw new Error("Failed to reset user.");
|
throw new Error("Failed to reset user.");
|
||||||
|
|
||||||
if (!(await db.user.resetPassword(form.password)))
|
if (
|
||||||
|
!(await db.user.resetPassword({
|
||||||
|
encryptionKey: { key: formData?.recoveryKey, salt: user.salt },
|
||||||
|
newPassword: form.password
|
||||||
|
}))
|
||||||
|
)
|
||||||
throw new Error("Could not reset account password.");
|
throw new Error("Could not reset account password.");
|
||||||
|
|
||||||
navigate("final");
|
navigate("final");
|
||||||
} catch (e) {
|
|
||||||
if ((e as Error).message === "invalid input") {
|
|
||||||
console.error(e);
|
|
||||||
throw new Error(
|
|
||||||
"Password reset failed because of invalid recovery key"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(form?: NewPasswordFormData) => (
|
{(form?: NewPasswordFormData) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user