diff --git a/apps/core/tailwind.config.ts b/apps/core/tailwind.config.ts index b9c64edf5a54a..1a4200b955943 100644 --- a/apps/core/tailwind.config.ts +++ b/apps/core/tailwind.config.ts @@ -77,6 +77,9 @@ export default { offwhite: '#fefefe', offblack: '#111111', ebony: '#101828', + avocado: { + 200: '#CBE5BE', + }, }, extend: { diff --git a/apps/wallet/src/background/accounts/index.ts b/apps/wallet/src/background/accounts/index.ts index 4bff6c5825173..50c15f9cda6f6 100644 --- a/apps/wallet/src/background/accounts/index.ts +++ b/apps/wallet/src/background/accounts/index.ts @@ -27,7 +27,7 @@ import { ImportedAccount } from './ImportedAccount'; import { LedgerAccount } from './LedgerAccount'; import { MnemonicAccount } from './MnemonicAccount'; import { QredoAccount } from './QredoAccount'; -import { ZkAccount } from './zk/ZkAccount'; +import { ZkAccount, type ZkAccountSerialized } from './zk/ZkAccount'; function toAccount(account: SerializedAccount) { if (MnemonicAccount.isOfType(account)) { @@ -362,5 +362,20 @@ export async function accountsHandleUIMessage(msg: Message, uiConnection: UiConn await uiConnection.send(createMessage({ type: 'done' }, msg.id)); return true; } + if (isMethodPayload(payload, 'acknowledgeZkLoginWarning')) { + const { accountID } = payload.args; + const account = await getAccountByID(accountID); + if (!account) { + throw new Error(`Account with id ${accountID} not found.`); + } + if (!(account instanceof ZkAccount)) { + throw new Error(`Account with id ${accountID} is not a zkLogin account.`); + } + const updates: Partial = { warningAcknowledged: true }; + await (await getDB()).accounts.update(accountID, updates); + accountsEvents.emit('accountStatusChanged', { accountID }); + await uiConnection.send(createMessage({ type: 'done' }, msg.id)); + return true; + } return false; } diff --git a/apps/wallet/src/background/accounts/zk/ZkAccount.ts b/apps/wallet/src/background/accounts/zk/ZkAccount.ts index b72a9139782c8..4e4a327a5a3e6 100644 --- a/apps/wallet/src/background/accounts/zk/ZkAccount.ts +++ b/apps/wallet/src/background/accounts/zk/ZkAccount.ts @@ -79,6 +79,7 @@ export interface ZkAccountSerialized extends SerializedAccount { * the name/key of the claim in claims used for the address sub or email */ claimName: 'sub' | 'email'; + warningAcknowledged?: boolean; } export interface ZkAccountSerializedUI extends SerializedUIAccount { @@ -86,6 +87,7 @@ export interface ZkAccountSerializedUI extends SerializedUIAccount { email: string | null; picture: string | null; provider: ZkProvider; + warningAcknowledged: boolean; } export function isZkAccountSerializedUI( @@ -174,7 +176,7 @@ export class ZkAccount } async toUISerialized(): Promise { - const { address, publicKey, type, claims, selected, provider, nickname } = + const { address, publicKey, type, claims, selected, provider, nickname, warningAcknowledged } = await this.getStoredData(); const { email, picture } = await deobfuscate(claims); return { @@ -191,6 +193,7 @@ export class ZkAccount isPasswordUnlockable: false, provider, isKeyPairExportable: false, + warningAcknowledged: !!warningAcknowledged, }; } diff --git a/apps/wallet/src/background/accounts/zk/providers.ts b/apps/wallet/src/background/accounts/zk/providers.ts index 530a450e6196e..1444ab1f47eab 100644 --- a/apps/wallet/src/background/accounts/zk/providers.ts +++ b/apps/wallet/src/background/accounts/zk/providers.ts @@ -14,6 +14,7 @@ export interface ZkProviderData { }) => void; enabled: boolean; hidden?: boolean; + mfaLink?: string; } const isDev = process.env.NODE_ENV === 'development'; @@ -35,6 +36,7 @@ export const zkProviderDataMap: Record = { } }, enabled: true, + mfaLink: 'https://support.google.com/accounts/answer/185839', }, twitch: { clientID: 'uzpfot3uotf7fp9hklsyctn2735bcw', @@ -57,6 +59,7 @@ export const zkProviderDataMap: Record = { } }, enabled: true, + mfaLink: 'https://help.twitch.tv/s/article/two-factor-authentication', }, facebook: { clientID: '829226485248571', @@ -67,5 +70,6 @@ export const zkProviderDataMap: Record = { }, enabled: isDev, hidden: !isDev, + mfaLink: 'https://www.facebook.com/help/148233965247823', }, }; diff --git a/apps/wallet/src/shared/messaging/messages/payloads/MethodPayload.ts b/apps/wallet/src/shared/messaging/messages/payloads/MethodPayload.ts index 857ff9c25e477..ae9e6f23b20b5 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/MethodPayload.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/MethodPayload.ts @@ -70,6 +70,7 @@ type MethodPayloads = { data: PasswordRecoveryData; }; removeAccount: { accountID: string }; + acknowledgeZkLoginWarning: { accountID: string }; }; type Methods = keyof MethodPayloads; diff --git a/apps/wallet/src/ui/app/background-client/index.ts b/apps/wallet/src/ui/app/background-client/index.ts index f1aa5bfb3ef38..df3c9e04198fd 100644 --- a/apps/wallet/src/ui/app/background-client/index.ts +++ b/apps/wallet/src/ui/app/background-client/index.ts @@ -564,6 +564,18 @@ export class BackgroundClient { ); } + public acknowledgeZkLoginWarning(args: MethodPayload<'acknowledgeZkLoginWarning'>['args']) { + return lastValueFrom( + this.sendMessage( + createMessage>({ + type: 'method-payload', + method: 'acknowledgeZkLoginWarning', + args, + }), + ).pipe(take(1)), + ); + } + private loadFeatures() { return lastValueFrom( this.sendMessage( diff --git a/apps/wallet/src/ui/app/components/accounts/ZkLoginAccountWaringModal.tsx b/apps/wallet/src/ui/app/components/accounts/ZkLoginAccountWaringModal.tsx new file mode 100644 index 0000000000000..d371ebdfdb014 --- /dev/null +++ b/apps/wallet/src/ui/app/components/accounts/ZkLoginAccountWaringModal.tsx @@ -0,0 +1,86 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { zkProviderDataMap, type ZkProvider } from '_src/background/accounts/zk/providers'; +import { isZkAccountSerializedUI } from '_src/background/accounts/zk/ZkAccount'; +import { type MethodPayload } from '_src/shared/messaging/messages/payloads/MethodPayload'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '_src/ui/app/shared/Dialog'; +import { useMutation } from '@tanstack/react-query'; +import toast from 'react-hot-toast'; + +import { useActiveAccount } from '../../hooks/useActiveAccount'; +import { useBackgroundClient } from '../../hooks/useBackgroundClient'; +import { Button } from '../../shared/ButtonUI'; +import { Link } from '../../shared/Link'; + +const providerToName: Record = { + google: 'Google', + facebook: 'Facebook', + twitch: 'Twitch', +}; + +export function ZkLoginAccountWarningModal() { + const activeAccount = useActiveAccount(); + const backgroundClient = useBackgroundClient(); + const warningMutation = useMutation({ + mutationKey: ['acknowledge-zk-login-warning'], + mutationFn: (args: MethodPayload<'acknowledgeZkLoginWarning'>['args']) => + backgroundClient.acknowledgeZkLoginWarning(args), + }); + if ( + activeAccount && + isZkAccountSerializedUI(activeAccount) && + !activeAccount.warningAcknowledged + ) { + const providerData = zkProviderDataMap[activeAccount.provider]; + return ( + + e.preventDefault()} background="avocado"> + + +
Turn on 2FA.
+
Protect Your Assets.
+
+
+ + Your {providerToName[activeAccount.provider]} Account now gives access to your Sui + Wallet. To help safeguard your assets, we strongly recommend you enable 2FA. + {providerData.mfaLink ? ( + <> + {' '} + + + {' '} + to find out how to set this up. + + ) : null} + + +
+ ); + } + return null; +} diff --git a/apps/wallet/src/ui/app/shared/Dialog.tsx b/apps/wallet/src/ui/app/shared/Dialog.tsx index 35f69fbd1748d..ccdcb6fe1850a 100644 --- a/apps/wallet/src/ui/app/shared/Dialog.tsx +++ b/apps/wallet/src/ui/app/shared/Dialog.tsx @@ -26,15 +26,16 @@ DialogOverlay.displayName = RadixDialog.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( + React.ComponentPropsWithoutRef & { background?: 'white' | 'avocado' } +>(({ className, background = 'white', ...props }, ref) => ( diff --git a/apps/wallet/src/ui/index.tsx b/apps/wallet/src/ui/index.tsx index 5d2a15227f003..cb22432848875 100644 --- a/apps/wallet/src/ui/index.tsx +++ b/apps/wallet/src/ui/index.tsx @@ -25,6 +25,7 @@ import App from './app'; import { walletApiProvider } from './app/ApiProvider'; import { AccountsFormProvider } from './app/components/accounts/AccountsFormContext'; import { UnlockAccountProvider } from './app/components/accounts/UnlockAccountContext'; +import { ZkLoginAccountWarningModal } from './app/components/accounts/ZkLoginAccountWaringModal'; import { SuiLedgerClientProvider } from './app/components/ledger/SuiLedgerClientProvider'; import { growthbook } from './app/experimentation/feature-gating'; import { persister, queryClient } from './app/helpers/queryClient'; @@ -93,6 +94,7 @@ function AppWrapper() { > +