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 @@ -2,10 +2,27 @@ import { useNavigation } from '@react-navigation/native'; import { BarCodeScanner, type BarCodeEvent } from 'expo-barcode-scanner'; +import invariant from 'invariant'; import * as React from 'react'; import { View } from 'react-native'; import { parseDataFromDeepLink } from 'lib/facts/links.js'; +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, + parseQRAuthTunnelbrokerMessage, +} from 'lib/utils/qr-code-auth.js'; import type { ProfileNavigationProp } from './profile.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; @@ -29,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'); @@ -69,6 +89,45 @@ [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', `It worked`, [{ text: 'OK' }]); + }, + [tunnelbrokerContext], + ); + + React.useEffect(() => { + tunnelbrokerContext.addListener(tunnelbrokerMessageListener); + + return () => { + tunnelbrokerContext.removeListener(tunnelbrokerMessageListener); + }; + }, [tunnelbrokerMessageListener, tunnelbrokerContext]); + React.useEffect(() => { void (async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); @@ -103,6 +162,8 @@ const keys = JSON.parse(decodeURIComponent(keysMatch)); const { aes256, ed25519 } = keys; + aes256Key.current = aes256; + secondaryDeviceID.current = ed25519; void (async () => { try { @@ -133,12 +194,7 @@ } })(); }, - [ - tunnelbrokerContext, - addDeviceToList, - identityContext, - navigation, - ], + [tunnelbrokerContext, addDeviceToList, identityContext, navigation], ); const onCancelScan = React.useCallback(() => setScanned(false), []); 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 @@ -42,6 +42,7 @@ const [qrCodeValue, setQrCodeValue] = React.useState(); const [deviceKeys, setDeviceKeys] = React.useState(); + const [primaryDeviceID, setPrimaryDeviceID] = React.useState(); const { setUnauthorizedDeviceID, addListener, @@ -53,6 +54,29 @@ 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'); @@ -78,7 +102,9 @@ ) { return; } - const { userID } = qrCodeAuthMessage; + const { primaryDeviceID: receivedPrimaryDeviceID, userID } = + qrCodeAuthMessage; + setPrimaryDeviceID(receivedPrimaryDeviceID); try { const nonce = await identityClient.generateNonce();