Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable manualSync, commit MFA changes in batches #2600

Merged
merged 6 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"@web3-onboard/ledger": "2.3.2",
"@web3-onboard/trezor": "^2.4.2",
"@web3-onboard/walletconnect": "^2.4.7",
"@web3auth/mpc-core-kit": "^1.0.2",
"@web3auth/mpc-core-kit": "^1.1.0",
"blo": "^1.1.1",
"bn.js": "^5.2.1",
"classnames": "^2.3.1",
Expand Down
32 changes: 7 additions & 25 deletions src/components/settings/SignerAccountMFA/PasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
import { DeviceShareRecovery } from '@/hooks/wallets/mpc/recovery/DeviceShareRecovery'
import { SecurityQuestionRecovery } from '@/hooks/wallets/mpc/recovery/SecurityQuestionRecovery'
import { Typography, TextField, FormControlLabel, Checkbox, Button, Box } from '@mui/material'
import { Typography, TextField, Button, Box } from '@mui/material'
import { type Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
import { useState, useMemo } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { enableMFA } from './helper'

enum PasswordFieldNames {
oldPassword = 'oldPassword',
newPassword = 'newPassword',
confirmPassword = 'confirmPassword',
storeDeviceShare = 'storeDeviceShare',
}

type PasswordFormData = {
[PasswordFieldNames.oldPassword]: string | undefined
[PasswordFieldNames.newPassword]: string
[PasswordFieldNames.confirmPassword]: string
[PasswordFieldNames.storeDeviceShare]: boolean
}

export const PasswordForm = ({ mpcCoreKit }: { mpcCoreKit: Web3AuthMPCCoreKit }) => {
const formMethods = useForm<PasswordFormData>({
mode: 'all',
defaultValues: async () => {
const isDeviceShareStored = await new DeviceShareRecovery(mpcCoreKit).isEnabled()
return {
[PasswordFieldNames.confirmPassword]: '',
[PasswordFieldNames.oldPassword]: undefined,
[PasswordFieldNames.newPassword]: '',
[PasswordFieldNames.storeDeviceShare]: isDeviceShareStored,
}
defaultValues: {
[PasswordFieldNames.confirmPassword]: '',
[PasswordFieldNames.oldPassword]: undefined,
[PasswordFieldNames.newPassword]: '',
},
})

const { register, formState, getValues, control, handleSubmit } = formMethods
const { register, formState, getValues, handleSubmit } = formMethods

const [enablingMFA, setEnablingMFA] = useState(false)

Expand Down Expand Up @@ -100,17 +93,6 @@ export const PasswordForm = ({ mpcCoreKit }: { mpcCoreKit: Web3AuthMPCCoreKit })
})}
/>

<Controller
control={control}
name={PasswordFieldNames.storeDeviceShare}
render={({ field: { value, ...field } }) => (
<FormControlLabel
control={<Checkbox checked={value ?? false} {...field} />}
label="Do not ask for second factor on this device"
/>
)}
/>

<Button
sx={{ justifySelf: 'flex-start' }}
disabled={!formMethods.formState.isValid || enablingMFA}
Expand Down
25 changes: 2 additions & 23 deletions src/components/settings/SignerAccountMFA/helper.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { DeviceShareRecovery } from '@/hooks/wallets/mpc/recovery/DeviceShareRecovery'
import { SecurityQuestionRecovery } from '@/hooks/wallets/mpc/recovery/SecurityQuestionRecovery'
import { logError } from '@/services/exceptions'
import ErrorCodes from '@/services/exceptions/ErrorCodes'
import { asError } from '@/services/exceptions/utils'
import { getPubKeyPoint } from '@tkey-mpc/common-types'
import { type Web3AuthMPCCoreKit } from '@web3auth/mpc-core-kit'
import BN from 'bn.js'

