# Quickstart with Automatic recovery

Build a functional authentication and wallet sample app using the Openfort React Native SDK.
This guide assumes you've completed the [Getting Started guide](/docs/products/embedded-wallet/react-native) and have your `OpenfortProvider` configured.

In this guide, we are going to help you set up your wallet recovery method to our `Automatic` method.
These are our wallet recovery methods you can choose from:

* **Using Automatic Recovery**
* [Switch to Password Recovery](/docs/products/embedded-wallet/react-native/quickstart/password)
* [Switch to Passkey Recovery](/docs/products/embedded-wallet/react-native/quickstart/passkey)

Not sure what wallet recovery method you need? Don't miss [our guide](/docs/configuration/recovery-methods).

::::::steps

## Get your Openfort keys

From this point on, you'll need an [Openfort account](https://dashboard.openfort.io/auth/register) to handle project keys and settings.

In the [Openfort Dashboard](https://dashboard.openfort.io), select your desired app and navigate to the [API keys](https://dashboard.openfort.io/api-keys).
There, you'll get the following keys:

**Project keys**: Required for any wallet.

| Key | Exposure | Description |
| --- | --- | --- |
| **Publishable key** | Public (client) | Initialize the SDK (for example, in `OpenfortProvider`). |
| **Secret key** | Secret (server) | Privileged backend actions (for example, sessions, wallet management). Never bundle in the app or commit to git. |

**Shield Keys**: Required for non-custodial wallets.

| Key | Exposure | Description |
| --- | --- | --- |
| **Encryption part** (one-time) | Secret | Server-only key that encrypts/decrypts sensitive wallet data. Store in a key manager; if lost, data can't be recovered. |
| **Publishable key** | Public (client) | Public key the app uses to enable non‑custodial wallet features. Safe to include in client code. |
| **Secret key** | Secret (server) | Server-only key for sensitive wallet operations (for example, starting encryption or recovery). Never ship in the app; store in a secret manager. |

Get the **Project Publishable Key** and the **Shield Publishable Key** and save them for the configuration steps below.

Before you begin, make sure you have set up your **publishable key** app from the Openfort Dashboard.
:::warning
A properly set up publishable key is required for mobile apps and other non-web platforms to allow your app to interact with the Openfort API. Please follow [this guide](/docs/configuration/native-apps) to configure an app client.
:::
Save the secret keys safely for your backend.

## Set up the recovery endpoint (only for Automatic recovery)

Create a backend endpoint that issues a Shield encryption session used to recover wallets.

### Option 1: One‑click deploy

Use our prebuilt endpoint and deploy in one click on Cloudflare or Vercel as a function.

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/openfort-xyz/recovery-endpoint-cloudflare)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/openfort-xyz/recovery-endpoint-vercel\&project-name=shield-recovery-endpoint\&env=SHIELD_PUBLISHABLE_KEY,SHIELD_SECRET_KEY,SHIELD_ENCRYPTION_SHARE\&envDescription=Required%20Shield%20API%20keys%20from%20your%20Openfort%20project\&envLink=https://www.openfort.io/docs)

### Option 2: Implement your own backend

Use this option if you have your backend set up and can add an extra endpoint.

