Add Login to Your React Native Application - Auth0 Docs
Documentation Index
Fetch the complete documentation index at: /llms.txt
Use this file to discover all available pages before exploring further.
This Quickstart is for the React Native framework. To integrate Auth0 into your Expo application, refer to the Expo Quickstart.
Use AI to integrate Auth0
If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:
npx skills add auth0/agent-skills --skill auth0
Then ask your AI assistant:
Add Auth0 authentication to my React Native app
Your AI assistant will automatically create your Auth0 application, fetch credentials, install react-native-auth0, configure native dependencies, and set up your authentication flow. Full agent skills documentation →
Get Started
- Create a new React Native project
Create a new React Native project for this quickstart.In your terminal:
npx @react-native-community/cli init Auth0ReactNativeSample
cd Auth0ReactNativeSample
Configure your project with:
- Name:
Auth0ReactNativeSample - Package name:
com.auth0.samples.reactnative
This creates a React Native app with the latest stable version. The Auth0 SDK requires React Native 0.78.0+ and React 19.0.0+.
- Install the Auth0 SDK
Add the Auth0 React Native SDK to your project.
npm install react-native-auth0
For iOS, install the native dependencies:
cd ios && pod install && cd ..
The SDK auto-links on both platforms. The pod install step installs the required iOS native modules (Auth0.swift, JWTDecode, SimpleKeychain).
- Configure your Auth0 Application
Create and configure an Auth0 application to work with your React Native app.
- Head to the Auth0 Dashboard
- Click on Applications > Applications > Create Application
- In the popup, enter a name for your app (e.g.,
Auth0 React Native Sample), selectNativeas the app type and click Create - Switch to the Settings tab on the Application Details page
- Note your Domain and Client ID values
Allowed Callback URLs:
org.reactjs.native.example.auth0reactnativesample.auth0://{yourDomain}/ios/org.reactjs.native.example.auth0reactnativesample/callback,
com.auth0reactnativesample.auth0://{yourDomain}/android/com.auth0reactnativesample/callback
Allowed Logout URLs:
Replace {yourDomain} with your actual Auth0 domain (e.g., dev-abc123.us.auth0.com).
Allowed Callback URLs are a critical security measure to ensure users are safely returned to your application after authentication. Without a matching URL, the login process will fail, and users will be blocked by an Auth0 error page instead of accessing your app.
Allowed Logout URLs are essential for providing a seamless user experience upon signing out. Without a matching URL, users will not be redirected back to your application after logout and will instead be left on a generic Auth0 page. The URL scheme includes .auth0 after your bundle identifier to ensure the callback is routed to your specific app. For this quickstart, the bundle identifier is org.reactjs.native.example.auth0reactnativesample.
Important: The callback URL scheme must include .auth0 after your bundle identifier (e.g., org.reactjs.native.example.auth0reactnativesample.auth0://). This is required for the SDK to properly handle the authentication callback.
- Configure Native Platforms
Configure both iOS and Android to handle the authentication callback.
Android Configuration: Open android/app/build.gradle and add the manifest placeholders inside defaultConfig:
android {
defaultConfig {
applicationId "com.auth0reactnativesample"
// ... other config
// Add Auth0 manifest placeholders
manifestPlaceholders = [\
auth0Domain: "{yourDomain}",\
auth0Scheme: "${applicationId}.auth0"\
]
}
}
Replace {yourDomain} with your Auth0 domain (e.g., dev-abc123.us.auth0.com).
iOS Configuration:
- AppDelegate.mm (Objective-C)
- AppDelegate.swift (Swift)
Open ios/Auth0ReactNativeSample/AppDelegate.mm and add the URL handling method:
#import <React/RCTLinkingManager.h>
// Add this method inside the @implementation block
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
return [RCTLinkingManager application:app openURL:url options:options];
}
Open ios/Auth0ReactNativeSample/AppDelegate.swift and add the URL handling method:
import React
// Add this method inside the AppDelegate class
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return RCTLinkingManager.application(app, open: url, options: options)
}
Open ios/Auth0ReactNativeSample/Info.plist and add the URL scheme. Add this before the closing </dict> tag:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>CFBundleURLName</key>
<string>auth0</string>
<key>CFBundleURLSchemes</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).auth0</string>
</array>
</dict>
</array>
The URL scheme uses your bundle identifier with .auth0 appended. This ensures the callback is routed to your specific app after authentication completes in the browser.
- Setup App Component
Setup your main app component based on your chosen implementation approach.
Hooks-based (with Provider)
Class-based (without Provider)
Replace the contents of App.tsx and wrap your application with the Auth0Provider component:
import React from 'react';
import {Auth0Provider} from 'react-native-auth0';
import {SafeAreaView, StyleSheet} from 'react-native';
import MainScreen from './src/MainScreen';
const App = () => {
return (
<Auth0Provider
domain="{yourDomain}"
clientId="{yourClientId}">
<SafeAreaView style={styles.container}>
<MainScreen />
</SafeAreaView>
</Auth0Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
export default App;
Replace {yourDomain} with your Auth0 domain and {yourClientId} with your Client ID from the Auth0 Dashboard.
The Auth0Provider initializes the SDK and provides authentication context to all child components via the useAuth0 hook.
Replace the contents of App.tsx with a simple container for your class-based component:
import React from 'react';
import {SafeAreaView, StyleSheet} from 'react-native';
import MainScreen from './src/MainScreen';
const App = () => {
return (
<SafeAreaView style={styles.container}>
<MainScreen />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
export default App;
The class-based approach doesn’t require the Auth0Provider since it uses the Auth0 class instance directly. The Auth0 configuration will be in the MainScreen component.
- Implement Login and Logout
Create a screen component that handles login and logout. You can choose between a hooks-based approach (recommended) or a class-based approach.
Hooks-based (Recommended)
Class-based
Create src/MainScreen.tsx using the useAuth0 hook:
import React from 'react';
import {
View,
Text,
Button,
StyleSheet,
ActivityIndicator,
Image,
} from 'react-native';
import {useAuth0} from 'react-native-auth0';
const MainScreen = () => {
const {authorize, clearSession, user, isLoading} = useAuth0();
const login = async () => {
try {
await authorize({scope: 'openid profile email'});
} catch (e) {
console.error(e);
}
};
const logout = async () => {
try {
await clearSession();
} catch (e) {
console.error(e);
}
};
if (isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0066cc" />
<Text style={styles.loadingText}>Loading...</Text>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>Auth0 React Native</Text>
{user ? (
<View style={styles.profileContainer}>
{user.picture && (
<Image source={{uri: user.picture}} style={styles.avatar} />
)}
<Text style={styles.welcomeText}>Welcome, {user.name}!</Text>
<Text style={styles.emailText}>{user.email}</Text>
<View style={styles.buttonContainer}>
<Button title="Log Out" onPress={logout} color="#dc3545" />
</View>
</View>
) : (
<View style={styles.loginContainer}>
<Text style={styles.subtitle}>
Tap the button below to log in
</Text>
<View style={styles.buttonContainer}>
<Button title="Log In" onPress={login} color="#0066cc" />
</View>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 20,
color: '#333',
},
subtitle: {
fontSize: 16,
color: '#666',
marginBottom: 30,
textAlign: 'center',
},
loadingText: {
marginTop: 10,
fontSize: 16,
color: '#666',
},
profileContainer: {
alignItems: 'center',
},
avatar: {
width: 100,
height: 100,
borderRadius: 50,
marginBottom: 20,
},
welcomeText: {
fontSize: 22,
fontWeight: '600',
marginBottom: 8,
color: '#333',
},
emailText: {
fontSize: 16,
color: '#666',
marginBottom: 30,
},
loginContainer: {
alignItems: 'center',
},
buttonContainer: {
width: 200,
marginTop: 10,
},
});
export default MainScreen;
Create src/MainScreen.tsx using the Auth0 class directly:
import React, {Component} from 'react';
import {
View,
Text,
Button,
StyleSheet,
ActivityIndicator,
Image,
} from 'react-native';
import Auth0, {Credentials} from 'react-native-auth0';
const auth0 = new Auth0({
domain: '{yourDomain}',
clientId: '{yourClientId}',
});
interface User {
name?: string;
email?: string;
picture?: string;
}
interface MainScreenState {
user: User | null;
isLoading: boolean;
}
class MainScreen extends Component<{}, MainScreenState> {
constructor(props: {}) {
super(props);
this.state = {
user: null,
isLoading: true,
};
}
async componentDidMount() {
await this.checkAuthStatus();
}
checkAuthStatus = async () => {
try {
const hasValidCredentials = await auth0.credentialsManager.hasValidCredentials();
if (hasValidCredentials) {
const credentials = await auth0.credentialsManager.getCredentials();
const userInfo = await auth0.auth.userInfo({token: credentials.accessToken});
this.setState({user: userInfo, isLoading: false});
} else {
this.setState({isLoading: false});
}
} catch (e) {
console.error(e);
this.setState({isLoading: false});
}
};
login = async () => {
try {
const credentials: Credentials = await auth0.webAuth.authorize({
scope: 'openid profile email',
});
await auth0.credentialsManager.saveCredentials(credentials);
const userInfo = await auth0.auth.userInfo({token: credentials.accessToken});
this.setState({user: userInfo});
} catch (e) {
console.error(e);
}
};
logout = async () => {
try {
await auth0.webAuth.clearSession();
await auth0.credentialsManager.clearCredentials();
this.setState({user: null});
} catch (e) {
console.error(e);
}
};
render() {
const {user, isLoading} = this.state;
return (
<View style={styles.container}>
<Text style={styles.title}>Auth0 React Native</Text>
{user ? (
<View style={styles.profileContainer}>
{user.picture && (
<Image source={{uri: user.picture}} style={styles.avatar} />
)}
<Text style={styles.welcomeText}>Welcome, {user.name}!</Text>
<Text style={styles.emailText}>{user.email}</Text>
<View style={styles.buttonContainer}>
<Button title="Log Out" onPress={this.logout} color="#dc3545" />
</View>
</View>
) : (
<View style={styles.loginContainer}>
<Text style={styles.subtitle}>
Tap the button below to log in
</Text>
<View style={styles.buttonContainer}>
<Button title="Log In" onPress={this.login} color="#0066cc" />
</View>
</View>
)}
</View>
);
}
}
export default MainScreen;
Replace {yourDomain} with your Auth0 domain and {yourClientId} with your Client ID from the Auth0 Dashboard.
The authorize() method (hooks) or auth0.webAuth.authorize() (class) opens Auth0’s Universal Login in a secure browser (ASWebAuthenticationSession on iOS, Chrome Custom Tabs on Android). The clearSession() method logs the user out and clears both the browser session and stored credentials.
- Run your app
Build and run your React Native application on a device or emulator.
For iOS (requires macOS):
npx react-native run-ios
For Android:
npx react-native run-android
Expected flow:
- App launches showing “Log In” button
- Tap Log In → Browser opens with Auth0 Universal Login
- Complete login (sign up or sign in)
- Browser closes → Returns to app automatically
- User profile displays with name, email, and avatar
iOS Simulator requires a valid Apple Developer account for ASWebAuthenticationSession. For testing on simulator without an account, use a physical device or Android emulator instead.
Checkpoint You should now have a fully functional Auth0 login experience running on your device or emulator. The app uses secure browser authentication and automatically manages credentials.
Troubleshooting & Advanced
Common Issues & Solutions
”Callback URL mismatch” error
Solution:
- Verify the exact URL (including
.auth0suffix) is in Allowed Callback URLs in your Auth0 Dashboard - Ensure both iOS and Android URLs are added
- Check that
{yourDomain}is replaced with your actual Auth0 domain
App doesn’t return after login (iOS)
Fix:
- Check
Info.plisthas theCFBundleURLSchemesentry with$(PRODUCT_BUNDLE_IDENTIFIER).auth0 - Verify
AppDelegate.mmincludes the URL handling method - Ensure the URL scheme matches your bundle identifier
Android build fails
Fix:
- Add
auth0Domainandauth0Schememanifest placeholders tobuild.gradle - Sync project with Gradle files
- Clean build:
./gradlew clean
”PKCE not allowed” error
Fix:
- Go to Auth0 Dashboard → Applications → Your Application
- Change application type to Native
- Save changes and try again
Pod install fails (iOS)
Fix:
- Update CocoaPods:
sudo gem install cocoapods - Update pod repo:
pod install --repo-update - If issues persist, delete
Podfile.lockandios/Podsfolder, then runpod installagain
User cancelled error
Handle gracefully in your login function:
const login = async () => {
try {
await authorize({scope: 'openid profile email'});
} catch (e) {
if (e.message === 'a0.session.user_cancelled') {
// User closed login screen - handle gracefully
console.log('Login cancelled by user');
} else {
console.error('Login failed:', e);
}
}
};
iOS Alert Dialog
On iOS, users see a permission dialog: “App Name” Wants to Use “auth0.com” to Sign In. This is expected behavior from ASWebAuthenticationSession. Users must tap Continue to proceed. To customize this behavior, you can use ephemeral sessions (disables SSO):
await authorize({scope: 'openid profile email'}, {ephemeralSession: true});
Retrieving Access Tokens
Use the getCredentials() method to retrieve tokens for API calls:
import {useAuth0} from 'react-native-auth0';
const MyComponent = () => {
const {getCredentials} = useAuth0();
const callApi = async () => {
try {
const credentials = await getCredentials();
const response = await fetch('https://your-api.com/endpoint', {
headers: {
Authorization: `Bearer ${credentials.accessToken}`,
},
});
// Handle response
} catch (e) {
console.error('Failed to get credentials', e);
}
};
};
Include the offline_access scope during login to receive a refresh token: authorize({scope: 'openid profile email offline_access'}). This enables automatic token renewal.
Check Authentication Status on App Launch
Use hasValidCredentials() to check if the user is already logged in:
import {useAuth0} from 'react-native-auth0';
import {useEffect} from 'react';
const App = () => {
const {hasValidCredentials, getCredentials} = useAuth0();
useEffect(() => {
const checkAuth = async () => {
const isLoggedIn = await hasValidCredentials();
if (isLoggedIn) {
const credentials = await getCredentials();
// User is authenticated, load their data
}
};
checkAuth();
}, []);
};
Production Deployment
Before deploying to production
Use HTTPS callback URLs for enhanced security:
https://{yourDomain}/ios/{bundleId}/callback
https://{yourDomain}/android/{packageName}/callback
Configure Android App Links in Auth0 Dashboard:
- Settings → Advanced Settings → Device Settings
- Add your app’s SHA-256 fingerprint
Configure iOS Universal Links:
- Add Associated Domains capability in Xcode
- Add
webcredentials:{yourDomain}to Associated Domains
Enable biometric authentication for sensitive apps using localAuthenticationOptions in Auth0Provider
Review security settings in Auth0 Dashboard:
Enable OIDC Conformant in Advanced Settings
Configure Token Expiration appropriately
Set up Brute Force Protection
Test on multiple devices and OS versions
Implement proper error handling for network failures