export const isMFAEnabled = (mpcCoreKit: Web3AuthMPCCoreKit) => {
if (!mpcCoreKit) {
Expand All @@ -20,18 +17,15 @@ export const enableMFA = async (
{
newPassword,
oldPassword,
storeDeviceShare,
}: {
newPassword: string
oldPassword: string | undefined
storeDeviceShare: boolean
},
) => {
if (!mpcCoreKit) {
return
}
const securityQuestions = new SecurityQuestionRecovery(mpcCoreKit)
const deviceShareRecovery = new DeviceShareRecovery(mpcCoreKit)
try {
// 1. setup device factor with password recovery
await securityQuestions.upsertPassword(newPassword, oldPassword)
Expand All @@ -42,25 +36,10 @@ export const enableMFA = async (

if (!isMFAEnabled(mpcCoreKit)) {
// 2. enable MFA in mpcCoreKit
const recoveryFactor = await mpcCoreKit.enableMFA({})

// 3. remove the recovery factor the mpcCoreKit creates
const recoverKey = new BN(recoveryFactor, 'hex')
const recoverPubKey = getPubKeyPoint(recoverKey)
await mpcCoreKit.deleteFactor(recoverPubKey, recoverKey)
await mpcCoreKit.enableMFA({}, false)
}

const hasDeviceShare = await deviceShareRecovery.isEnabled()

if (!hasDeviceShare && storeDeviceShare) {
await deviceShareRecovery.createAndStoreDeviceFactor()
}

if (hasDeviceShare && !storeDeviceShare) {
// Switch to password recovery factor such that we can delete the device factor
await mpcCoreKit.inputFactorKey(new BN(securityQuestionFactor, 'hex'))
await deviceShareRecovery.removeDeviceFactor()
}
await mpcCoreKit.commitChanges()
} catch (e) {
const error = asError(e)
logError(ErrorCodes._304, error.message)
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,7 @@ describe('useMPCWallet', () => {
} as unknown as UserInfo) as unknown as Web3AuthMPCCoreKit,
)

// TODO: remove unnecessary cast if mpc core sdk gets updated
jest.spyOn(mpcCoreKit, 'getWebBrowserFactor').mockReturnValue(Promise.resolve(undefined as unknown as string))
jest.spyOn(mpcCoreKit, 'getWebBrowserFactor').mockReturnValue(Promise.resolve(undefined))
jest.spyOn(mpcCoreKit, 'TssSecurityQuestion').mockReturnValue({
getQuestion: () => 'SOME RANDOM QUESTION',
} as unknown as TssSecurityQuestion)
Expand Down
12 changes: 10 additions & 2 deletions src/hooks/wallets/mpc/recovery/DeviceShareRecovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ export class DeviceShareRecovery {

async removeDeviceFactor() {
const deviceFactor = await getWebBrowserFactor(this.mpcCoreKit)
if (!deviceFactor) {
// No device factor exists. Nothing to do
return
}
// Delete factor
const key = new BN(deviceFactor, 'hex')
const pubKey = getPubKeyPoint(key)
const pubKeyX = pubKey.x.toString('hex', 64)
await this.mpcCoreKit.deleteFactor(pubKey)

// Remove from local storage
const metadata = this.mpcCoreKit.tKey.getMetadata()
const tkeyPubX = metadata.pubKey.x.toString(16, 64)
const currentStorage = BrowserStorage.getInstance('mpc_corekit_store')
currentStorage.set(pubKeyX, undefined)
currentStorage.remove(tkeyPubX)
}
}
1 change: 1 addition & 0 deletions src/hooks/wallets/mpc/useMPC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const useInitMPC = () => {
uxMode: 'popup',
enableLogging: true,
chainConfig,
manualSync: true,
})

web3AuthCoreKit
Expand Down
6 changes: 5 additions & 1 deletion src/hooks/wallets/mpc/useMPCWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ export const useMPCWallet = (): MPCWalletHook => {
}
}

const finalizeLogin = () => {
const finalizeLogin = async () => {
if (!mpcCoreKit || !onboard) {
return
}

if (mpcCoreKit.status === COREKIT_STATUS.LOGGED_IN) {
await mpcCoreKit.commitChanges()

connectWallet(onboard, {
autoSelect: {
label: ONBOARD_MPC_MODULE_LABEL,
Expand All @@ -118,6 +121,7 @@ export const useMPCWallet = (): MPCWalletHook => {
if (storeDeviceShare) {
const deviceShareRecovery = new DeviceShareRecovery(mpcCoreKit)
await deviceShareRecovery.createAndStoreDeviceFactor()
await mpcCoreKit.commitChanges()
}

finalizeLogin()
Expand Down
36 changes: 18 additions & 18 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5672,22 +5672,22 @@
json-stable-stringify "^1.0.2"
loglevel "^1.8.1"

"@toruslabs/tss-client@^1.6.1-alpha.0":
version "1.6.1-alpha.0"
resolved "https://registry.yarnpkg.com/@toruslabs/tss-client/-/tss-client-1.6.1-alpha.0.tgz#876cdc3c1cae0889a29ad3ed7bf6460bf5055ee2"
integrity sha512-48nYPc/8v2LkbtgBJw3GRLM0Eb08ASFyK5i3nEDmLg1np7rDn/9TMovPckIKZNsqAb4Rwnsb6FmTNszg3UxYfw==
"@toruslabs/tss-client@^1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@toruslabs/tss-client/-/tss-client-1.7.1.tgz#018511a75a1ee6094fbe74d2b7be968b92255e97"
integrity sha512-gaVjXy/eJKv59zdWW2lnAhjumw8DMMGlGHXEOipVywCylKqS8VvQVwwo+UyevR2VXUQsNkBr9deD5TGFH0OQHQ==
dependencies:
"@toruslabs/eccrypto" "^2.1.1"
"@toruslabs/tss-lib" "^1.6.0-alpha.0"
"@toruslabs/eccrypto" "^4.0.0"
"@toruslabs/tss-lib" "^1.7.1"
bn.js "^5.2.1"
elliptic "^6.5.4"
keccak256 "^1.0.6"
socket.io-client "^4.5.1"
socket.io-client "^4.7.2"

"@toruslabs/tss-lib@^1.6.0-alpha.0":
version "1.6.0-alpha.0"
resolved "https://registry.yarnpkg.com/@toruslabs/tss-lib/-/tss-lib-1.6.0-alpha.0.tgz#3c896c3cd7cec04bc66f4bb240f65c5f4ffe4400"
integrity sha512-sCkAFRZYMDYDWDkMEo73fyz1NnPdXZTRiQWaUCywmtV8fWQBFgneq3bVZGIMAhUNYY62PD0BubW3XaV6mqMYTg==
"@toruslabs/tss-lib@^1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@toruslabs/tss-lib/-/tss-lib-1.7.1.tgz#758c62b72b41450de5df90acb42d6c2ba5df0482"
integrity sha512-kdjBO95cPq4i7RaRMkjUJFH0aiSHrMZV/A4I42oUr0FkBd7e/RYyn1e1QH1pAAyidCIKbMkwqTxgPg4nuHEcDg==

"@trezor/[email protected]":
version "1.0.2"
Expand Down Expand Up @@ -7071,10 +7071,10 @@
loglevel "^1.8.1"
ts-custom-error "^3.3.1"

"@web3auth/mpc-core-kit@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@web3auth/mpc-core-kit/-/mpc-core-kit-1.0.2.tgz#0d6ee19df4c30449d8e283532c1c4d8c8a034f5f"
integrity sha512-68qp8vwydpsZa9c7YCOSbFJ/HulgauSTMymJiNFQ0q/gssAK+rzUMi7GJq/1gWlY8DOozO9C+u5MJnt9XG3JDw==
"@web3auth/mpc-core-kit@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@web3auth/mpc-core-kit/-/mpc-core-kit-1.1.0.tgz#b122a1ad2644e19f563679ee33c8cb6a9948d1a0"
integrity sha512-T7I9BB9hrZbpY9fwYXe7l0qNbKbbLlMvU4Gl1aPAyWXrnIP+bma9qqq0pVZaAx0DFehz023Yf5zgiwt6GR2F/g==
dependencies:
"@tkey-mpc/chrome-storage" "^8.2.2"
"@tkey-mpc/common-types" "^8.2.2"
Expand All @@ -7091,8 +7091,8 @@
"@toruslabs/metadata-helpers" "^5.x"
"@toruslabs/openlogin-session-manager" "^3.0.0"
"@toruslabs/torus.js" "^11.0.5"
"@toruslabs/tss-client" "^1.6.1-alpha.0"
"@toruslabs/tss-lib" "^1.6.0-alpha.0"
"@toruslabs/tss-client" "^1.7.1"
"@toruslabs/tss-lib" "^1.7.1"
"@web3auth-mpc/ethereum-provider" "^2.3.0"
"@web3auth/base" "^7.0.1"
"@web3auth/base-provider" "^7.0.1"
Expand Down Expand Up @@ -15691,7 +15691,7 @@ smart-buffer@^4.2.0:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==

socket.io-client@^4.5.1, socket.io-client@^4.6.1, socket.io-client@^4.7.2:
socket.io-client@^4.6.1, socket.io-client@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.2.tgz#f2f13f68058bd4e40f94f2a1541f275157ff2c08"
integrity sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==
Expand Down