diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -645,6 +645,29 @@ invariant(identityClient, 'Identity client should be set'); return identityClient.logInWalletUser; } +function useIdentitySecondaryDeviceLogIn(): ( + userID: string, +) => Promise { + const client = React.useContext(IdentityClientContext); + const identityClient = client?.identityClient; + invariant(identityClient, 'Identity client should be set'); + const { generateNonce, uploadKeysForRegisteredDeviceAndLogIn } = + identityClient; + + const { signMessage } = getConfig().olmAPI; + + return React.useCallback( + async (userID: string) => { + const nonce = await generateNonce(); + const nonceSignature = await signMessage(nonce); + return await uploadKeysForRegisteredDeviceAndLogIn(userID, { + nonce, + nonceSignature, + }); + }, + [generateNonce, signMessage, uploadKeysForRegisteredDeviceAndLogIn], + ); +} const legacyLogInActionTypes = Object.freeze({ started: 'LEGACY_LOG_IN_STARTED', @@ -954,6 +977,7 @@ identityLogInActionTypes, useIdentityPasswordLogIn, useIdentityWalletLogIn, + useIdentitySecondaryDeviceLogIn, useLegacyLogIn, legacyLogInActionTypes, useLogOut, diff --git a/lib/hooks/login-hooks.js b/lib/hooks/login-hooks.js --- a/lib/hooks/login-hooks.js +++ b/lib/hooks/login-hooks.js @@ -7,6 +7,7 @@ identityLogInActionTypes, useIdentityPasswordLogIn, useIdentityWalletLogIn, + useIdentitySecondaryDeviceLogIn, logOutActionTypes, useIdentityLogOut, } from '../actions/user-actions.js'; @@ -174,4 +175,23 @@ ); } -export { usePasswordLogIn, useWalletLogIn }; +function useSecondaryDeviceLogIn(): (userID: string) => Promise { + const identitySecondaryDeviceLogIn = useIdentitySecondaryDeviceLogIn(); + const dispatchActionPromise = useDispatchActionPromise(); + const identitySecondaryDeviceAuth = React.useCallback( + (userID: string) => { + const promise = identitySecondaryDeviceLogIn(userID); + void dispatchActionPromise(identityLogInActionTypes, promise); + return promise; + }, + [identitySecondaryDeviceLogIn, dispatchActionPromise], + ); + + const logIn = useLogIn(); + return React.useCallback( + (userID: string) => logIn(identitySecondaryDeviceAuth(userID)), + [logIn, identitySecondaryDeviceAuth], + ); +} + +export { usePasswordLogIn, useWalletLogIn, useSecondaryDeviceLogIn }; diff --git a/native/qr-code/qr-code-screen.react.js b/native/qr-code/qr-code-screen.react.js --- a/native/qr-code/qr-code-screen.react.js +++ b/native/qr-code/qr-code-screen.react.js @@ -1,17 +1,15 @@ // @flow -import invariant from 'invariant'; import * as React from 'react'; import { View, Text } from 'react-native'; import QRCode from 'react-native-qrcode-svg'; import { qrCodeLinkURL } from 'lib/facts/links.js'; +import { useSecondaryDeviceLogIn } from 'lib/hooks/login-hooks.js'; import { useQRAuth } from 'lib/hooks/qr-auth.js'; import { uintArrayToHexString } from 'lib/media/data-utils.js'; -import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js'; import type { BackupKeys } from 'lib/types/backup-types.js'; -import type { SignedNonce } from 'lib/types/identity-service-types.js'; import { getContentSigningKey } from 'lib/utils/crypto-utils.js'; import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js'; @@ -19,7 +17,6 @@ composeTunnelbrokerQRAuthMessage, parseTunnelbrokerQRAuthMessage, } from './qr-code-utils.js'; -import { olmAPI } from '../crypto/olm-api.js'; import { commCoreModule } from '../native-modules.js'; import type { NavigationRoute } from '../navigation/route-names.js'; import { useStyles } from '../themes/colors.js'; @@ -46,24 +43,11 @@ React.useState(); const { setUnauthorizedDeviceID } = useTunnelbroker(); - const identityContext = React.useContext(IdentityClientContext); - const identityClient = identityContext?.identityClient; - + const secondaryDeviceLogIn = useSecondaryDeviceLogIn(); const performRegistration = React.useCallback( async (userID: string) => { - invariant(identityClient, 'identity context not set'); try { - const nonce = await identityClient.generateNonce(); - const nonceSignature = await olmAPI.signMessage(nonce); - const challengeResponse: SignedNonce = { - nonce, - nonceSignature, - }; - - await identityClient.uploadKeysForRegisteredDeviceAndLogIn( - userID, - challengeResponse, - ); + await secondaryDeviceLogIn(userID); setUnauthorizedDeviceID(null); } catch (err) { console.error('Secondary device registration error:', err); @@ -72,7 +56,7 @@ ]); } }, - [setUnauthorizedDeviceID, identityClient], + [secondaryDeviceLogIn, setUnauthorizedDeviceID], ); const generateQRCode = React.useCallback(async () => { diff --git a/web/account/qr-code-login.react.js b/web/account/qr-code-login.react.js --- a/web/account/qr-code-login.react.js +++ b/web/account/qr-code-login.react.js @@ -1,18 +1,15 @@ // @flow -import invariant from 'invariant'; import { QRCodeSVG } from 'qrcode.react'; import * as React from 'react'; -import { identityLogInActionTypes } from 'lib/actions/user-actions.js'; import { qrCodeLinkURL } from 'lib/facts/links.js'; +import { useSecondaryDeviceLogIn } from 'lib/hooks/login-hooks.js'; import { useQRAuth } from 'lib/hooks/qr-auth.js'; import { generateKeyCommon } from 'lib/media/aes-crypto-utils-common.js'; import * as AES from 'lib/media/aes-crypto-utils-common.js'; import { hexToUintArray, uintArrayToHexString } from 'lib/media/data-utils.js'; -import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js'; -import type { SignedNonce } from 'lib/types/identity-service-types.js'; import { peerToPeerMessageTypes, type QRCodeAuthMessage, @@ -22,10 +19,8 @@ type QRCodeAuthMessagePayload, } from 'lib/types/tunnelbroker/qr-code-auth-message-types.js'; import { getContentSigningKey } from 'lib/utils/crypto-utils.js'; -import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import css from './qr-code-login.css'; -import { olmAPI } from '../crypto/olm-api.js'; import { base64DecodeBuffer, base64EncodeBuffer, @@ -72,34 +67,17 @@ React.useState(); const { setUnauthorizedDeviceID } = useTunnelbroker(); - const identityContext = React.useContext(IdentityClientContext); - const identityClient = identityContext?.identityClient; - - const dispatchActionPromise = useDispatchActionPromise(); + const secondaryDeviceLogIn = useSecondaryDeviceLogIn(); const performRegistration = React.useCallback( async (userID: string) => { - invariant(identityClient, 'identity context not set'); try { - const nonce = await identityClient.generateNonce(); - const nonceSignature = await olmAPI.signMessage(nonce); - const challengeResponse: SignedNonce = { - nonce, - nonceSignature, - }; - - await dispatchActionPromise( - identityLogInActionTypes, - identityClient.uploadKeysForRegisteredDeviceAndLogIn( - userID, - challengeResponse, - ), - ); + await secondaryDeviceLogIn(userID); setUnauthorizedDeviceID(null); } catch (err) { console.error('Secondary device registration error:', err); } }, - [dispatchActionPromise, identityClient, setUnauthorizedDeviceID], + [secondaryDeviceLogIn, setUnauthorizedDeviceID], ); const generateQRCode = React.useCallback(async () => {