Back to Supabase

Build a Social Auth App with Expo React Native

apps/docs/content/guides/auth/quickstarts/with-expo-react-native-social-auth.mdx

1.26.0419.8 KB
Original Source

This tutorial demonstrates how to build a React Native app with Expo that implements social authentication. The app showcases a complete authentication flow with protected navigation using:

  • Supabase Database - a Postgres database for storing your user data with Row Level Security to ensure data is protected and users can only access their own information.
  • Supabase Auth - enables users to log in through social authentication providers (Apple and Google).

<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-social-auth with the standard template:

bash
npx create-expo-app@latest

cd expo-social-auth

Install the additional dependencies:

bash
npx expo install @supabase/supabase-js @react-native-async-storage/async-storage expo-secure-store expo-splash-screen

Now, create a helper file to initialize the Supabase client for both web and React Native platforms using platform-specific storage adapters: Expo SecureStore for mobile and AsyncStorage for web.

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

<TabPanel id="async-storage" label="AsyncStorage">
<$CodeSample
  path="/auth/expo-social-auth/lib/supabase.web.ts"
  lines={[[1, -1]]}
  meta="name=lib/supabase.web.ts"
/>
</TabPanel> <TabPanel id="secure-store" label="SecureStore">
If you want to encrypt the user's session information, 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.

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

<$CodeSample
  path="/auth/expo-social-auth/lib/supabase.ts"
  lines={[[1, -1]]}
  meta="name=lib/supabase.ts"
/>
</TabPanel> </Tabs>

Set up environment variables

You need the API URL and the anon key copied earlier. These variables are safe to expose in your Expo app since Supabase has Row Level Security enabled on your database.

Create a .env file containing these variables:

<$CodeSample path="/auth/expo-social-auth/.env.template" lines={[[1, -1]]} meta="name=.env" />

Set up protected navigation

Next, you need to protect app navigation to prevent unauthenticated users from accessing protected routes. Use the Expo SplashScreen to display a loading screen while fetching the user profile and verifying authentication status.

Create the AuthContext

Create a React context to manage the authentication session, making it accessible from any component:

<$CodeSample path="/auth/expo-social-auth/hooks/use-auth-context.tsx" lines={[[1, -1]]} meta="name=hooks/use-auth-context.tsx" />

Create the AuthProvider

Next, create a provider component to manage the authentication session throughout the app:

<$CodeSample path="/auth/expo-social-auth/providers/auth-provider.tsx" lines={[[1, -1]]} meta="name=providers/auth-provider.tsx" />

Create the SplashScreenController

Create a SplashScreenController component to display the Expo SplashScreen while the authentication session is loading:

<$CodeSample path="/auth/expo-social-auth/components/splash-screen-controller.tsx" lines={[[1, -1]]} meta="name=components/splash-screen-controller.tsx" />

Create a logout component

Create a logout button component to handle user sign-out:

<$CodeSample path="/auth/expo-social-auth/components/social-auth-buttons/sign-out-button.tsx" lines={[[1, -1]]} meta="name=components/social-auth-buttons/sign-out-button.tsx" />

And add it to the app/(tabs)/index.tsx file used to display the user profile data and the logout button:

<$CodeSample path="/auth/expo-social-auth/app/(tabs)/index.tsx" lines={[[1, -1]]} meta="name=app/(tabs)/index.tsx" />

Create a login screen

Next, create a basic login screen component:

<$CodeSample path="/auth/expo-social-auth/app/login.tsx" lines={[[1, -1]]} meta="name=app/login.tsx" />

Implement protected routes

Wrap the navigation with the AuthProvider and SplashScreenController.

Using Expo Router's protected routes, you can secure navigation: <$CodeSample path="/auth/expo-social-auth/app/_layout.tsx" lines={[[1, -1]]} meta="name=app/_layout.tsx" />

You can now test the app by running:

bash
npx expo prebuild
npx expo start --clear

Verify that the app works as expected. The splash screen displays while fetching the user profile, and the login page appears even when attempting to navigate to the home screen using the Link button.

<Admonition type="note">

By default Supabase Auth requires email verification before a session is created for the user. 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>

Integrate social authentication

Now integrate social authentication with Supabase Auth, starting with Apple authentication. If you only need to implement Google authentication, you can skip to the Google authentication section.

Apple authentication

Start by adding the button inside the login screen:

<$CodeTabs>