<details>
  <summary style={{ margin: '20px 0 12px 0', fontSize: 16, fontWeight: 500 }}>Endpoint examples for you to copy</summary>

  <div style={{ margin: '12px 0' }}>
    Expose a GET endpoint `/api/protected-create-encryption-session` that returns `{ "session": "<session_id>" }`. Use one of the examples below.
  </div>

  :::code-group

  ```ts [Express.js]
  import express, { Request, Response } from 'express';

  // Initialize the Openfort client  // [!code focus]
  import Openfort from "@openfort/openfort-node";  // [!code focus]
  const openfort = new Openfort(process.env.OPENFORT_SECRET_KEY);  // [!code focus]

  // Use your own middleware (JWT, sessions, API key, etc.) to authenticate the user here
  export const authenticateUser = (req: Request, res: Response, next: NextFunction) => {
    // Example: req.user = { id: 'user_123', email: 'user@example.com' };
    return next();
  };

  const router = express.Router();

  router.get('/api/protected-create-encryption-session', authenticateUser, async (req: Request, res: Response) => {  // [!code focus]
    try {  // [!code focus]
      const session = await openfort.createEncryptionSession(  // [!code focus]
        process.env.OPENFORT_SHIELD_PUBLISHABLE_KEY as string,  // [!code focus]
        process.env.OPENFORT_SHIELD_SECRET_KEY as string,  // [!code focus]
        process.env.OPENFORT_SHIELD_ENCRYPTION_SHARE as string  // [!code focus]
      );  // [!code focus]
      res.status(200).json({ session });  // [!code focus]
    } catch (e) {  // [!code focus]
      console.error(e);  // [!code focus]
      res.status(500).json({ error: 'Internal server error' });  // [!code focus]
    }  // [!code focus]
  });  // [!code focus]

  export default router;
  ```

  ```ts [Next.js (/api/protected-create-encryption-session.ts)]
  import type { NextApiRequest, NextApiResponse } from 'next';

  // Initialize the Openfort client  // [!code focus]
  import Openfort from "@openfort/openfort-node";  // [!code focus]
  const openfort = new Openfort(process.env.OPENFORT_SECRET_KEY);  // [!code focus]

  // Use your own authentication middleware (JWT, sessions, API key, etc.) to authenticate the user here
  function authenticateUser(req: NextApiRequest): boolean {
    // Example: req.user = { id: 'user_123', email: 'user@example.com' };
    return true;
  }

  export default async function handler(req: NextApiRequest, res: NextApiResponse) {  // [!code focus]
    // Authenticate user  // [!code focus]
    if (!authenticateUser(req)) {  // [!code focus]
      return res.status(401).json({ error: 'Unauthorized' });  // [!code focus]
    }  // [!code focus]
    // [!code focus]
    try {  // [!code focus]
      const session = await openfort.createEncryptionSession(  // [!code focus]
        process.env.OPENFORT_SHIELD_PUBLISHABLE_KEY as string,  // [!code focus]
        process.env.OPENFORT_SHIELD_SECRET_KEY as string,  // [!code focus]
        process.env.OPENFORT_SHIELD_ENCRYPTION_SHARE as string  // [!code focus]
      );  // [!code focus]
      res.status(200).json({ session });  // [!code focus]
    } catch (e) {  // [!code focus]
      console.error(e);  // [!code focus]
      res.status(500).json({ error: 'Internal server error' });  // [!code focus]
    }  // [!code focus]
  }  // [!code focus]
  ```

  :::
</details>

After setting up the `/api/protected-create-encryption-session` endpoint, save the full endpoint URL for the next step.

## Configure environment variables

```bash
# .env
# Publishable keys
OPENFORT_PROJECT_PUBLISHABLE_KEY=YOUR_PROJECT_PUBLISHABLE_KEY
OPENFORT_SHIELD_PUBLISHABLE_KEY=YOUR_SHIELD_PUBLISHABLE_KEY

# Automatic recovery endpoint
OPENFORT_SHIELD_RECOVERY_ENDPOINT=https://your-backend.com/api/protected-create-encryption-session

# Fee sponsorship ID
OPENFORT_POLICY_ID=YOUR_POLICY_ID
```

Create `app.config.js` to manage your Openfort keys securely:

```js
// app.config.js
export default {
  expo: {
    name: "openfort-sample",
    slug: "openfort-sample",
    version: "1.0.0",
    platforms: ["ios", "android"],
    extra: {
      openfortPublishableKey: process.env.OPENFORT_PROJECT_PUBLISHABLE_KEY || "YOUR_PROJECT_PUBLISHABLE_KEY",
      openfortShieldPublishableKey: process.env.OPENFORT_SHIELD_PUBLISHABLE_KEY || "YOUR_SHIELD_PUBLISHABLE_KEY",
      openfortShieldRecoveryEndpoint: process.env.OPENFORT_SHIELD_RECOVERY_ENDPOINT || "https://your-backend.com/api/protected-create-encryption-session",
      openfortPolicyId: process.env.OPENFORT_POLICY_ID || "YOUR_POLICY_ID",
    },
  },
};
```

## Replace your Openfort Provider

Replace your basic provider setup with a more comprehensive configuration:

:::code-group

```tsx [With expo-router]
// app/_layout.tsx
import { OpenfortProvider, RecoveryMethod } from "@openfort/react-native";
import Constants from "expo-constants";
import { Stack } from "expo-router";

export default function RootLayout() {
  return (
    <OpenfortProvider
      publishableKey={Constants.expoConfig?.extra?.openfortPublishableKey}
      walletConfig={{
        debug: false,
        recoveryMethod: RecoveryMethod.AUTOMATIC, // or RecoveryMethod.PASSWORD
        feeSponsorshipId: Constants.expoConfig?.extra?.openfortPolicyId,
        shieldPublishableKey: Constants.expoConfig?.extra?.openfortShieldPublishableKey,
        createEncryptedSessionEndpoint: Constants.expoConfig?.extra?.openfortShieldRecoveryEndpoint,
      }}
      verbose={true}
      supportedChains={[
        {
          id: 84532,
          name: 'Base Sepolia',
          nativeCurrency: {
            name: 'Base Sepolia Ether',
            symbol: 'ETH',
            decimals: 18
          },
          rpcUrls: { default: { http: ['https://sepolia.base.org'] } },
        },
        {
          id: 11155111,
          name: 'Sepolia',
          nativeCurrency: {
            name: 'Sepolia Ether',
            symbol: 'ETH',
            decimals: 18
          },
          rpcUrls: { default: { http: ['https://ethereum-sepolia-rpc.publicnode.com'] } },
        },
      ]}
    >
      <Stack>
        <Stack.Screen name="index" />
      </Stack>
    </OpenfortProvider>
  );
}
```

