Bitcoin

Setting Up Auth0 Authentication with Expo Router: A Complete Guide

Implementing a robust authentication system is crucial for mobile applications. In this guide, I’ll walk you through setting up Auth0 authentication with Expo Router, creating a seamless and secure user experience.

Prerequisites

Before starting, ensure you have:

  • An Expo project using Expo Router
  • An Auth0 account with a configured application
  • Basic understanding of React Native and TypeScript

Step 1: Install Required Dependencies

First, install the Auth0 React Native SDK:

yarn add react-native-auth0

Step 2: Configure Auth0

Create an auth0.config.js file in your project root:

const config = {
  clientId: "YOUR_AUTH0_CLIENT_ID",
  domain: "YOUR_AUTH0_DOMAIN",
}

export default config

Replace the placeholders with your actual Auth0 credentials.

Step 3: Create an Authentication Context

The authentication context will manage the auth state throughout your app. Create a file called useAuth.tsx in your hooks directory:

import { createContext, useContext, useEffect, useState } from "react"
import { useAuth0 } from "react-native-auth0"
import { router, useSegments, useRootNavigationState } from "expo-router"

// Define the shape of our auth context
type AuthContextType = {
  signIn: () => Promise
  signOut: () => Promise
  isAuthenticated: boolean
  isLoading: boolean
  user: any
  error: Error | null
}

// Create the context with a default value
const AuthContext = createContext(null)

// Provider component that wraps the app
export function AuthProvider({ children }: { children: React.ReactNode }) {
  const { authorize, clearSession, user, error, getCredentials, isLoading } =
    useAuth0()
  const [isAuthenticated, setIsAuthenticated] = useState(false)
  
  const segments = useSegments()
  const navigationState = useRootNavigationState()

  // Check if the user is authenticated and redirect accordingly
  useEffect(() => {
    if (!navigationState?.key) return
    
    const inAuthGroup = segments[0] === "(auth)"
    
    if (isAuthenticated && inAuthGroup) {
      // Redirect authenticated users from auth screens to the main app
      router.replace("/(tabs)")
    } else if (!isAuthenticated && !inAuthGroup) {
      // Redirect unauthenticated users to the login screen
      router.replace("/(auth)/login")
    }
  }, [isAuthenticated, segments, navigationState?.key])

  // Update authentication state when user changes
  useEffect(() => {
    setIsAuthenticated(!!user)
  }, [user])

  // Sign in function
  const signIn = async () => {
    try {
      await authorize()
      const credentials = await getCredentials()
      console.log("Auth credentials:", credentials)
      setIsAuthenticated(true)
    } catch (e) {
      console.error("Login error:", e)
    }
  }

  // Sign out function
  const signOut = async () => {
    try {
      await clearSession()
      setIsAuthenticated(false)
    } catch (e) {
      console.error("Logout error:", e)
    }
  }

  return (
    
      {children}
    
  )
}

// Custom hook to use the auth context
export function useAuth() {
  const context = useContext(AuthContext)
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider")
  }
  return context
}

This context provides:

  • Authentication state management
  • Sign-in and sign-out functions
  • Automatic redirection based on authentication status
  • Access to user information and error states

Step 4: Set Up the Root Layout

Update your app/_layout.tsx file to include the Auth0Provider and AuthProvider:

import { Auth0Provider } from "react-native-auth0"
import config from "@/auth0.config"
import { AuthProvider } from "@/hooks/useAuth"
// Other imports...

export default function RootLayout() {
  // Other code...

  return (
    
      
        
          
            
            
            
          
          
        
      
    
  )
}

Step 5: Create the Authentication Group

Expo Router uses directory-based routing. Create an (auth) directory in your app folder with a layout file:

// app/(auth)/_layout.tsx
import { Stack } from "expo-router"

export default function AuthLayout() {
  return (
    
      
    
  )
}

Step 6: Create the Login Screen

Create a login screen in app/(auth)/login.tsx:

import { ThemedText } from "@/components/ThemedText"
import { useAuth } from "@/hooks/useAuth"
import {
  StyleSheet,
  View,
  TouchableOpacity,
  ActivityIndicator,
} from "react-native"

export default function LoginScreen() {
  const { signIn, isLoading, error } = useAuth()

  return (
    
      
        Welcome to Your App
        Sign in to continue

        
          {isLoading ? (
            
          ) : (
            Sign In
          )}
        

        {error && (
          {error.message}
        )}
      
    
  )
}

// Styles...

Step 7: Create a Profile Screen

Add a profile screen to display user information and provide a logout option:

// app/(tabs)/profile.tsx
import { ThemedText } from "@/components/ThemedText"
import { useAuth } from "@/hooks/useAuth"
import {
  StyleSheet,
  View,
  TouchableOpacity,
  Image,
  ScrollView,
} from "react-native"

export default function ProfileScreen() {
  const { user, signOut, isLoading } = useAuth()

  return (
    
      
        {user?.picture ? (
          
        ) : (
          
            
              {user?.name?.charAt(0) || user?.email?.charAt(0) || "?"}
            
          
        )}

        {user?.name || "User"}
        {user?.email || ""}
      

      {/* User information display */}
      
      
        
          Sign Out
        
      
    
  )
}

// Styles...

Step 8: Update the Tabs Layout

Ensure your tabs layout includes the profile tab and checks authentication:

// app/(tabs)/_layout.tsx
import { useAuth } from "@/hooks/useAuth"
// Other imports...

export default function TabLayout() {
  const { isAuthenticated } = useAuth()

  // Redirect to login if not authenticated
  React.useEffect(() => {
    if (!isAuthenticated) {
      // The AuthProvider will handle the redirect
    }
  }, [isAuthenticated])

  return (
    
      {/* Other tabs */}
       (
            
          ),
        }}
      />
    
  )
}

Step 9: Create a Root Redirect

Finally, create a root index file to handle initial routing:

// app/index.tsx
import { Redirect } from "expo-router"
import { useAuth } from "@/hooks/useAuth"

export default function Index() {
  const { isAuthenticated, isLoading } = useAuth()
  
  // While checking authentication status, don't redirect yet
  if (isLoading) {
    return null
  }
  
  // Redirect based on authentication status
  return isAuthenticated ? (
    
  ) : (
    
  )
}

How It Works

  1. Initial Load: When the app starts, it checks the authentication status.
  2. Authentication Flow:
    • Unauthenticated users are directed to the login screen
    • After successful login, users are redirected to the main app
    • The profile screen displays user information and provides logout functionality
  3. Protected Routes: The AuthProvider automatically protects routes by redirecting unauthenticated users to the login screen.

Benefits of This Approach

  • Clean Separation: Authentication logic is isolated in a dedicated context
  • Route Protection: Automatic redirection based on authentication status
  • Reusable Authentication: The useAuth hook can be used throughout the app
  • Seamless UX: Users are directed to the appropriate screens based on their authentication status

Conclusion

Setting up Auth0 with Expo Router provides a robust authentication system for your mobile application. This approach leverages Expo Router’s group-based routing to create a clean separation between authenticated and unauthenticated content, while the authentication context manages the state and provides a consistent interface for authentication operations.

By following this guide, you’ve implemented a complete authentication flow that handles login, logout, and protected routes in a maintainable and scalable way.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button