0PricingLogin
React Native Academy · Lesson

Authentication Flow and Protected Routes

Implement sign-up, login, and session persistence, set up a root navigator that switches between auth and app stacks, and protect all data screens from unauthenticated access.

Authentication Architecture Overview

A production auth flow has three states your navigator must handle: loading (checking if a session exists from storage), unauthenticated (show auth screens), and authenticated (show app screens). The key insight is that your root navigator switches between the auth stack and the app stack based on session state — users cannot access data screens without a valid session, and authenticated users cannot land on the login screen.

// Auth states:
// 'loading'        -> Show a splash/loading screen
// 'unauthenticated' -> Show auth stack (Sign In, Sign Up)
// 'authenticated'   -> Show app stack (tabs, data screens)

// The navigator reads session from AuthContext:
const { session, isLoading } = useAuth();

if (isLoading) return <SplashScreen />;

return (
  <NavigationContainer>
    {session ? <AppNavigator /> : <AuthNavigator />}
  </NavigationContainer>
);

AuthContext Setup

Create an AuthContext that provides the current session, user object, sign-in, sign-up, and sign-out functions to the entire component tree. On mount, check AsyncStorage or Supabase's auto-restored session to determine if the user is already logged in from a previous app launch. This prevents the auth screens from flashing before the session is restored.

// src/context/AuthContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { Session } from '@supabase/supabase-js';
import { supabase } from '../services/supabase';

interface AuthContextType {
  session: Session | null;
  isLoading: boolean;
  signOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>(null!);

export function AuthProvider({ children }) {
  const [session, setSession] = useState<Session | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    supabase.auth.getSession().then(({ data }) => {
      setSession(data.session);
      setIsLoading(false);
    });

    const { data: listener } = supabase.auth.onAuthStateChange(
      (_event, session) => setSession(session)
    );
    return () => listener.subscription.unsubscribe();
  }, []);

  const signOut = async () => { await supabase.auth.signOut(); };

  return (
    <AuthContext.Provider value={{ session, isLoading, signOut }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

All lessons in this course

  1. Planning Architecture and Tech Stack
  2. Authentication Flow and Protected Routes
  3. Core Feature: Data Feed with Offline Support
  4. Polishing, Testing, and Shipping to Both Stores
← Back to React Native Academy