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
- Initial Load: When the app starts, it checks the authentication status.
- 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
- 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.