Basic Expo Setup for OpenAdapter

When asking for help in this category, please make sure to provide the following details:

I’m trying to build and integrate a very basic web3auth flow for my react native expo app that I’m running in an android emulator. I’m familiar with web3auth on web but new to mobile and react native expo.

I’ve the below packages and piece of code. And the error I’m running into.
I’ve also added polyfills so as to avoid any compatibility issues.
Any help would be highly appreciated.
Let me know if any more information is needed from my end.

Package.json -
{
“name”: “fortify”,
“main”: “expo-router/entry”,
“version”: “1.0.0”,
“scripts”: {
“start”: “expo start”,
“reset-project”: “node ./scripts/reset-project.js”,
“android”: “expo run:android”,
“ios”: “expo run:ios”,
“web”: “expo start --web”,
“lint”: “expo lint”
},
“dependencies”: {
@expo/vector-icons”: “^15.0.2”,
@react-navigation/bottom-tabs”: “^7.4.0”,
@react-navigation/elements”: “^2.6.3”,
@react-navigation/native”: “^7.1.8”,
@web3auth/react-native-sdk”: “^8.1.0”,
@web3auth/solana-provider”: “^9.7.0”,
“buffer”: “^6.0.3”,
“expo”: “54.0.13”,
“expo-camera”: “~17.0.8”,
“expo-constants”: “~18.0.9”,
“expo-font”: “~14.0.9”,
“expo-haptics”: “~15.0.7”,
“expo-image”: “~3.0.9”,
“expo-linear-gradient”: “^15.0.7”,
“expo-linking”: “~8.0.8”,
“expo-router”: “~6.0.12”,
“expo-secure-store”: “~15.0.7”,
“expo-splash-screen”: “~31.0.10”,
“expo-status-bar”: “~3.0.8”,
“expo-symbols”: “~1.0.7”,
“expo-system-ui”: “~6.0.7”,
“expo-web-browser”: “~15.0.8”,
“react”: “19.1.0”,
“react-dom”: “19.1.0”,
“react-native”: “0.81.4”,
“react-native-gesture-handler”: “~2.28.0”,
“react-native-qrcode-svg”: “^6.3.15”,
“react-native-quick-crypto”: “^0.7.17”,
“react-native-reanimated”: “~4.1.1”,
“react-native-safe-area-context”: “~5.6.0”,
“react-native-screens”: “~4.16.0”,
“react-native-svg”: “15.12.1”,
“react-native-web”: “~0.21.0”,
“react-native-worklets”: “0.5.1”
},
“devDependencies”: {
@types/react”: “~19.1.0”,
“eslint”: “^9.25.0”,
“eslint-config-expo”: “~10.0.0”,
“typescript”: “~5.9.2”
},
“private”: true
}

App.tsx -
import { CHAIN_NAMESPACES } from “@web3auth/base”;
import Web3Auth, { WEB3AUTH_NETWORK } from “@web3auth/react-native-sdk”;
import { SolanaPrivateKeyProvider } from “@web3auth/solana-provider”;
import * as Linking from “expo-linking”;
import * as SecureStore from “expo-secure-store”;
import * as WebBrowser from “expo-web-browser”;
import { useEffect, useRef } from “react”;
import { Platform, StyleSheet, Text, View } from “react-native”;
import { Button } from “…/components/ui”;
import “…/globals”;

const clientId = process.end.clientId;