tsx
import AppleSignInButton from '@/components/social-auth-buttons/apple/apple-sign-in-button';
…
export default function LoginScreen() {
  return (
    <>
      <Stack.Screen options={{ title: 'Login' }} />
      <ThemedView style={styles.container}><AppleSignInButton /></ThemedView>
    </>
  );
}
…

</$CodeTabs>

For Apple authentication, you can choose between:

For either option, you need to obtain a Service ID from the Apple Developer Console.

<Admonition type="note">

To enable Apple sign-up on Android and Web, you also need to register the tunnelled URL (e.g., https://arnrer1-anonymous-8081.exp.direct) obtained by running:

bash
npx expo start --tunnel

And add it to the Redirect URLs field in your Supabase dashboard Authentication configuration.

For more information, follow the Supabase Login with Apple guide.

</Admonition>

<Tabs scrollable size="large" type="underlined" defaultActiveId="invertase-react-native-apple-authentication" queryGroup="apple-authentication"

<TabPanel id="invertase-react-native-apple-authentication" label="Invertase">
#### Prerequisites

Before proceeding, ensure you have followed the Invertase prerequisites documented in the [Invertase Initial Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/INITIAL_SETUP.md) and the [Invertase Android Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/ANDROID_EXTRA.md).

You need to add two new environment variables to the `.env` file:

```bash
EXPO_PUBLIC_APPLE_AUTH_SERVICE_ID="YOUR_APPLE_AUTH_SERVICE_ID"
EXPO_PUBLIC_APPLE_AUTH_REDIRECT_URI="YOUR_APPLE_AUTH_REDIRECT_URI"
```

#### iOS

Install the `@invertase/react-native-apple-authentication` library:

```bash
npx expo install @invertase/react-native-apple-authentication
```

Then create the iOS specific button component `AppleSignInButton`:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/apple/apple-sign-in-button.ios.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/apple/apple-sign-in-button.ios.tsx"
/>

<Admonition type="note">

To test functionality on the simulator, remove the `getCredentialStateForUser` check:

<$CodeTabs>

```tsx name=components/social-auth-buttons/apple/apple-sign-in-button.ios.tsx
…
const credentialState = await appleAuth.getCredentialStateForUser(appleAuthRequestResponse.user);
…
```

</$CodeTabs>

</Admonition>


Enable the Apple authentication capability in iOS:

<$CodeTabs>

```json name=app.json
{
  "expo": {
    …
    "ios": {
      …
      "usesAppleSignIn": true
      …
    },
    …
  }
}
```

</$CodeTabs>

Add the capabilities to the `Info.plist` file by following the [Expo documentation](https://docs.expo.dev/build-reference/ios-capabilities/#xcode).

<Admonition type="note">

Before testing the app, if you've already built the iOS app, clean the project artifacts:

```bash
npx react-native-clean-project clean-project-auto
```

If issues persist, try completely cleaning the cache, as reported by many users in this [closed issue](https://github.com/invertase/react-native-apple-authentication/issues/23).

</Admonition>

Finally, update the iOS project by installing the Pod library and running the Expo prebuild command:

```bash
cd ios
pod install
cd ..
npx expo prebuild
```

Now test the application on a physical device:

```bash
npx expo run:ios --no-build-cache --device
```

You should see the login screen with the Apple authentication button.

<Admonition type="note">

If you get stuck while working through this guide, refer to the [full Invertase example on GitHub](https://github.com/invertase/react-native-apple-authentication?tab=readme-ov-file#react-native-apple-authentication).

</Admonition>

#### Android

Install the required libraries:

```bash
npx expo install @invertase/react-native-apple-authentication react-native-get-random-values uuid
```

Next, create the Android-specific `AppleSignInButton` component:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/apple/apple-sign-in-button.android.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/apple/apple-sign-in-button.android.tsx"
/>

You should now be able to test the authentication by running it on a physical device or simulator:

```bash
npx expo run:android --no-build-cache
```
</TabPanel> <TabPanel id="react-apple-signin-auth" label="Web">
#### Prerequisites

Before proceeding, as per the mobile options you need an Apple Service ID. To obtain it you can follow the [Invertase Initial Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/INITIAL_SETUP.md) and the [Invertase Android Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/ANDROID_EXTRA.md) mentioned in the Invertase tab.

You also need to add two new environment variables to the `.env` file:

```bash
EXPO_PUBLIC_APPLE_AUTH_SERVICE_ID="YOUR_APPLE_AUTH_SERVICE_ID"
EXPO_PUBLIC_APPLE_AUTH_REDIRECT_URI="YOUR_APPLE_AUTH_REDIRECT_URI"
```

#### Web

Install the required libraries:

```bash
npx expo install react-apple-signin-auth
```

Next, create the Web-specific `AppleSignInButton` component:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/apple/apple-sign-in-button.web.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/apple/apple-sign-in-button.web.tsx"
/>

Test the authentication in your browser using the tunneled HTTPS URL:

```bash
npx expo start --tunnel
```
</TabPanel> <TabPanel id="expo-apple-authentication" label="Expo">
#### Prerequisites

Before proceeding, ensure you have followed the Expo prerequisites documented in the [Expo Setup Guide](https://docs.expo.dev/versions/latest/sdk/apple-authentication/).

#### iOS

Install the `expo-apple-authentication` library:

```bash
npx expo install expo-apple-authentication
```

Enable the Apple authentication capability in iOS and the plugin in `app.json`:

<$CodeTabs>

```json name=app.json
{
  "expo": {
    …
    "ios": {
      …
      "usesAppleSignIn": true
      …
    },
    "plugins": ["expo-apple-authentication"]
    …
  }
}
```

</$CodeTabs>

Then create the iOS specific button component `AppleSignInButton`:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/apple/apple-sign-in-button.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/apple/apple-sign-in-button.tsx"
/>

<Admonition type="note">

The Expo Apple Sign In button does not support the Simulator, so you need to test it on a physical device.

</Admonition>
</TabPanel> </Tabs>

Google authentication

Start by adding the button to the login screen:

<$CodeTabs>

tsx
import GoogleSignInButton from '@/components/social-auth-buttons/google/google-sign-in-button';
…
export default function LoginScreen() {
  return (
    <>
      <Stack.Screen options={{ title: 'Login' }} />
      <ThemedView style={styles.container}><GoogleSignInButton /></ThemedView>
    </>
  );
}
…

</$CodeTabs>

For Google authentication, you can choose between the following options:

<Admonition type="note">

The GN Google Sign In Free doesn't support iOS or Android, as it doesn't allow to pass a custom nonce to the sign-in request.

</Admonition>

For either option, you need to obtain a Web Client ID from the Google Cloud Engine, as explained in the Google Sign In guide.

This guide only uses the @react-oauth/google@latest option for the Web, and the signInWithOAuth for the mobile platforms.

Before proceeding, add a new environment variable to the .env file:

bash
EXPO_PUBLIC_GOOGLE_AUTH_WEB_CLIENT_ID="YOUR_GOOGLE_AUTH_WEB_CLIENT_ID"

<Tabs scrollable size="large" type="underlined" defaultActiveId="web" queryGroup="google-authentication"

<TabPanel id="mobile" label="Mobile">
Create the mobile generic button component `GoogleSignInButton`:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/google/google-sign-in-button.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/google/google-sign-in-button.tsx"
/>

Finally, update the iOS and Android projects by running the Expo prebuild command:

```bash
npx expo prebuild --clean
```

Now test the application on both iOS and Android:

```bash
npx expo run:ios && npx expo run:android
```

You should see the login screen with the Google authentication button.

![Supabase Social Auth example](/docs/img/supabase-expo-social-auth-tabs.png)
</TabPanel> <TabPanel id="web" label="Web">
Install the `@react-oauth/google` library:

```bash
npx expo install @react-oauth/google
```

Enable the `expo-web-browser` plugin in `app.json`:

<$CodeTabs>

```json name=app.json
{
  "expo": {
    …
    "plugins": [
      …
      [
        "expo-web-browser",
        {
          "experimentalLauncherActivity": false
        }
      ]
      …
    ],
    …
  }
}
```

</$CodeTabs>

Then create the iOS specific button component `GoogleSignInButton`:

<$CodeSample
  path="/auth/expo-social-auth/components/social-auth-buttons/google/google-sign-in-button.web.tsx"
  lines={[[1, -1]]}
  meta="name=components/social-auth-buttons/google/google-sign-in-button.web.tsx"
/>

Test the authentication in your browser using the tunnelled HTTPS URL:

```bash
npx expo start --tunnel
```

<Admonition type="note">

To allow the Google Sign In to work, as you did before for Apple, you need to register the tunnelled URL (e.g., `https://arnrer1-anonymous-8081.exp.direct`) obtained to the Authorized JavaScript origins list of your [Google Cloud Console's OAuth 2.0 Client IDs](https://console.cloud.google.com/auth/clients/) configuration.

</Admonition>
</TabPanel> </Tabs>