From 195bf2faf0f052e8577cf07f4e0621838d16ff00 Mon Sep 17 00:00:00 2001 From: Rustin Date: Wed, 31 Jul 2024 20:41:00 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20change=20password=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wallet/src/apis/models.ts | 14 +++++++++ apps/wallet/src/apis/services/auth.ts | 13 ++++++-- apps/wallet/src/pages/password/create.tsx | 2 +- apps/wallet/src/pages/password/reset.tsx | 36 ++++++++++++++++------- apps/wallet/src/stores/session.ts | 33 +++++++++++++++++---- 5 files changed, 79 insertions(+), 19 deletions(-) diff --git a/apps/wallet/src/apis/models.ts b/apps/wallet/src/apis/models.ts index e3edd65..3e08dc6 100644 --- a/apps/wallet/src/apis/models.ts +++ b/apps/wallet/src/apis/models.ts @@ -384,3 +384,17 @@ export class GetMnemonicResult { export class GetPublicKeyResult { publicKeyBase64!: string } + +export class UpdateMnemonicInput { + aesKey!: string + oldMnemonicContent!: string + oldVersion!: number + newMnemonicContent!: string + newVersion!: number + + public constructor(init?: Partial) { + Object.assign(this, init); + } +} + +export class UpdateMnemonicResult extends GetMnemonicResult {} diff --git a/apps/wallet/src/apis/services/auth.ts b/apps/wallet/src/apis/services/auth.ts index 6b6c8a3..0147bf2 100644 --- a/apps/wallet/src/apis/services/auth.ts +++ b/apps/wallet/src/apis/services/auth.ts @@ -1,5 +1,5 @@ import { sendAuthRequest } from ".."; -import { CreateMnemonicInput, GetMnemonicInput, GetMnemonicResult, GetPublicKeyResult } from "../models"; +import { CreateMnemonicInput, GetMnemonicInput, GetMnemonicResult, GetPublicKeyResult, UpdateMnemonicInput, UpdateMnemonicResult } from "../models"; export const CreateMnemonicAsync = async (input: CreateMnemonicInput) => { await sendAuthRequest( @@ -26,7 +26,16 @@ export const DeleteMnemonicAsync = async () => { export const GetPublicKeyAsync = async () => { const result = await sendAuthRequest( null, - '/api/app/mnemonic/get' + '/api/app/mnemonic/public-key', + 'GET', + ); + return result +} + +export const UpdateMnemonicAsync = async (input: UpdateMnemonicInput) => { + const result = await sendAuthRequest( + input, + '/api/app/mnemonic/update' ); return result } diff --git a/apps/wallet/src/pages/password/create.tsx b/apps/wallet/src/pages/password/create.tsx index 9949765..bd3cd22 100644 --- a/apps/wallet/src/pages/password/create.tsx +++ b/apps/wallet/src/pages/password/create.tsx @@ -42,7 +42,7 @@ const CreatePasswordPage: FC = observer(() => { } const pwd = MD5(`${password}${hibitIdSession.userId}`).toString() const phrase = HDNodeWallet.createRandom().mnemonic!.phrase - const encryptedPhrase = AES.encrypt(phrase, pwd).toString(format.Hex) + const encryptedPhrase = AES.encrypt(phrase, pwd).toString() await CreateMnemonicAsync(new CreateMnemonicInput({ aesKey: '', // TODO: aesKey mnemonicContent: encryptedPhrase, diff --git a/apps/wallet/src/pages/password/reset.tsx b/apps/wallet/src/pages/password/reset.tsx index 1d2b356..165eafe 100644 --- a/apps/wallet/src/pages/password/reset.tsx +++ b/apps/wallet/src/pages/password/reset.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect } from "react"; +import { FC } from "react"; import SvgGo from '../../assets/right-arrow.svg?react' import { useNavigate } from "react-router-dom"; import { object, string, ref } from 'yup' @@ -9,6 +9,9 @@ import { observer } from "mobx-react"; import { useMutation } from "@tanstack/react-query"; import toaster from "../../components/Toaster"; import LoaderButton from "../../components/LoaderButton"; +import hibitIdSession from "../../stores/session"; +import { getErrorMessage, HibitIDError, HibitIDErrorCode } from "../../utils/error-code"; +import { useTranslation } from "react-i18next"; const formSchema = object({ password: string() @@ -23,7 +26,9 @@ const formSchema = object({ const ResetPasswordPage: FC = observer(() => { const navigate = useNavigate() + const { t } = useTranslation() const { + setError, register, handleSubmit, formState: { errors }, @@ -33,20 +38,29 @@ const ResetPasswordPage: FC = observer(() => { }) const submitMutation = useMutation({ - mutationFn: async () => { - // TODO: - await new Promise((resolve) => { - setTimeout(() => { - resolve(true) - }, 2000) - }) + mutationFn: async ({ oldPassword, newPassword }: { + oldPassword: string + newPassword: string + }) => { + try { + await hibitIdSession.updatePassword(oldPassword, newPassword) + toaster.success('Password changed') + navigate('/') + } catch (e) { + if (e instanceof HibitIDError && e.code === HibitIDErrorCode.INVALID_PASSWORD) { + setError('password', { message: 'Password incorrect' }) + } else { + toaster.error(getErrorMessage(e, t)) + } + } } }) const handleConfirm = handleSubmit(async (values) => { - await submitMutation.mutateAsync() - toaster.success('Password changed') - navigate('/') + await submitMutation.mutateAsync({ + oldPassword: values.password, + newPassword: values.newPassword + }) }) return ( diff --git a/apps/wallet/src/stores/session.ts b/apps/wallet/src/stores/session.ts index 3f03f8c..7571af3 100644 --- a/apps/wallet/src/stores/session.ts +++ b/apps/wallet/src/stores/session.ts @@ -9,10 +9,10 @@ import rpcManager from "./rpc"; import { WalletAccount } from "@deland-labs/hibit-id-sdk"; import { TonChainWallet } from "../utils/chain/chain-wallets/ton"; import { Oidc } from "oidc-spa/oidc"; -import { GetMnemonicAsync } from "../apis/services/auth"; +import { GetMnemonicAsync, UpdateMnemonicAsync } from "../apis/services/auth"; import { HibitIDError, HibitIDErrorCode } from "../utils/error-code"; -import { GetMnemonicInput, GetMnemonicResult } from "../apis/models"; -import { AES, enc } from "crypto-js"; +import { GetMnemonicInput, GetMnemonicResult, UpdateMnemonicInput } from "../apis/models"; +import { AES, enc, MD5 } from "crypto-js"; export class HibitIdSession { public wallet: ChainWallet | null = null @@ -137,12 +137,35 @@ export class HibitIdSession { this._mnemonic = mnemonicRes } + public updatePassword = async (oldPasswordRaw: string, newPasswordRaw: string) => { + if (!this._mnemonic?.mnemonicContent) { + throw new HibitIDError(HibitIDErrorCode.MNEMONIC_NOT_CREATED) + } + if (!this._password) { + throw new HibitIDError(HibitIDErrorCode.WALLET_LOCKED) + } + const oldPwd = MD5(`${oldPasswordRaw}${this.userId}`).toString() + if (oldPwd !== this._password) { + throw new HibitIDError(HibitIDErrorCode.INVALID_PASSWORD) + } + const newPwd = MD5(`${newPasswordRaw}${this.userId}`).toString() + const phrase = AES.decrypt(this._mnemonic.mnemonicContent, oldPwd).toString(enc.Utf8); + const encryptedContent = AES.encrypt(phrase, newPwd).toString() + await UpdateMnemonicAsync(new UpdateMnemonicInput({ + aesKey: '', // TODO: + oldMnemonicContent: this._mnemonic.mnemonicContent, + oldVersion: 0, // TODO: + newMnemonicContent: encryptedContent, + newVersion: 0, // TODO: + })) + await this.fetchMnemonic() + } + private initWallet = async (chainInfo: ChainInfo, password: string): Promise => { if (!this._mnemonic?.id || !this._mnemonic?.mnemonicContent) { throw new HibitIDError(HibitIDErrorCode.MNEMONIC_NOT_CREATED) } - const utf8Content = Buffer.from(this._mnemonic.mnemonicContent, 'hex').toString('utf8') - const phrase = AES.decrypt(utf8Content, password).toString(enc.Utf8); + const phrase = AES.decrypt(this._mnemonic.mnemonicContent, password).toString(enc.Utf8); if (!phrase) { throw new HibitIDError(HibitIDErrorCode.INVALID_PASSWORD) }