# Quickstart with Password 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 `Password` method.
These are our wallet recovery methods you can choose from:

* [Switch to Automatic Recovery](/docs/products/embedded-wallet/react-native/quickstart/automatic)
* **Using Password Recovery**
* [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 (in `OpenfortProvider`). |
| **Secret key** | Secret (server) | Privileged backend actions (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 (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.

:::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.

## Configure environment variables

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

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",
    },
  },
};
```

## Setting up 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.PASSWORD, // or RecoveryMethod.AUTOMATIC
        feeSponsorshipId: undefined, // replace with your fee sponsorship ID
        shieldPublishableKey: Constants.expoConfig?.extra?.openfortShieldPublishableKey,
      }}
      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,
        feeSponsorshipId: undefined, // replace with your fee sponsorship ID
        recoveryMethod: RecoveryMethod.PASSWORD, // or RecoveryMethod.AUTOMATIC
        shieldPublishableKey: Constants.expoConfig?.extra?.openfortShieldPublishableKey,
      }}
      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 } = useOAuth();

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

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

      <Button
        title="Login as Guest"
        onPress={() => signUpGuest()}
      />

      <View style={styles.providersContainer}>
        {[
          { provider: OAuthProvider.Twitter, label: "Twitter" },
          { provider: OAuthProvider.Google, label: "Google" },
          { provider: OAuthProvider.Discord, label: "Discord" },
          { provider: OAuthProvider.Apple, label: "Apple" },
        ].map(({ provider, label }) => (
          <View key={label}>
            <Button
              title={`Login with ${label}`}
              onPress={async () => {
                try {
                  await initOAuth({ provider });
                } catch (error) {
                  console.error("Error logging in with OAuth:", error);
                }
              }}
            />
          </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 ethereum = useEmbeddedEthereumWallet();
  const activeWallet = ethereum.status === 'connected' ? ethereum.activeWallet : null;

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

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

  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={ethereum.status === 'creating' ? "Creating Wallet..." : activeWallet?.address ? "Wallet Created" : "Create Wallet"}
          disabled={ethereum.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"
/>

::::::