```tsx [Without expo-router]
// App.tsx
import { OpenfortProvider, RecoveryMethod } from "@openfort/react-native";
import Constants from "expo-constants";
import MainApp from "./MainApp";

export default function App() {
  return (
    <OpenfortProvider
      publishableKey={Constants.expoConfig?.extra?.openfortPublishableKey}
      walletConfig={{
        debug: false,
        recoveryMethod: RecoveryMethod.AUTOMATIC, // or RecoveryMethod.PASSWORD
        feeSponsorshipId: Constants.expoConfig?.extra?.openfortPolicyId,
        shieldPublishableKey: Constants.expoConfig?.extra?.openfortShieldPublishableKey,
        createEncryptedSessionEndpoint: Constants.expoConfig?.extra?.openfortShieldRecoveryEndpoint,
      }}
      verbose={true}
      supportedChains={[
        {
          id: 84532,
          name: 'Base Sepolia',
          nativeCurrency: {
            name: 'Base Sepolia Ether',
            symbol: 'ETH',
            decimals: 18
          },
          rpcUrls: { default: { http: ['https://sepolia.base.org'] } },
        },
        {
          id: 11155111,
          name: 'Sepolia',
          nativeCurrency: {
            name: 'Sepolia Ether',
            symbol: 'ETH',
            decimals: 18
          },
          rpcUrls: { default: { http: ['https://ethereum-sepolia-rpc.publicnode.com'] } },
        },
      ]}
    >
      <MainApp />
    </OpenfortProvider>
  );
}
```

:::

## Create the login and user screens

Create the login component that handles multiple authentication methods and the user dashboard component that allows wallet creation and message signing:

:::code-group

```tsx [components/LoginScreen.tsx]
// components/LoginScreen.tsx
import { OAuthProvider, useGuestAuth, useOAuth } from "@openfort/react-native";
import { useEffect } from "react";
import { Button, Text, View, StyleSheet } from "react-native";

export default function LoginScreen() {
  const { signUpGuest } = useGuestAuth();
  const { initOAuth, error: authError } = useOAuth();

  const SELECTED_PROVIDERS = [
    OAuthProvider.Google,
    OAuthProvider.Twitter,
    OAuthProvider.Discord,
    OAuthProvider.Apple,
  ]

  useEffect(() => {
    if (authError) {
      console.error("[Openfort RN] Error logging in with OAuth:", authError);
    }
  }, [authError]);

  const handleSignUpGuest = () => {
    signUpGuest()
        .catch((error) => {
            console.error("[Openfort RN] Error signing up guest:", error);
        });
  };

  const handleLoginWithOAuth = async (provider: OAuthProvider) => {
    initOAuth({ provider });
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Openfort Expo Example</Text>

      <Button
        title="Login as Guest"
        onPress={handleSignUpGuest}
      />

      <View style={styles.providersContainer}>
        {SELECTED_PROVIDERS.map((provider) => (
          <View key={provider.toString()}>
            <Button
              title={`Login with ${provider.toString()}`}
              onPress={() => handleLoginWithOAuth(provider)}
            />
          </View>
        ))}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    gap: 10,
    marginHorizontal: 10,
  },
  title: {
    fontSize: 20,
    fontWeight: "bold",
  },
  providersContainer: {
    display: "flex",
    flexDirection: "column",
    gap: 5,
    margin: 10,
  },
});
```

