diff --git a/native/profile/secondary-device-qr-code-scanner.react.js b/native/profile/secondary-device-qr-code-scanner.react.js --- a/native/profile/secondary-device-qr-code-scanner.react.js +++ b/native/profile/secondary-device-qr-code-scanner.react.js @@ -10,8 +10,19 @@ import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js'; import type { RawDeviceList } from 'lib/types/identity-service-types.js'; +import { + tunnelbrokerMessageTypes, + type TunnelbrokerMessage, +} from 'lib/types/tunnelbroker/messages.js'; +import { + peerToPeerMessageTypes, + type PeerToPeerMessage, +} from 'lib/types/tunnelbroker/peer-to-peer-message-types.js'; import { qrCodeAuthMessageTypes } from 'lib/types/tunnelbroker/qr-code-auth-message-types.js'; -import { createQRAuthTunnelbrokerMessage } from 'lib/utils/qr-code-auth.js'; +import { + createQRAuthTunnelbrokerMessage, + parseQRAuthTunnelbrokerMessage, +} from 'lib/utils/qr-code-auth.js'; import type { ProfileNavigationProp } from './profile.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; @@ -35,6 +46,9 @@ const tunnelbrokerContext = useTunnelbroker(); const identityContext = React.useContext(IdentityClientContext); + const aes256Key = React.useRef(null); + const secondaryDeviceID = React.useRef(null); + const addDeviceToList = React.useCallback( async (newDeviceID: string) => { invariant(identityContext, 'identity context not set'); @@ -75,6 +89,47 @@ [identityContext], ); + const tunnelbrokerMessageListener = React.useCallback( + async (message: TunnelbrokerMessage) => { + const encryptionKey = aes256Key.current; + const targetDeviceID = secondaryDeviceID.current; + if (!encryptionKey || !targetDeviceID) { + return; + } + if (message.type !== tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE) { + return; + } + + const innerMessage: PeerToPeerMessage = JSON.parse(message.payload); + if (innerMessage.type !== peerToPeerMessageTypes.QR_CODE_AUTH_MESSAGE) { + return; + } + + const payload = parseQRAuthTunnelbrokerMessage( + encryptionKey, + innerMessage, + ); + if ( + payload?.type !== + qrCodeAuthMessageTypes.SECONDARY_DEVICE_REGISTRATION_SUCCESS + ) { + return; + } + Alert.alert('Device added', 'Device registered successfully', [ + { text: 'OK' }, + ]); + }, + [tunnelbrokerContext], + ); + + React.useEffect(() => { + tunnelbrokerContext.addListener(tunnelbrokerMessageListener); + + return () => { + tunnelbrokerContext.removeListener(tunnelbrokerMessageListener); + }; + }, [tunnelbrokerMessageListener, tunnelbrokerContext]); + React.useEffect(() => { void (async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); @@ -109,6 +164,8 @@ const keys = JSON.parse(decodeURIComponent(keysMatch)); const { aes256, ed25519 } = keys; + aes256Key.current = aes256; + secondaryDeviceID.current = ed25519; void (async () => { try { 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 @@ -19,7 +19,10 @@ } from 'lib/types/tunnelbroker/messages.js'; import { peerToPeerMessageTypes } from 'lib/types/tunnelbroker/peer-to-peer-message-types.js'; import { qrCodeAuthMessageTypes } from 'lib/types/tunnelbroker/qr-code-auth-message-types.js'; -import { parseQRAuthTunnelbrokerMessage } from 'lib/utils/qr-code-auth.js'; +import { + createQRAuthTunnelbrokerMessage, + parseQRAuthTunnelbrokerMessage, +} from 'lib/utils/qr-code-auth.js'; import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js'; import { commCoreModule } from '../native-modules.js'; @@ -39,11 +42,41 @@ const [qrCodeValue, setQrCodeValue] = React.useState(); const [deviceKeys, setDeviceKeys] = React.useState(); - const { setUnauthorizedDeviceID, addListener, removeListener } = - useTunnelbroker(); + const [primaryDeviceID, setPrimaryDeviceID] = React.useState(); + const { + setUnauthorizedDeviceID, + addListener, + removeListener, + connected: tunnelbrokerConnected, + isAuthorized, + sendMessage, + } = useTunnelbroker(); const identityContext = React.useContext(IdentityClientContext); const identityClient = identityContext?.identityClient; + React.useEffect(() => { + if ( + !(tunnelbrokerConnected && isAuthorized && primaryDeviceID && deviceKeys) + ) { + return; + } + + const message = createQRAuthTunnelbrokerMessage(deviceKeys.aesKey, { + type: qrCodeAuthMessageTypes.SECONDARY_DEVICE_REGISTRATION_SUCCESS, + }); + console.log('SECONDARY MESSAGE', message); + void sendMessage({ + deviceID: primaryDeviceID, + payload: JSON.stringify(message), + }); + }, [ + tunnelbrokerConnected, + isAuthorized, + sendMessage, + primaryDeviceID, + deviceKeys, + ]); + const tunnelbrokerMessageListener = React.useCallback( async (message: TunnelbrokerMessage) => { invariant(identityClient, 'identity context not set'); @@ -69,7 +102,9 @@ ) { return; } - const { userID } = qrCodeAuthMessage; + const { primaryDeviceID: receivedPrimaryDeviceID, userID } = + qrCodeAuthMessage; + setPrimaryDeviceID(receivedPrimaryDeviceID); try { const nonce = await identityClient.generateNonce();