diff --git a/web/account/account-hooks.js b/web/account/account-hooks.js --- a/web/account/account-hooks.js +++ b/web/account/account-hooks.js @@ -1,35 +1,127 @@ // @flow +import olm from '@commapp/olm'; +import invariant from 'invariant'; import * as React from 'react'; +import { useDispatch } from 'react-redux'; +import uuid from 'uuid'; -import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js'; +import type { + SignedIdentityKeysBlob, + CryptoStore, + IdentityKeysBlob, +} from 'lib/types/crypto-types.js'; +import { initOlm } from '../olm/olm-utils.js'; +import { setCryptoStore } from '../redux/crypto-store-reducer.js'; import { useSelector } from '../redux/redux-utils.js'; -import { getSignedIdentityKeysBlobSelector } from '../selectors/socket-selectors.js'; - -function useSignedIdentityKeysBlob(): ?SignedIdentityKeysBlob { - const getSignedIdentityKeysBlob: ?() => Promise = - useSelector(getSignedIdentityKeysBlobSelector); - - const [signedIdentityKeysBlob, setSignedIdentityKeysBlob] = - React.useState(null); - - React.useEffect(() => { - (async () => { - if ( - getSignedIdentityKeysBlob === null || - getSignedIdentityKeysBlob === undefined - ) { - setSignedIdentityKeysBlob(null); - return; - } - const resolvedSignedIdentityKeysBlob: SignedIdentityKeysBlob = - await getSignedIdentityKeysBlob(); - setSignedIdentityKeysBlob(resolvedSignedIdentityKeysBlob); - })(); - }, [getSignedIdentityKeysBlob]); - - return signedIdentityKeysBlob; + +const CryptoStoreContext: React.Context> = + React.createContext(null); + +type Props = { + +children: React.Node, +}; + +function CryptoStoreProvider(props: Props): React.Node { + const dispatch = useDispatch(); + const currentCryptoStore = useSelector(state => state.cryptoStore); + + const cryptoStoreValue = React.useMemo(async () => { + if (currentCryptoStore) { + return currentCryptoStore; + } + + await initOlm(); + + const identityAccount = new olm.Account(); + identityAccount.create(); + const { ed25519: identityED25519, curve25519: identityCurve25519 } = + JSON.parse(identityAccount.identity_keys()); + + const identityAccountPicklingKey = uuid.v4(); + const pickledIdentityAccount = identityAccount.pickle( + identityAccountPicklingKey, + ); + + const notificationAccount = new olm.Account(); + notificationAccount.create(); + const { ed25519: notificationED25519, curve25519: notificationCurve25519 } = + JSON.parse(notificationAccount.identity_keys()); + + const notificationAccountPicklingKey = uuid.v4(); + const pickledNotificationAccount = notificationAccount.pickle( + notificationAccountPicklingKey, + ); + + const newCryptoStore = { + primaryAccount: { + picklingKey: identityAccountPicklingKey, + pickledAccount: pickledIdentityAccount, + }, + primaryIdentityKeys: { + ed25519: identityED25519, + curve25519: identityCurve25519, + }, + notificationAccount: { + picklingKey: notificationAccountPicklingKey, + pickledAccount: pickledNotificationAccount, + }, + notificationIdentityKeys: { + ed25519: notificationED25519, + curve25519: notificationCurve25519, + }, + }; + + dispatch({ + type: setCryptoStore, + payload: newCryptoStore, + }); + + return newCryptoStore; + }, [currentCryptoStore, dispatch]); + + return ( + + {props.children} + + ); +} + +function useCryptoStore(): Promise { + const context = React.useContext(CryptoStoreContext); + invariant(context, 'GetOrCreateCryptoStoreContext not found'); + + return context; +} + +function useGetSignedIdentityKeysBlob(): () => Promise { + const cryptoStorePromise = useCryptoStore(); + + return React.useCallback(async () => { + const { primaryAccount, primaryIdentityKeys, notificationIdentityKeys } = + await cryptoStorePromise; + + await initOlm(); + const primaryOLMAccount = new olm.Account(); + primaryOLMAccount.unpickle( + primaryAccount.picklingKey, + primaryAccount.pickledAccount, + ); + + const identityKeysBlob: IdentityKeysBlob = { + primaryIdentityPublicKeys: primaryIdentityKeys, + notificationIdentityPublicKeys: notificationIdentityKeys, + }; + + const payloadToBeSigned: string = JSON.stringify(identityKeysBlob); + const signedIdentityKeysBlob: SignedIdentityKeysBlob = { + payload: payloadToBeSigned, + signature: primaryOLMAccount.sign(payloadToBeSigned), + }; + + return signedIdentityKeysBlob; + }, [cryptoStorePromise]); } -export { useSignedIdentityKeysBlob }; +export { useGetSignedIdentityKeysBlob, useCryptoStore, CryptoStoreProvider }; diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js --- a/web/account/log-in-form.react.js +++ b/web/account/log-in-form.react.js @@ -1,10 +1,8 @@ // @flow -import olm from '@commapp/olm'; import { useConnectModal } from '@rainbow-me/rainbowkit'; import * as React from 'react'; import { useDispatch } from 'react-redux'; -import uuid from 'uuid'; import { useWalletClient } from 'wagmi'; import { isDev } from 'lib/utils/dev-utils.js'; @@ -15,71 +13,13 @@ import TraditionalLoginForm from './traditional-login-form.react.js'; import Button from '../components/button.react.js'; import OrBreak from '../components/or-break.react.js'; -import { initOlm } from '../olm/olm-utils.js'; import { updateNavInfoActionType } from '../redux/action-types.js'; -import { setCryptoStore } from '../redux/crypto-store-reducer.js'; -import { useSelector } from '../redux/redux-utils.js'; function LoginForm(): React.Node { const { openConnectModal } = useConnectModal(); const { data: signer } = useWalletClient(); const dispatch = useDispatch(); - const cryptoStore = useSelector(state => state.cryptoStore); - - React.useEffect(() => { - (async () => { - if (cryptoStore !== null && cryptoStore !== undefined) { - return; - } - await initOlm(); - - const identityAccount = new olm.Account(); - identityAccount.create(); - const { ed25519: identityED25519, curve25519: identityCurve25519 } = - JSON.parse(identityAccount.identity_keys()); - - const identityAccountPicklingKey = uuid.v4(); - const pickledIdentityAccount = identityAccount.pickle( - identityAccountPicklingKey, - ); - - const notificationAccount = new olm.Account(); - notificationAccount.create(); - const { - ed25519: notificationED25519, - curve25519: notificationCurve25519, - } = JSON.parse(notificationAccount.identity_keys()); - - const notificationAccountPicklingKey = uuid.v4(); - const pickledNotificationAccount = notificationAccount.pickle( - notificationAccountPicklingKey, - ); - - dispatch({ - type: setCryptoStore, - payload: { - primaryAccount: { - picklingKey: identityAccountPicklingKey, - pickledAccount: pickledIdentityAccount, - }, - primaryIdentityKeys: { - ed25519: identityED25519, - curve25519: identityCurve25519, - }, - notificationAccount: { - picklingKey: notificationAccountPicklingKey, - pickledAccount: pickledNotificationAccount, - }, - notificationIdentityKeys: { - ed25519: notificationED25519, - curve25519: notificationCurve25519, - }, - }, - }); - })(); - }, [dispatch, cryptoStore]); - const onQRCodeLoginButtonClick = React.useCallback(() => { dispatch({ type: updateNavInfoActionType, diff --git a/web/account/siwe-login-form.react.js b/web/account/siwe-login-form.react.js --- a/web/account/siwe-login-form.react.js +++ b/web/account/siwe-login-form.react.js @@ -19,10 +19,7 @@ import stores from 'lib/facts/stores.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import type { LogInStartingPayload } from 'lib/types/account-types.js'; -import type { - OLMIdentityKeys, - SignedIdentityKeysBlob, -} from 'lib/types/crypto-types.js'; +import type { OLMIdentityKeys } from 'lib/types/crypto-types.js'; import { useDispatchActionPromise, useServerCall, @@ -34,7 +31,7 @@ siweMessageSigningExplanationStatements, } from 'lib/utils/siwe-utils.js'; -import { useSignedIdentityKeysBlob } from './account-hooks.js'; +import { useGetSignedIdentityKeysBlob } from './account-hooks.js'; import HeaderSeparator from './header-separator.react.js'; import css from './siwe.css'; import Button from '../components/button.react.js'; @@ -88,11 +85,11 @@ state => state.cryptoStore?.primaryIdentityKeys, ); - const signedIdentityKeysBlob: ?SignedIdentityKeysBlob = - useSignedIdentityKeysBlob(); + const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob(); const callSIWEAuthEndpoint = React.useCallback( async (message: string, signature: string, extraInfo) => { + const signedIdentityKeysBlob = await getSignedIdentityKeysBlob(); invariant( signedIdentityKeysBlob, 'signedIdentityKeysBlob must be set in attemptSIWEAuth', @@ -115,7 +112,7 @@ throw e; } }, - [signedIdentityKeysBlob, siweAuthCall], + [getSignedIdentityKeysBlob, siweAuthCall], ); const attemptSIWEAuth = React.useCallback( @@ -186,8 +183,7 @@ if ( siweAuthLoadingStatus === 'loading' || !siweNonce || - !primaryIdentityPublicKeys || - !signedIdentityKeysBlob + !primaryIdentityPublicKeys ) { return (
diff --git a/web/account/traditional-login-form.react.js b/web/account/traditional-login-form.react.js --- a/web/account/traditional-login-form.react.js +++ b/web/account/traditional-login-form.react.js @@ -15,13 +15,12 @@ LogInStartingPayload, } from 'lib/types/account-types.js'; import { logInActionSources } from 'lib/types/account-types.js'; -import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; -import { useSignedIdentityKeysBlob } from './account-hooks.js'; +import { useGetSignedIdentityKeysBlob } from './account-hooks.js'; import HeaderSeparator from './header-separator.react.js'; import css from './log-in-form.css'; import PasswordInput from './password-input.react.js'; @@ -39,8 +38,7 @@ const dispatchActionPromise = useDispatchActionPromise(); const modalContext = useModalContext(); - const signedIdentityKeysBlob: ?SignedIdentityKeysBlob = - useSignedIdentityKeysBlob(); + const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob(); const usernameInputRef = React.useRef(); React.useEffect(() => { @@ -67,6 +65,7 @@ const logInAction = React.useCallback( async (extraInfo: LogInExtraInfo) => { + const signedIdentityKeysBlob = await getSignedIdentityKeysBlob(); try { invariant( signedIdentityKeysBlob, @@ -94,7 +93,7 @@ throw e; } }, - [callLogIn, modalContext, password, signedIdentityKeysBlob, username], + [callLogIn, modalContext, password, getSignedIdentityKeysBlob, username], ); const onSubmit = React.useCallback( @@ -174,11 +173,7 @@