export default function Index() {
const web3authRef = useRef<Web3Auth | null>(null);

useEffect(() => {
let disposed = false;

const initWeb3Auth = async () => {

  if (!clientId) {
    console.warn("Missing EXPO_PUBLIC_WEB3AUTH_CLIENT_ID for Web3Auth initialization.");
    return;
  }

  // Configure Web3Auth to target Solana on the devnet cluster.
  const chainConfig = {
    chainNamespace: CHAIN_NAMESPACES.SOLANA,
    chainId: "0x3",
    rpcTarget: "https://api.devnet.solana.com",
    displayName: "Solana Devnet",
    blockExplorerUrl: "https://explorer.solana.com",
    ticker: "SOL",
    tickerName: "Solana",
    decimals: 9,
  } as const;

  const privateKeyProvider = new SolanaPrivateKeyProvider({
    config: { chainConfig },
  });

  const redirectUrl = Linking.createURL("auth", { scheme: "fortify" });

  try {
    const web3auth = new Web3Auth(WebBrowser, SecureStore, {
      clientId,
      network: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET,
      redirectUrl: Platform.select({ default: redirectUrl }) ?? redirectUrl,
      privateKeyProvider,
    });

    await web3auth.init();

    if (!disposed) {
      web3authRef.current = web3auth;
    }
  } catch (error) {
    console.error("Failed to initialize Web3Auth", error);
  }
};

initWeb3Auth();

return () => {
  disposed = true;
  web3authRef.current = null;
};

}, []);

const navigateToAuth = () => {
console.log(“Navigating to auth…”);
};

return (

Fortify
Asset Growth Platform

  <View style={styles.buttonContainer}>
    <Button
      title="Get Started"
      onPress={navigateToAuth}
      style={styles.button}
    />
  </View>
</View>

);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: “center”,
alignItems: “center”,
padding: 24,
backgroundColor: “#1E293B”,
},
title: {
fontSize: 48,
fontWeight: “bold”,
color: “#FFFFFF”,
marginBottom: 8,
},
subtitle: {
fontSize: 18,
color: “rgba(255, 255, 255, 0.7)”,
marginBottom: 48,
textAlign: “center”,
},
buttonContainer: {
width: “100%”,
gap: 16,
},
button: {
width: “100%”,
},
});

Globals.js -
global.Buffer = require(“buffer”).Buffer;

// eslint-disable-next-line import/first
import { install } from “react-native-quick-crypto”;

install();

// Needed so that ‘stream-http’ chooses the right default protocol.
global.location = {
protocol: “file:”,
};

global.process.version = “v16.0.0”;
if (!global.process.version) {
global.process = require(“process”);
console.log({ process: global.process });
}

process.browser = true;

metro.config.js -
// Learn more Metro bundler - Expo Documentation
const { getDefaultConfig } = require(“expo/metro-config”);

/** @type {import(‘expo/metro-config’).MetroConfig} */
const config = getDefaultConfig(__dirname);

config.resolver.extraNodeModules = {
buffer: require.resolve(“buffer”)
};

config.transformer.getTransformOptions = () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true
}
});

module.exports = config;

Error faced -
All im seeing is a black screen on the emulator.

Thanks for sharing all the details and code! Here’s a summary of your issue and the relevant context for the support team, along with some initial troubleshooting steps and suggestions.


Issue Summary

  • SDK: Web3Auth Plug and Play (PnP) React Native SDK
  • SDK Version: @web3auth/react-native-sdk v8.1.0
  • Platform: Expo Managed Workflow (with Android emulator)
  • Blockchain: Solana (using @web3auth/solana-provider)
  • Error: App displays a black screen on the emulator after attempting to initialize Web3Auth.
  • Packages Used:
    • expo: 54.0.13
    • expo-web-browser: ~15.0.8
    • expo-secure-store: ~15.0.7
    • react-native: 0.81.4
    • buffer, react-native-quick-crypto, etc. (polyfills)
  • Code Provided: Yes (App.tsx, Globals.js, metro.config.js)
  • Polyfills: Added for buffer, crypto, etc.
  • Redirect URL: Using Linking.createURL(“auth”, { scheme: “fortify” })
  • Client ID: Fetched from process.env (ensure this is set correctly)
  • Expo Scheme: Using “fortify” (should match everywhere)
  • Expo Go: Not mentioned, but remember Web3Auth does not work with Expo Go, only with a custom dev client or EAS build (docs).

Checklist for Support

Provided:

  • SDK details, version, and platform
  • Initialization and polyfill code
  • Error description

Missing (if needed for deeper debugging):

  • Browser Console / Device Logs: Any logs from Metro or the emulator, especially errors or warnings.
  • Screenshots: (Optional) Black screen, logs, etc.
  • app.json: To confirm Expo scheme is set.
  • AndroidManifest.xml: If you have ejected or using a custom dev client, confirm intent filter for scheme.
  • Web3Auth Dashboard: Make sure the redirect URL is registered.

Initial Troubleshooting & Suggestions

  1. Expo Go Limitation:

    • Web3Auth does not work with Expo Go. You must use a Custom Dev Client or build with EAS.
  2. Expo Scheme Setup:

    • In your app.json, ensure:
      {
        "expo": {
          "scheme": "fortify"
        }
      }
      
    • The scheme in Linking.createURL and app.json must match.
  3. Redirect URL Registration:

  4. Check Client ID:

    • Make sure process.env.clientId is correctly set and available in your environment.
  5. Polyfills:

    • Your polyfills look correct, but ensure they are loaded before any Web3Auth imports.
  6. Logs:

    • Add more console.log statements, especially before and after await web3auth.init(), to see where it might hang.
    • Check Metro and device logs for any silent errors.
  7. Example Reference:

  8. Android Emulator:

    • If using a custom dev client, ensure it is built after adding the scheme and all dependencies.

