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,19 +1,95 @@
 // @flow
 
+import olm from '@commapp/olm';
+import invariant from 'invariant';
 import { QRCodeSVG } from 'qrcode.react';
 import * as React from 'react';
+import { createSelector } from 'reselect';
 
+import { identityLogInActionTypes } from 'lib/actions/user-actions.js';
+import { QRAuthHandler } from 'lib/components/qr-auth-handler.react.js';
 import { qrCodeLinkURL } from 'lib/facts/links.js';
 import { generateKeyCommon } from 'lib/media/aes-crypto-utils-common.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 { CryptoStore, PickledOLMAccount } from 'lib/types/crypto-types.js';
+import type {
+  NonceChallenge,
+  SignedMessage,
+} from 'lib/types/identity-service-types.js';
+import type { WebAppState } from 'lib/types/redux-types.js';
+import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
 
 import css from './qr-code-login.css';
+import { initOlm } from '../olm/olm-utils.js';
 import { useSelector } from '../redux/redux-utils.js';
 
+const deviceIDAndPrimaryAccountSelector: (state: WebAppState) => {
+  ed25519Key: ?string,
+  primaryAccount: ?PickledOLMAccount,
+} = createSelector(
+  (state: WebAppState) => state.cryptoStore,
+  (cryptoStore: ?CryptoStore) => ({
+    ed25519Key: cryptoStore?.primaryIdentityKeys.ed25519,
+    primaryAccount: cryptoStore?.primaryAccount,
+  }),
+);
+
 function QrCodeLogin(): React.Node {
   const [qrCodeValue, setQrCodeValue] = React.useState<?string>();
-  const ed25519Key = useSelector(
-    state => state.cryptoStore?.primaryIdentityKeys.ed25519,
+  const { ed25519Key, primaryAccount } = useSelector(
+    deviceIDAndPrimaryAccountSelector,
+  );
+  const [deviceKeys, setDeviceKeys] =
+    React.useState<?{ +deviceID: string, +aesKey: string }>();
+  const { setUnauthorizedDeviceID } = useTunnelbroker();
+
+  const identityContext = React.useContext(IdentityClientContext);
+  const identityClient = identityContext?.identityClient;
+
+  const dispatchActionPromise = useDispatchActionPromise();
+  const performRegistration = React.useCallback(
+    async (userID: string) => {
+      if (!primaryAccount) {
+        return;
+      }
+      invariant(identityClient, 'identity context not set');
+      try {
+        await initOlm();
+        const primaryOLMAccount = new olm.Account();
+        primaryOLMAccount.unpickle(
+          primaryAccount.picklingKey,
+          primaryAccount.pickledAccount,
+        );
+
+        const nonce = await identityClient.generateNonce();
+        const nonceChallenge: NonceChallenge = { nonce };
+        const nonceMessage = JSON.stringify(nonceChallenge);
+        const signature = await primaryOLMAccount.sign(nonceMessage);
+        const challengeResponse: SignedMessage = {
+          message: nonceMessage,
+          signature,
+        };
+
+        await dispatchActionPromise(
+          identityLogInActionTypes,
+          identityClient.uploadKeysForRegisteredDeviceAndLogIn(
+            userID,
+            challengeResponse,
+          ),
+        );
+        setUnauthorizedDeviceID(null);
+      } catch (err) {
+        console.error('Secondary device registration error:', err);
+      }
+    },
+    [
+      dispatchActionPromise,
+      identityClient,
+      primaryAccount,
+      setUnauthorizedDeviceID,
+    ],
   );
 
   const generateQRCode = React.useCallback(async () => {
@@ -26,36 +102,45 @@
       const aesKeyAsHexString: string = uintArrayToHexString(rawAESKey);
 
       const url = qrCodeLinkURL(aesKeyAsHexString, ed25519Key);
+      setUnauthorizedDeviceID(ed25519Key);
       setQrCodeValue(url);
+      setDeviceKeys({ deviceID: ed25519Key, aesKey: aesKeyAsHexString });
     } catch (err) {
       console.error('Failed to generate QR Code:', err);
     }
-  }, [ed25519Key]);
+  }, [ed25519Key, setUnauthorizedDeviceID]);
 
   React.useEffect(() => {
     void generateQRCode();
   }, [generateQRCode]);
 
   return (
-    <div className={css.qrContainer}>
-      <div className={css.title}>Log in to Comm</div>
-      <div className={css.scanInstructions}>
-        Open the Comm app on your phone and scan the QR code below
-      </div>
-      <QRCodeSVG value={qrCodeValue} size={300} marginSize={4} level="L" />
-      <div className={css.instructionsContainer}>
-        <div className={css.instructionsTitle}>How to find the scanner:</div>
-        <div className={css.instructionsStep}>
-          Go to <strong>Profile</strong>
-        </div>
-        <div className={css.instructionsStep}>
-          Select <strong>Linked devices</strong>
+    <>
+      <QRAuthHandler
+        secondaryDeviceID={deviceKeys?.deviceID}
+        aesKey={deviceKeys?.aesKey}
+        performSecondaryDeviceRegistration={performRegistration}
+      />
+      <div className={css.qrContainer}>
+        <div className={css.title}>Log in to Comm</div>
+        <div className={css.scanInstructions}>
+          Open the Comm app on your phone and scan the QR code below
         </div>
-        <div className={css.instructionsStep}>
-          Click <strong>Add</strong> on the top right
+        <QRCodeSVG value={qrCodeValue} size={300} marginSize={4} level="L" />
+        <div className={css.instructionsContainer}>
+          <div className={css.instructionsTitle}>How to find the scanner:</div>
+          <div className={css.instructionsStep}>
+            Go to <strong>Profile</strong>
+          </div>
+          <div className={css.instructionsStep}>
+            Select <strong>Linked devices</strong>
+          </div>
+          <div className={css.instructionsStep}>
+            Click <strong>Add</strong> on the top right
+          </div>
         </div>
       </div>
-    </div>
+    </>
   );
 }