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
@@ -4,23 +4,12 @@
 import { View, Text } from 'react-native';
 import QRCode from 'react-native-qrcode-svg';
 
+import { useQRAuthContext } from 'lib/components/qr-auth-provider.react.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 { uintArrayToHexString } from 'lib/media/data-utils.js';
-import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
-import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 
 import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js';
-import {
-  composeTunnelbrokerQRAuthMessage,
-  handleSecondaryDeviceRegistrationError,
-  parseTunnelbrokerQRAuthMessage,
-  performBackupRestore,
-} from './qr-code-utils.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
 import { useStyles } from '../themes/colors.js';
-import * as AES from '../utils/aes-crypto-module.js';
 
 type QRCodeScreenProps = {
   +navigation: QRCodeSignInNavigationProp<'QRCodeScreen'>,
@@ -29,37 +18,7 @@
 
 // eslint-disable-next-line no-unused-vars
 function QRCodeScreen(props: QRCodeScreenProps): React.Node {
-  const [qrData, setQRData] =
-    React.useState<?{ +deviceID: string, +aesKey: string }>();
-
-  const { setUnauthorizedDeviceID } = useTunnelbroker();
-  const generateQRCode = React.useCallback(async () => {
-    try {
-      const [ed25519, rawAESKey] = await Promise.all([
-        getContentSigningKey(),
-        AES.generateKey(),
-      ]);
-      const aesKeyAsHexString: string = uintArrayToHexString(rawAESKey);
-
-      setUnauthorizedDeviceID(ed25519);
-      setQRData({ deviceID: ed25519, aesKey: aesKeyAsHexString });
-    } catch (err) {
-      console.error('Failed to generate QR Code:', err);
-    }
-  }, [setUnauthorizedDeviceID]);
-
-  const logInSecondaryDevice = useSecondaryDeviceLogIn();
-  const performRegistration = React.useCallback(
-    async (userID: string) => {
-      try {
-        await logInSecondaryDevice(userID);
-      } catch (err) {
-        handleSecondaryDeviceRegistrationError(err);
-        void generateQRCode();
-      }
-    },
-    [logInSecondaryDevice, generateQRCode],
-  );
+  const { qrData, generateQRCode } = useQRAuthContext();
 
   React.useEffect(() => {
     void generateQRCode();
@@ -70,19 +29,6 @@
     [qrData],
   );
 
-  const qrAuthInput = React.useMemo(
-    () => ({
-      secondaryDeviceID: qrData?.deviceID,
-      aesKey: qrData?.aesKey,
-      performSecondaryDeviceRegistration: performRegistration,
-      composeMessage: composeTunnelbrokerQRAuthMessage,
-      processMessage: parseTunnelbrokerQRAuthMessage,
-      performBackupRestore,
-    }),
-    [qrData, performRegistration],
-  );
-  useQRAuth(qrAuthInput);
-
   const styles = useStyles(unboundStyles);
   return (
     <View style={styles.container}>
diff --git a/native/qr-code/qr-code-utils.js b/native/qr-code/qr-code-utils.js
--- a/native/qr-code/qr-code-utils.js
+++ b/native/qr-code/qr-code-utils.js
@@ -89,9 +89,14 @@
   );
 }
 
+function generateQRAuthKey(): Promise<Uint8Array> {
+  return Promise.resolve(AES.generateKey());
+}
+
 export {
   composeTunnelbrokerQRAuthMessage,
   parseTunnelbrokerQRAuthMessage,
   handleSecondaryDeviceRegistrationError,
   performBackupRestore,
+  generateQRAuthKey,
 };
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -32,6 +32,7 @@
 import { NeynarClientProvider } from 'lib/components/neynar-client-provider.react.js';
 import PlatformDetailsSynchronizer from 'lib/components/platform-details-synchronizer.react.js';
 import PrekeysHandler from 'lib/components/prekeys-handler.react.js';
+import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js';
 import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
 import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js';
 import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js';
@@ -76,6 +77,13 @@
 import OrientationHandler from './navigation/orientation-handler.react.js';
 import { navStateAsyncStorageKey } from './navigation/persistance.js';
 import RootNavigator from './navigation/root-navigator.react.js';
+import {
+  composeTunnelbrokerQRAuthMessage,
+  handleSecondaryDeviceRegistrationError,
+  parseTunnelbrokerQRAuthMessage,
+  performBackupRestore,
+  generateQRAuthKey,
+} from './qr-code/qr-code-utils.js';
 import ConnectivityUpdater from './redux/connectivity-updater.react.js';
 import { DimensionsUpdater } from './redux/dimensions-updater.react.js';
 import { getPersistor } from './redux/persist.js';
@@ -309,63 +317,73 @@
         <IdentityServiceContextProvider>
           <TunnelbrokerProvider>
             <IdentitySearchProvider>
-              <FeatureFlagsProvider>
-                <NavContext.Provider value={navContext}>
-                  <RootContext.Provider value={rootContext}>
-                    <InputStateContainer>
-                      <MessageEditingContextProvider>
-                        <SafeAreaProvider initialMetrics={initialWindowMetrics}>
-                          <ActionSheetProvider>
-                            <ENSCacheProvider provider={provider}>
-                              <NeynarClientProvider apiKey={neynarKey}>
-                                <MediaCacheProvider
-                                  persistence={filesystemMediaCache}
-                                >
-                                  <EditUserAvatarProvider>
-                                    <NativeEditThreadAvatarProvider>
-                                      <MarkdownContextProvider>
-                                        <MessageSearchProvider>
-                                          <BottomSheetProvider>
-                                            <RegistrationContextProvider>
-                                              <SQLiteDataHandler />
-                                              <ConnectedStatusBar />
-                                              <ReduxPersistGate
-                                                persistor={getPersistor()}
-                                              >
-                                                {gated}
-                                              </ReduxPersistGate>
-                                              <PersistedStateGate>
-                                                <KeyserverConnectionsHandler
-                                                  socketComponent={Socket}
-                                                  detectUnsupervisedBackgroundRef={
-                                                    detectUnsupervisedBackgroundRef
-                                                  }
-                                                />
-                                                <VersionSupportedChecker />
-                                                <PlatformDetailsSynchronizer />
-                                                <BackgroundIdentityLoginHandler />
-                                                <PrekeysHandler />
-                                                <ReportHandler />
-                                                <FarcasterDataHandler />
-                                                <AutoJoinCommunityHandler />
-                                              </PersistedStateGate>
-                                              {navigation}
-                                            </RegistrationContextProvider>
-                                          </BottomSheetProvider>
-                                        </MessageSearchProvider>
-                                      </MarkdownContextProvider>
-                                    </NativeEditThreadAvatarProvider>
-                                  </EditUserAvatarProvider>
-                                </MediaCacheProvider>
-                              </NeynarClientProvider>
-                            </ENSCacheProvider>
-                          </ActionSheetProvider>
-                        </SafeAreaProvider>
-                      </MessageEditingContextProvider>
-                    </InputStateContainer>
-                  </RootContext.Provider>
-                </NavContext.Provider>
-              </FeatureFlagsProvider>
+              <QRAuthProvider
+                processTunnelbrokerMessage={parseTunnelbrokerQRAuthMessage}
+                composeTunnelbrokerMessage={composeTunnelbrokerQRAuthMessage}
+                generateAESKey={generateQRAuthKey}
+                performBackupRestore={performBackupRestore}
+                onLoginError={handleSecondaryDeviceRegistrationError}
+              >
+                <FeatureFlagsProvider>
+                  <NavContext.Provider value={navContext}>
+                    <RootContext.Provider value={rootContext}>
+                      <InputStateContainer>
+                        <MessageEditingContextProvider>
+                          <SafeAreaProvider
+                            initialMetrics={initialWindowMetrics}
+                          >
+                            <ActionSheetProvider>
+                              <ENSCacheProvider provider={provider}>
+                                <NeynarClientProvider apiKey={neynarKey}>
+                                  <MediaCacheProvider
+                                    persistence={filesystemMediaCache}
+                                  >
+                                    <EditUserAvatarProvider>
+                                      <NativeEditThreadAvatarProvider>
+                                        <MarkdownContextProvider>
+                                          <MessageSearchProvider>
+                                            <BottomSheetProvider>
+                                              <RegistrationContextProvider>
+                                                <SQLiteDataHandler />
+                                                <ConnectedStatusBar />
+                                                <ReduxPersistGate
+                                                  persistor={getPersistor()}
+                                                >
+                                                  {gated}
+                                                </ReduxPersistGate>
+                                                <PersistedStateGate>
+                                                  <KeyserverConnectionsHandler
+                                                    socketComponent={Socket}
+                                                    detectUnsupervisedBackgroundRef={
+                                                      detectUnsupervisedBackgroundRef
+                                                    }
+                                                  />
+                                                  <VersionSupportedChecker />
+                                                  <PlatformDetailsSynchronizer />
+                                                  <BackgroundIdentityLoginHandler />
+                                                  <PrekeysHandler />
+                                                  <ReportHandler />
+                                                  <FarcasterDataHandler />
+                                                  <AutoJoinCommunityHandler />
+                                                </PersistedStateGate>
+                                                {navigation}
+                                              </RegistrationContextProvider>
+                                            </BottomSheetProvider>
+                                          </MessageSearchProvider>
+                                        </MarkdownContextProvider>
+                                      </NativeEditThreadAvatarProvider>
+                                    </EditUserAvatarProvider>
+                                  </MediaCacheProvider>
+                                </NeynarClientProvider>
+                              </ENSCacheProvider>
+                            </ActionSheetProvider>
+                          </SafeAreaProvider>
+                        </MessageEditingContextProvider>
+                      </InputStateContainer>
+                    </RootContext.Provider>
+                  </NavContext.Provider>
+                </FeatureFlagsProvider>
+              </QRAuthProvider>
             </IdentitySearchProvider>
           </TunnelbrokerProvider>
         </IdentityServiceContextProvider>