Next Steps

  • If you have not already, try running your app with a Custom Dev Client or EAS build.
  • Double-check your app.json scheme and Web3Auth Dashboard redirect URL.
  • If the issue persists, please provide:
    • Any error logs from Metro/dev client or the emulator.
    • Your app.json contents.
    • Screenshot of the error/logs if possible.

Helpful Links:

Let us know if you need more help or if you can share any logs/screenshots for further debugging!

Note: This response was generated by an AI bot and may require further verification. The Web3Auth support team has tracked this issue and will be responding as soon as possible.

Additionally, we would recommend you to join our biweekly Office Hours to get this resolved even faster. Web3Auth Developer Office Hours

Somehow I made it work by initializing the web3auth once after my page is rendered and it’s working now.

Now the error I’m facing is that after using any social login, it’s throwing me and error saying - Not valid redirect, please whitelist your domain for the clientId.

I’ve my clientID correct and attached is the whitelisted domain on the developers portal.

I’m using RN expo to run my project locally on android emulator.
Here’s my context for the web3auth login.

import React, { createContext, useContext, useMemo, useRef, useState } from "react";

const clientId = process.env.EXPO_PUBLIC_WEB3AUTH_CLIENT_ID;

interface Web3AuthContextType {
  web3auth: any | null;
  isInitialized: boolean;
  isLoading: boolean;
  error: string | null;
  initializeWeb3Auth: () => Promise<void>;
  login: (provider?: string) => Promise<void>;
  logout: () => Promise<void>;
}

const Web3AuthContext = createContext<Web3AuthContextType | undefined>(undefined);

export function Web3AuthProvider({ children }: { children: React.ReactNode }) {
  const web3authRef = useRef<any | null>(null);
  const [isInitialized, setIsInitialized] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Dedicated function to initialize Web3Auth with lazy loading (called when user clicks "Get Started")
  const initializeWeb3Auth = async () => {
    // Prevent duplicate initialization
    if (web3authRef.current || isLoading) {
      console.log("Web3Auth already initialized or initializing");
      return;
    }

    console.log("Web3AuthProvider: Starting lazy initialization...");
    setIsLoading(true);
    setError(null);

    try {
      console.log("Step 1 - Dynamically importing Web3Auth SDK modules...");
      
      // Lazy load all Web3Auth modules only when needed
      const [
        { CHAIN_NAMESPACES },
        Web3AuthModule,
        { SolanaPrivateKeyProvider },
        Linking,
        SecureStore,
        WebBrowser,
      ] = await Promise.all([
        import("@web3auth/base"),
        import("@web3auth/react-native-sdk"),
        import("@web3auth/solana-provider"),
        import("expo-linking"),
        import("expo-secure-store"),
        import("expo-web-browser"),
      ]);

      const Web3Auth = Web3AuthModule.default;
      const { WEB3AUTH_NETWORK } = Web3AuthModule;

      console.log("Step 2 - Checking clientId");
      if (!clientId) {
        throw new Error("Missing EXPO_PUBLIC_WEB3AUTH_CLIENT_ID");
      }

      console.log("Step 3 - Creating chain config");
      const chainConfig = {
        chainNamespace: CHAIN_NAMESPACES.SOLANA,
        chainId: "0x3",
        rpcTarget: "https://api.devnet.solana.com",
        displayName: "Solana Devnet",
        blockExplorerUrl: "https://explorer.solana.com",
        ticker: "SOL",
        tickerName: "Solana",
        decimals: 9,
      } as const;

      console.log("Step 4 - Creating private key provider");
      const privateKeyProvider = new SolanaPrivateKeyProvider({
        config: { chainConfig },
      });

      console.log("Step 5 - Creating redirect URL");
      // Try using just the scheme without path
      const redirectUrl = "fortify://";
      console.log("Redirect URL:", redirectUrl);
      
      // Also log the package name for debugging
      console.log("Package name: com.sushantsuryakantbondle.Fortify");
      console.log("Make sure these URLs are whitelisted in Web3Auth Dashboard:");
      console.log("  1. fortify://");
      console.log("  2. fortify://auth");
      console.log("  3. com.sushantsuryakantbondle.fortify://auth");

      console.log("Step 6 - Creating Web3Auth instance");
      const web3auth = new Web3Auth(WebBrowser, SecureStore, {
        clientId,
        network: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET,
        redirectUrl,
        privateKeyProvider,
      });

      console.log("Step 7 - Calling init()...");
      await web3auth.init();

      console.log("Step 8 - Init completed");
      web3authRef.current = web3auth;
      setIsInitialized(true);
      setIsLoading(false);
      console.log("✓ Web3Auth initialized successfully with lazy loading");
    } catch (err) {
      console.error("Web3AuthProvider: Failed to initialize", err);
      setError(err instanceof Error ? err.message : String(err));
      setIsLoading(false);
      throw err;
    }
  };

  const login = async (provider: string = "google") => {
    // Auto-initialize if not already done
    if (!web3authRef.current && !isInitialized) {
      console.log("Auto-initializing Web3Auth before login...");
      await initializeWeb3Auth();
    }

    if (!web3authRef.current) {
      throw new Error("Web3Auth initialization failed");
    }

    try {
      console.log(`Web3AuthProvider: Logging in with ${provider}...`);
      const loginProvider = await web3authRef.current.login({
        loginProvider: provider as any,
      });
      
      if (loginProvider) {
        console.log("✓ Login successful");
      }
    } catch (err) {
      console.error("Login failed:", err);
      throw err;
    }
  };

  const logout = async () => {
    if (!web3authRef.current) {
      throw new Error("Web3Auth not initialized");
    }

    try {
      console.log("Web3AuthProvider: Logging out...");
      await web3authRef.current.logout();
      console.log("✓ Logout successful");
    } catch (err) {
      console.error("Logout failed:", err);
      throw err;
    }
  };

  const value: Web3AuthContextType = useMemo(
    () => ({
      web3auth: web3authRef.current,
      isInitialized,
      isLoading,
      error,
      initializeWeb3Auth,
      login,
      logout,
    }),
    [isInitialized, isLoading, error]
  );

  return (
    <Web3AuthContext.Provider value={value}>
      {children}
    </Web3AuthContext.Provider>
  );
}