```tsx [components/UserScreen.tsx]
// components/UserScreen.tsx
import { useCallback } from "react";
import { Alert, Button, ScrollView, Text, View, StyleSheet } from "react-native";
import { useUser, useEmbeddedEthereumWallet, useSignOut } from "@openfort/react-native";

export const UserScreen = () => {
  const { user } = useUser();
  const { signOut } = useSignOut();
  const { wallets, create, status } = useEmbeddedEthereumWallet();
  const activeWallet = wallets?.[0];

  const handleCreateWallet = useCallback(async () => {
    if (activeWallet?.address) {
      Alert.alert("Wallet already created", activeWallet.address);
      return;
    }
    try {
      await create();
      Alert.alert("Wallet created");
    } catch (error) {
      console.error("Error creating wallet", error);
      Alert.alert("Error", "Failed to create wallet");
    }
  }, [activeWallet, create]);

  const handleSignMessage = useCallback(async () => {
    try {
      if (!activeWallet?.address) {
        Alert.alert("Create a wallet first");
        return;
      }
      const provider = await activeWallet.getProvider();
      const result = await provider.request({
        method: "personal_sign",
        params: [
          "Hello from Openfort",
          activeWallet.address,
        ],
      });
      Alert.alert("Message signed", String(result));
    } catch (e) {
      console.error("Error signing message", e);
      Alert.alert("Error", "Failed to sign message");
    }
  }, [activeWallet]);

  if (!user) return null;

  return (
    <ScrollView contentContainerStyle={styles.container}>
      <View style={styles.section}>
        <Text style={styles.label}>User ID</Text>
        <Text>{user.id}</Text>
      </View>

      <View style={styles.section}>
        <Text style={styles.label}>Wallet</Text>
        <Text>{activeWallet?.address ?? "No wallet yet"}</Text>
      </View>

      <View style={styles.buttonWrap}>
        <Button
          title={status === 'creating' ? "Creating Wallet..." : activeWallet?.address ? "Wallet Created" : "Create Wallet"}
          disabled={status === 'creating' || !!activeWallet?.address}
          onPress={handleCreateWallet}
        />
      </View>

      <View style={styles.buttonWrap}>
        <Button
          title="Sign Message"
          disabled={!activeWallet?.address}
          onPress={handleSignMessage}
        />
      </View>

      <View style={styles.buttonWrap}>
        <Button title="Logout" onPress={signOut} />
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    padding: 20,
    alignItems: 'center',
    justifyContent: 'center',
  },
  section: {
    width: '100%',
    maxWidth: 480,
    marginBottom: 12,
  },
  label: {
    fontWeight: 'bold',
    marginBottom: 4,
  },
  buttonWrap: {
    width: '100%',
    maxWidth: 480,
    marginTop: 8,
  },
});
```

:::

## Create your main app logic

Wire everything together with the main app component:

:::code-group

```tsx [With expo-router]
// app/index.tsx
import LoginScreen from '../components/LoginScreen';
import { UserScreen } from '../components/UserScreen';
import { useUser } from '@openfort/react-native';

export default function Index() {
  const { user } = useUser();

  if (user === null) {
    console.warn('Tried to fetch user from Openfort. The user is not authenticated yet.');
  } else {
    console.log('Fetched user from Openfort:', user);
  }

  return !user ? <LoginScreen /> : <UserScreen />;
}
```

```tsx [Without expo-router]
// MainApp.tsx
import LoginScreen from './components/LoginScreen';
import { UserScreen } from './components/UserScreen';
import { useUser } from '@openfort/react-native';

export default function MainApp() {
  const { user } = useUser();

  if (user === null) {
    console.warn('Tried to fetch user from Openfort. The user is not authenticated yet.');
  } else {
    console.log('Fetched user from Openfort:', user);
  }

  return !user ? <LoginScreen /> : <UserScreen />;
}
```

:::

## Test your app

Run your app to test authentication, wallet creation, and message signing:

```bash
npm run android
# or
npm run ios
```

The app shows:

1. **Login Screen**: Authentication options (guest, OAuth providers)
2. **User Dashboard**: Simple wallet creation and message signing

## Activate logging in with authentication providers

You now see the option to log in with various auth providers. These need to be set up in the [Openfort Dashboard](https://dashboard.openfort.io) for them to work.

From the [Openfort Dashboard](https://dashboard.openfort.io), select your desired app and navigate to [Configuration > Providers](https://dashboard.openfort.io/providers).
Select the account types you'd like users to be able to login with: Email, Discord, Epic Games, Google, Apple and more.

## Next steps

* Add gas sponsorship policies for gasless transactions
* Explore advanced wallet features in the [React Native documentation](/docs/products/embedded-wallet/react-native)
* View our React Native sample app to see the full functionality of the SDK:

<HoverCardLink
  description="View our React Native sample. A minimal installation of the Openfort SDK for React Native, using Expo."
  href="https://github.com/openfort-xyz/react-native-auth-sample"
  title="View sample"
  subtitle="React Native Sample"
  img={{
  src: "/img/icons/react-icon.svg",
  alt: "View sample",
}}
  color="#61DAFB"
/>

::::::
