ExpoAuthenticationSupabase

How to Add Authentication to an Expo React Native App

A practical guide to adding authentication to an Expo app — the options (Supabase, Clerk, Firebase, custom), a Supabase email + OAuth walkthrough, and how to protect routes with Expo Router.

DeveloperMill2 min read

Adding authentication to an Expo React Native app comes down to three decisions: which provider to use, how to store the session securely, and how to gate your screens. For most apps the fastest reliable path is a managed provider (Supabase, Clerk, or Firebase) plus Expo Router for route protection — you avoid building password hashing and token handling yourself.

Pick an auth approach

You have four realistic options:

  • Supabase Auth — email/password, magic links, and OAuth, bundled with a Postgres database. Great when you also need data storage.
  • Clerk — polished drop-in auth UI and user management, fastest to a working sign-in screen.
  • Firebase Authentication — mature, generous free tier, good if you're already in the Google ecosystem.
  • Roll your own — full control, but you own security, sessions, and OAuth. Only worth it for unusual requirements.

For a typical app, start with a managed provider. This guide uses Supabase because it's a common Expo pairing.

Install the dependencies

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

Create the Supabase client

Persist the session so users stay logged in between app launches:

import "react-native-url-polyfill/auto"
import AsyncStorage from "@react-native-async-storage/async-storage"
import { createClient } from "@supabase/supabase-js"

export const supabase = createClient(
  process.env.EXPO_PUBLIC_SUPABASE_URL,
  process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY,
  {
    auth: {
      storage: AsyncStorage,
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: false,
    },
  }
)

Sign users in

Email and password is the simplest starting point:

const { error } = await supabase.auth.signInWithPassword({
  email,
  password,
})

To sign up instead, call supabase.auth.signUp with the same arguments.

Track the session

Listen for auth changes once, near the root of your app, and share the session through context or a store:

supabase.auth.getSession().then(({ data }) => setSession(data.session))

const { data: sub } = supabase.auth.onAuthStateChange((_event, session) => {
  setSession(session)
})

Protect your routes with Expo Router

Gate screens in a layout. Signed-out users get redirected to the sign-in screen; everyone else sees the app:

import { Redirect, Stack } from "expo-router"

export default function AppLayout() {
  const { session, loading } = useAuth()

  if (loading) return null
  if (!session) return <Redirect href="/sign-in" />

  return <Stack />
}

Put your authenticated screens under this layout and your sign-in/sign-up screens in a separate group so the redirect can't loop.

Add OAuth (optional)

Enable a provider in the Supabase dashboard, then trigger the flow with a redirect back to your app's scheme. Make sure your scheme is set in app.json — you can generate a valid one with our Expo App Config Generator.

Skip the boilerplate

If you'd rather not wire all of this up by hand, our Expo auth template ships sign-in, sign-up, session handling, protected routes, and OAuth already connected to Supabase — TypeScript-first and styled with NativeWind.

Frequently asked questions

What is the easiest way to add authentication to an Expo app?

Use a managed auth provider like Supabase, Clerk, or Firebase Authentication. They handle secure password storage, sessions, and OAuth, so you only wire up the UI and a session listener. Supabase is a popular choice because it also gives you a database and works well with Expo.

Can I use OAuth (Google, Apple, GitHub) in Expo?

Yes. Expo supports OAuth through expo-auth-session and through provider SDKs. With Supabase or Clerk you configure the provider in their dashboard and trigger the flow with a redirect back to your app's URL scheme.

How do I protect screens so only logged-in users can see them?

Load the session in a root layout, then redirect based on auth state — send signed-out users to the sign-in screen and signed-in users to the app. With Expo Router you do this in a layout using the Redirect component or by conditionally rendering route groups.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates