Expo is a framework built on React Native that provides many out-of-the-box features, similar to NextJS for web but designed for mobile development. The Para SDK provides a react-native-wallet package that works seamlessly in both React Native bare and Expo workflows, utilizing the device’s Native Passkeys for secure wallet management.

Prerequisites

To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.

Don’t have an API key yet? Request access to the Developer Portal to create API keys, manage billing, teams, and more.

Dependency Installation

Install the required packages for Para SDK integration:

Project Setup

1

Set Up Associated Domains

Configure your app.json file to enable passkey functionality and secure communication:

app.json
{
  "expo": {
    "ios": {
      "bundleIdentifier": "your.app.bundleIdentifier",
      "associatedDomains": [
        "webcredentials:app.beta.getpara.com?mode=developer",
        "webcredentials:app.getpara.com"
      ]
    }
  }
}
Important: Your teamId + bundleIdentifier must be registered with the Para team to set up associated domains. For example, if your Team ID is A1B2C3D4E5 and Bundle Identifier is com.yourdomain.yourapp, provide A1B2C3D4E5.com.yourdomain.yourapp to Para. This is required by Apple for passkey security. Allow up to 24 hours for domain propagation.
2

Configure Metro Bundler

Create or update metro.config.js in your project root to polyfill required Node.js modules:

metro.config.js
const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);

config.resolver.extraNodeModules = {
  crypto: require.resolve("react-native-quick-crypto"),
  buffer: require.resolve("@craftzdog/react-native-buffer"),
  stream: require.resolve("readable-stream"),
};

module.exports = config;
3

Create Custom Entry Point (Optional)

You can create a custom entry point to ensure proper shimming of global modules before the Expo Router entry point. Alternatively you can import the shim at _layout.tsx.

Create a new index.js file in your project root and add the following:

index.js
import "@getpara/react-native-wallet/dist/shim";
import "expo-router/entry";

Update your package.json to use this new entry point:

package.json
{
  "main": "index.js"
}
The shim must be imported before the Expo Router entry point to ensure proper global shimming across all route types, including groups.
4

Prebuild and Run

Since native modules are required, you’ll need to use Expo Development Build to ensure that linking is successful:

npx expo prebuild
npx expo run:ios
npx expo run:android
You cannot use Expo Go as it doesn’t support native module linking. When running via yarn start, switch to development mode by pressing s, then i for iOS or a for Android.

Using the Para SDK

The Para SDK provides two main authentication flows: creating a new user and logging in an existing user. Both flows utilize Native Passkeys for secure and seamless authentication. Follow the steps below to implement these flows in your Expo application.

Beta Testing Credentials In the BETA Environment, you can use any email ending in @test.getpara.com (like dev@test.getpara.com) or US phone numbers (+1) in the format (area code)-555-xxxx (like (425)-555-1234). Any OTP code will work for verification with these test credentials. These credentials are for beta testing only. You can delete test users anytime in the beta developer console to free up user slots.

Create New User

This flow guides you through the process of registering a new user, verifying their email, and setting up their wallet.

1

Initialize Para Client

First, set up the Para client to enable SDK interactions:

import { ParaMobile, Environment } from "@getpara/react-native-wallet";

const para = new ParaMobile(Environment.BETA, YOUR_API_KEY);

Para offers two hosted environments: Environment.BETA (alias Environment.DEVELOPMENT) for testing, and Environment.PROD (alias Environment.PRODUCTION) for live use. Select the environment that matches your current development phase.

2

Register New User

Create a new user account by calling the createUser method. This will automatically send a verification email to the user:

const handleCreateUser = async (email: string): Promise<void> => {
  try {
    await para.createUser({ email });
    console.log("User created. Verification email sent.");
  } catch (error) {
    console.error("Error creating user:", error);
  }
};
3

Verify Email

Once the user receives the verification code, call the verifyEmailBiometricsId method to confirm their email:

const handleVerifyEmail = async (code: string): Promise<string | undefined> => {
  try {
    const biometricsId = await para.verifyEmailBiometricsId(code);
    console.log("Email verified successfully");
    return biometricsId;
  } catch (error) {
    console.error("Error verifying email:", error);
  }
};
4

Register Passkey and Create Wallet

Use the returned biometricsId to register the user’s passkey and create their wallet:

import { webcrypto } from "crypto";

const handleRegisterPasskeyAndCreateWallet = async (
  email: string,
  biometricsId: string
): Promise<{ wallets: any; recoverySecret: string } | undefined> => {
  try {
    await para.registerPasskey(email, biometricsId, webcrypto);
    console.log("Passkey registered successfully");
    const { wallets, recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
    console.log("User wallet created");
    return { wallets, recoverySecret };
  } catch (error) {
    console.error("Error registering passkey or creating wallet:", error);
  }
};
5

Complete New User Flow

Implement the full flow by combining all the previous steps:

const handleNewUserFlow = async (email: string, verificationCode: string): Promise<void> => {
  await handleCreateUser(email);
  const biometricsId = await handleVerifyEmail(verificationCode);
  if (biometricsId) {
    const result = await handleRegisterPasskeyAndCreateWallet(email, biometricsId);
    if (result) {
      console.log("Wallet created:", result.wallets);
      console.log("Recovery Secret:", result.recoverySecret);
      // Securely display or store the recovery secret for the user
    }
  }
};
It’s crucial to securely share the recoverySecret with the user. This secret is necessary for account recovery in case the user loses access to their device. Ensure you have a secure method to display or store this secret for the user.

Login Existing User

This flow demonstrates how to authenticate an existing user using their email and passkey.

1

Initialize Para Client

Ensure the Para client is set up (if not already done):

import { ParaMobile, Environment } from "@getpara/react-native-wallet";

const para = new ParaMobile(Environment.BETA, YOUR_API_KEY);
2

Check User Existence and Login

Verify if the user exists and log them in using their passkey:

const handleLogin = async (email: string): Promise<void> => {
  try {
    const userExists = await para.checkIfUserExists({ email });
    if (userExists) {
      await para.login();
      console.log("User logged in successfully");
    } else {
      console.log("User does not exist. Please create a new account.");
    }
  } catch (error) {
    console.error("Login error:", error);
  }
};

By following these steps, you can implement a secure and user-friendly authentication system in your Expo application using the Para SDK.

Examples

For practical implementations of the Para SDK in Expo environments, check out our GitHub repository:

Next Steps

After integrating Para, you can explore other features and integrations to enhance your Para experience. Here are some resources to help you get started:

Ecosystems

Learn how to use Para with popular Web3 clients and wallet connectors. We’ll cover integration with key libraries for EVM, Solana, and Cosmos ecosystems.

If you’re ready to go live with your Para integration, make sure to review our go-live checklist:

Troubleshooting

If you encounter issues during the integration or usage of the Para SDK in your Expo application, here are some common problems and their solutions:

For a more comprehensive list of solutions, including Expo-specific issues, visit our troubleshooting guide:

Integration Support

If you’re experiencing issues that aren’t resolved by our troubleshooting resources, please contact our team for assistance. To help us resolve your issue quickly, please include the following information in your request:

  1. 1

    A detailed description of the problem you’re encountering.

  2. 2

    Any relevant error messages or logs.

  3. 3

    Steps to reproduce the issue.

  4. 4

    Details about your system or environment (e.g., device, operating system, software version).

Providing this information will enable our team to address your concerns more efficiently.