To resolve Web3Auth Expo Custom Tab & TmMfaAdapter Not Defined Fix, fix Web3Auth React Native Expo Custom Tab redirect loops and. Ensure all derivation paths, environment parameters, and cryptographic signatures are verified client-side using deterministic tools before broadcasting live transactions.
The Web3Auth Expo Managed Workflow Custom Tab redirect loop and ReferenceError: TmMfaAdapter is not defined error occur when Android intent filters drop OAuth deep-links or Metro/Hermes bundlers fail to resolve the Torus MFA adapter in React Native. Missing app.json scheme plugins prevent Android Custom Tabs from returning MPC session keys to the app. Adding the Web3Auth plugin to app.json and polyfilling the MFA adapter constructor resolves both issues.
Before debugging mobile deep-links, inspect your WebAuthn authentication payloads using our WebAuthn Passkey Debugger Tool. If your users encounter social threshold share recovery failures, see our guide on Web3Auth MPC-TSS threshold recovery troubleshooting.
Diagnostic Implementation Block
To fix this immediately, you must inject the Web3Auth intent filter schema into your app.json configuration to handle the expo-web-browser deep-link intercept.
{
"expo": {
"name": "YourApp",
"scheme": "yourappscheme",
"plugins": [
[
"@web3auth/react-native-sdk",
{
"scheme": "yourappscheme"
}
]
]
}
}
Fix: Run npx expo prebuild --clean to regenerate the native Android files with the newly injected scheme. Unlike desktop environments where a web3auth mpc-tss threshold recovery failed social login error is often an iframe issue, mobile execution relies entirely on the OS-level URL routing table.
Architectural Breakdown: Deep-Linking, Intent Filters, and the OAuth Callback Lifecycle
In the context of a React Native application, especially one managed by the Expo ecosystem, the authentication flow is fundamentally different from a standard web application. On the web, the SDK can manage state within the same browser tab or a popup. On mobile, security models prevent apps from directly capturing user credentials via a raw WebView. Instead, the industry standard is to use ASWebAuthenticationSession on iOS and Android Custom Tabs.
The Role of ASWebAuthenticationSession and Android Custom Tabs
When the @web3auth/react-native-sdk initiates a login, it leverages the expo-web-browser module. This module invokes the platform’s native secure browser component. This component is isolated from the application’s runtime. The crucial moment occurs when the authentication is complete: the Web3Auth server sends a 302 Redirect to a custom URI scheme (e.g., yourappscheme://auth).
The mobile Operating System (OS) acts as the router. It checks its internal registry of URI schemes to decide which application should handle the incoming request. If your application has not explicitly registered this scheme, the OS will either ignore it or offer the user a generic “Open with…” dialog that rarely includes your app.
Android Intent Filters: The Manifest Conflict
On Android, this registration happens in the AndroidManifest.xml via <intent-filter> blocks. In an Expo Managed Workflow, you don’t edit this file directly. Instead, the @web3auth/react-native-sdk plugin in app.json acts as a pre-compiler hook. During npx expo prebuild, it injects the necessary XML:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourappscheme" android:host="auth" />
</intent-filter>
If this block is missing or if the android:scheme doesn’t match the redirectUrl passed to the Web3Auth constructor, the “callback loop” breaks. The browser window closes, but the app never receives the UserOperation or the reconstructed MPC share, leaving the user staring at a loading spinner.
Technical Analysis: Integrating @web3auth/react-native-sdk with expo-linking
To ensure high-fidelity delivery of the cryptographic Shards from the Torus Network, the developer must synchronize several moving parts: the OS registry, the Expo routing logic, and the Web3Auth SDK state.
Using expo-linking for Dynamic URI Resolution
Hard-coding redirect URLs is a common failure point. Instead, utilize expo-linking to resolve the scheme dynamically based on the environment (development, staging, or production).
import * as Linking from 'expo-linking';
// This automatically resolves to 'yourappscheme://' in production
// or 'exp://127.0.0.1:19000/--/' during development
const redirectUrl = Linking.createURL("auth", { scheme: "yourappscheme" });
The WebAuthn Factor on Mobile
While the focus here is on OAuth (Google/Apple), the same infrastructure handles WebAuthn (Passkeys) on mobile. When a user uses a Passkey, the OS invokes the Authenticator service. Once the biometric is verified, the browser component must still redirect back to the app to complete the TSS (Threshold Signature Scheme) reconstruction. Without the correct deep-link configuration, even biometric logins will fail to “return” to the app.
Production-Grade Prevention: Deep-Link Audit and Security Policies
To immunize your Expo builds against callback loop failures, implement a multi-layered verification strategy.
1. The Prebuild Audit
Never rely on npx expo run:android to magically update your schemes. Always perform a clean prebuild when changing app.json plugins.
# Force a clean native rebuild
rm -rf android ios
npx expo prebuild --clean
2. Deep-Link Verification CLI
Use the Android Debug Bridge (ADB) to simulate the callback. If your app doesn’t open when running this command, your app.json configuration is incorrect:
adb shell am start -W -a android.intent.action.VIEW -d "yourappscheme://auth" com.your.bundle.id
On iOS, use the simctl tool:
xcrun simctl openurl booted "yourappscheme://auth"
3. Environment Variables Schema for Schemes
Avoid “Scheme Collision”—where two apps on the same device use the same scheme. Use a unique prefix, typically your reverse-domain name.
| variable | description | example |
|---|---|---|
APP_SCHEME | The internal OS scheme | com.mycompany.myapp |
WEB3AUTH_CLIENT_ID | From dashboard | Bxxxxx... |
BUNDLE_ID | Android/iOS identifier | io.mycompany.app |
4. Callback Handling in App.tsx
Ensure your app is listening for the URL even if it’s already open in the background. The expo-web-browser module’s maybeCompleteAuthSession() is essential for finishing the session in the browser context.
useEffect(() => {
const handleDeepLink = (event: Linking.EventType) => {
// Logic to resume Web3Auth session if the URL is valid
console.log("Deep link received:", event.url);
};
const subscription = Linking.addEventListener('url', handleDeepLink);
return () => subscription.remove();
}, []);
Resolving “ReferenceError: TmMfaAdapter is not defined” in React Native
A frequent companion error during Web3Auth initialization in React Native is the crash:
ReferenceError: Property 'TmMfaAdapter' doesn't exist
at Object.<anonymous> (node_modules/@web3auth/react-native-sdk/dist/index.js)
at require (node_modules/metro-runtime/src/polyfills/require.js)
Cause: Metro Bundler Optimization & Missing Polyfills
React Native’s Hermes engine does not include Node.js core modules (crypto, buffer, stream). When the Torus MFA module attempts to register TmMfaAdapter for multi-factor authentication shards, Metro evaluates the class before its polyfills load.
Solution: Explicit Adapter Registration & Polyfill Setup
- Install the required shim dependencies:
npm install --save react-native-get-random-values @craftzdog/react-native-buffer
- Import polyfills at the very top of your
index.jsorApp.tsx:
import 'react-native-get-random-values';
import { Buffer } from '@craftzdog/react-native-buffer';
global.Buffer = Buffer;
- Initialize the MFA adapter explicitly if using MFA recovery:
import { TmMfaAdapter } from '@web3auth/mfa-adapter';
// Ensure the adapter is bound in scope before SDK init
const mfaAdapter = new TmMfaAdapter({
mfaProvider: 'web3auth',
});
Advanced FAQ Layer
1. What is the difference between Deep Links and Android App Links in this context?
A “Deep Link” (e.g., yourappscheme://) is a custom protocol that anyone can claim, which can lead to “ambiguity” if multiple apps use the same scheme. An “Android App Link” (or iOS Universal Link) uses a standard https:// URL verified by a well-known/assetlinks.json file on your server. While Web3Auth supports both, Deep Links are preferred for the initial OAuth callback because they are simpler to configure and less prone to SSL/DNS-related failures during the reconstruction of MPC shares.
2. Why does the authentication work in the Expo Go app but fail in my standalone build?
Expo Go has its own internal URI scheme (exp://) and a pre-configured AndroidManifest.xml that handles many common callback scenarios. When you build a standalone app (APK/IPA), you are responsible for providing your own scheme. If you forget to update your app.json with your custom scheme and the Web3Auth plugin, the standalone app will not have the necessary intent filters that Expo Go provides by default.
3. Can I use a WebView instead of Custom Tabs to avoid these redirect issues?
Technically, you can, but it is highly discouraged and often blocked by providers. Google, for instance, prevents “embedded browser” logins (WebViews) because they allow the app developer to intercept keystrokes and steal passwords. Custom Tabs and ASWebAuthenticationSession provide a “Shared Cookie Jar” with the system browser, allowing users to remain logged in across apps, and they provide an isolation layer that protects the user’s credentials from the host app. Web3Auth enforces these secure browser sessions to maintain its non-custodial security guarantees.