export function useWeb3Auth() {
  const context = useContext(Web3AuthContext);
  if (context === undefined) {
    throw new Error("useWeb3Auth must be used within a Web3AuthProvider");
  }
  return context;
}

Attaching error screen -

Package.json -

{
  "name": "fortify",
  "main": "expo-router/entry",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start",
    "reset-project": "node ./scripts/reset-project.js",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web",
    "lint": "expo lint"
  },
  "dependencies": {
    "@expo/vector-icons": "^15.0.2",
    "@react-navigation/bottom-tabs": "^7.4.0",
    "@react-navigation/elements": "^2.6.3",
    "@react-navigation/native": "^7.1.8",
    "@web3auth/react-native-sdk": "^8.1.0",
    "@web3auth/solana-provider": "^9.7.0",
    "buffer": "^6.0.3",
    "crypto-browserify": "^3.12.1",
    "expo": "54.0.13",
    "expo-camera": "~17.0.8",
    "expo-constants": "~18.0.9",
    "expo-font": "~14.0.9",
    "expo-haptics": "~15.0.7",
    "expo-image": "~3.0.9",
    "expo-linear-gradient": "^15.0.7",
    "expo-linking": "~8.0.8",
    "expo-router": "~6.0.12",
    "expo-secure-store": "~15.0.7",
    "expo-splash-screen": "~31.0.10",
    "expo-status-bar": "~3.0.8",
    "expo-symbols": "~1.0.7",
    "expo-system-ui": "~6.0.7",
    "expo-web-browser": "~15.0.8",
    "react": "19.1.0",
    "react-dom": "19.1.0",
    "react-native": "0.81.4",
    "react-native-gesture-handler": "~2.28.0",
    "react-native-get-random-values": "^1.11.0",
    "react-native-qrcode-svg": "^6.3.15",
    "react-native-quick-crypto": "^0.7.17",
    "react-native-reanimated": "~4.1.1",
    "react-native-safe-area-context": "~5.6.0",
    "react-native-screens": "~4.16.0",
    "react-native-svg": "15.12.1",
    "react-native-web": "~0.21.0",
    "react-native-worklets": "0.5.1",
    "readable-stream": "^4.7.0"
  },
  "devDependencies": {
    "@types/react": "~19.1.0",
    "eslint": "^9.25.0",
    "eslint-config-expo": "~10.0.0",
    "typescript": "~5.9.2"
  },
  "private": true
}

Let me know if anything else if provided from my side so as to resolve the error.