Back to Supabase

Build a User Management App with Expo React Native

apps/docs/content/guides/getting-started/tutorials/with-expo-react-native.mdx

1.26.047.8 KB
Original Source

<$Partial path="quickstart_intro.mdx" />

<Admonition type="note">

If you get stuck while working through this guide, refer to the full example on GitHub.

</Admonition>

<$Partial path="project_setup.mdx" variables={{ "framework": "exporeactnative", "tab": "mobiles" }} />

Building the app

Start by building the React Native app from scratch.

Initialize a React Native app

Use expo to initialize an app called expo-user-management:

bash
npx create-expo-app -t expo-template-blank-typescript expo-user-management

cd expo-user-management

Then install the additional dependencies:

bash
npx expo install @supabase/supabase-js @rneui/themed expo-sqlite

Now create a helper file to initialize the Supabase client using the API URL and the key that you copied earlier.

These variables are safe to expose in your Expo app since Supabase has Row Level Security enabled on your Database.

<Tabs scrollable size="large" type="underlined" defaultActiveId="local-storage" queryGroup="auth-store"

<TabPanel id="local-storage" label="LocalStorage">
<$CodeSample
path="/user-management/expo-user-management/lib/supabase.ts"
lines={[[1, -1]]}
meta="name=lib/supabase.ts"
/>
</TabPanel> <TabPanel id="secure-store" label="SecureStore">
If you wish to encrypt the user's session information, you can use `aes-js` and store the encryption key in [Expo SecureStore](https://docs.expo.dev/versions/latest/sdk/securestore). The [`aes-js` library](https://github.com/ricmoo/aes-js) is a reputable JavaScript-only implementation of the AES encryption algorithm in CTR mode. A new 256-bit encryption key is generated using the `react-native-get-random-values` library. This key is stored inside Expo's SecureStore, while the value is encrypted and placed inside AsyncStorage.

Make sure that:

- You keep the `expo-secure-storage`, `aes-js` and `react-native-get-random-values` libraries up-to-date.
- Choose the correct [`SecureStoreOptions`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestoreoptions) for your app's needs. E.g. [`SecureStore.WHEN_UNLOCKED`](https://docs.expo.dev/versions/latest/sdk/securestore/#securestorewhen_unlocked) regulates when the data can be accessed.
- Carefully consider optimizations or other modifications to the above example, as those can lead to introducing subtle security vulnerabilities.

Install the necessary dependencies in the root of your Expo project:

```bash
npm install @supabase/supabase-js
npm install @rneui/themed @react-native-async-storage/async-storage
npm install aes-js react-native-get-random-values
npm install --save-dev @types/aes-js
npx expo install expo-secure-store
```

Implement a `LargeSecureStore` class to pass in as Auth storage for the `supabase-js` client:

<$CodeTabs>

```ts name=lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from 'expo-secure-store';
import * as aesjs from 'aes-js';
import 'react-native-get-random-values';

// As Expo's SecureStore does not support values larger than 2048
// bytes, an AES-256 key is generated and stored in SecureStore, while
// it is used to encrypt/decrypt values stored in AsyncStorage.
class LargeSecureStore {
  private async _encrypt(key: string, value: string) {
    const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));

    const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
    const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));

    await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));

    return aesjs.utils.hex.fromBytes(encryptedBytes);
  }

  private async _decrypt(key: string, value: string) {
    const encryptionKeyHex = await SecureStore.getItemAsync(key);
    if (!encryptionKeyHex) {
      return encryptionKeyHex;
    }

    const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
    const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));

    return aesjs.utils.utf8.fromBytes(decryptedBytes);
  }

  async getItem(key: string) {
    const encrypted = await AsyncStorage.getItem(key);
    if (!encrypted) { return encrypted; }

    return await this._decrypt(key, encrypted);
  }

  async removeItem(key: string) {
    await AsyncStorage.removeItem(key);
    await SecureStore.deleteItemAsync(key);
  }

  async setItem(key: string, value: string) {
    const encrypted = await this._encrypt(key, value);

    await AsyncStorage.setItem(key, encrypted);
  }
}

const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL
const supabasePublishableKey = YOUR_REACT_NATIVE_SUPABASE_PUBLISHABLE_KEY

const supabase = createClient(supabaseUrl, supabasePublishableKey, {
  auth: {
    storage: new LargeSecureStore(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});
```

</$CodeTabs>
</TabPanel> </Tabs>

Set up a login component

Set up a React Native component to manage logins and sign ups. Users should be able to sign in with their email and password.

<$CodeSample path="/user-management/expo-user-management/components/Auth.tsx" lines={[[1, -1]]} meta="name=components/Auth.tsx" />

<Admonition type="note">

By default Supabase Auth requires email verification before a session is created for the users. To support email verification you need to implement deep link handling!

While testing, you can disable email confirmation in your project's email auth provider settings.

</Admonition>

Account page

After a user signs in, you can let them to edit their profile details and manage their account.

Create a new component for that called Account.tsx.

<$CodeSample path="/user-management/expo-user-management/components/Account.tsx" lines={[[1, -1]]} meta="name=components/Account.tsx" />

Launch!

Now that you have all the components in place, update App.tsx:

<$CodeSample path="/user-management/expo-user-management/App.tsx" lines={[[1, -1]]} meta="name=App.tsx" />

Once that's done, run this in a terminal window:

bash
npm start

And then press the appropriate key for the environment you want to test the app in and you should see the completed app.

Bonus: Profile photos

Every Supabase project is configured with Storage for managing large files like photos and videos.

Additional dependency installation

You need an image picker that works on the environment you are building the project for, this example uses expo-image-picker.

bash
npx expo install expo-image-picker

Create an upload widget

Create an avatar for the user so that they can upload a profile photo. Start by creating a new component:

<$CodeSample path="/user-management/expo-user-management/components/Avatar.tsx" lines={[[1, -1]]} meta="name=components/Avatar.tsx" />

Add the new widget

And then add the widget to the Account page. The Account.tsx component shown earlier already includes the Avatar component when using the full example code.

Now run the prebuild command to get the application working on your chosen platform.

bash
npx expo prebuild

At this stage you have a fully functional application!