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,16 +1,35 @@
 // @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 { 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 {
+  NonceChallenge,
+  SignedMessage,
+} from 'lib/types/identity-service-types.js';
+import {
+  tunnelbrokerMessageTypes,
+  type TunnelbrokerMessage,
+} from 'lib/types/tunnelbroker/messages.js';
+import {
+  peerToPeerMessageTypes,
+  peerToPeerMessageValidator,
+} 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 type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js';
+import { commCoreModule } from '../native-modules.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
 import { useStyles } from '../themes/colors.js';
 import * as AES from '../utils/aes-crypto-module.js';
+import Alert from '../utils/alert.js';
 import { getContentSigningKey } from '../utils/crypto-utils.js';
 
 type QRCodeScreenProps = {
@@ -21,6 +40,91 @@
 // eslint-disable-next-line no-unused-vars
 function QRCodeScreen(props: QRCodeScreenProps): React.Node {
   const [qrCodeValue, setQrCodeValue] = React.useState<?string>();
+  const [qrData, setQRData] =
+    React.useState<?{ +deviceID: string, +aesKey: string }>();
+  const { setUnauthorizedDeviceID, addListener, removeListener } =
+    useTunnelbroker();
+  const identityContext = React.useContext(IdentityClientContext);
+  const identityClient = identityContext?.identityClient;
+
+  const tunnelbrokerMessageListener = React.useCallback(
+    async (message: TunnelbrokerMessage) => {
+      invariant(identityClient, 'identity context not set');
+      if (
+        !qrData ||
+        message.type !== tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE
+      ) {
+        return;
+      }
+
+      let innerMessage;
+      try {
+        innerMessage = JSON.parse(message.payload);
+      } catch {
+        return;
+      }
+      if (
+        !peerToPeerMessageValidator.is(innerMessage) ||
+        innerMessage.type !== peerToPeerMessageTypes.QR_CODE_AUTH_MESSAGE
+      ) {
+        return;
+      }
+      const qrCodeAuthMessage = parseQRAuthTunnelbrokerMessage(
+        qrData.aesKey,
+        innerMessage,
+      );
+      if (
+        !qrCodeAuthMessage ||
+        qrCodeAuthMessage.type !==
+          qrCodeAuthMessageTypes.DEVICE_LIST_UPDATE_SUCCESS
+      ) {
+        return;
+      }
+      const { userID } = qrCodeAuthMessage;
+
+      try {
+        const nonce = await identityClient.generateNonce();
+        const nonceChallenge: NonceChallenge = { nonce };
+        const nonceMessage = JSON.stringify(nonceChallenge);
+        const signature = await commCoreModule.signMessage(nonceMessage);
+        const challengeResponse: SignedMessage = {
+          message: nonceMessage,
+          signature,
+        };
+
+        await identityClient.uploadKeysForRegisteredDeviceAndLogIn(
+          userID,
+          challengeResponse,
+        );
+        setUnauthorizedDeviceID(null);
+      } catch (err) {
+        console.error('Secondary device registration error:', err);
+        Alert.alert('Registration failed', 'Failed to upload device keys', [
+          { text: 'OK' },
+        ]);
+      }
+    },
+    [setUnauthorizedDeviceID, identityClient, qrData],
+  );
+
+  React.useEffect(() => {
+    if (!qrData) {
+      return undefined;
+    }
+    addListener(tunnelbrokerMessageListener);
+    setUnauthorizedDeviceID(qrData.deviceID);
+
+    return () => {
+      removeListener(tunnelbrokerMessageListener);
+      setUnauthorizedDeviceID(null);
+    };
+  }, [
+    setUnauthorizedDeviceID,
+    qrData,
+    addListener,
+    removeListener,
+    tunnelbrokerMessageListener,
+  ]);
 
   const generateQRCode = React.useCallback(async () => {
     try {
@@ -31,6 +135,7 @@
 
       const url = qrCodeLinkURL(aesKeyAsHexString, ed25519Key);
       setQrCodeValue(url);
+      setQRData({ deviceID: ed25519Key, aesKey: aesKeyAsHexString });
     } catch (err) {
       console.error('Failed to generate QR Code:', err);
     }