Skip to main content
This Quickstart is for Expo applications. To integrate Auth0 into your React Native application, refer to the React Native Quickstart.

Get Started

1

Create a new Expo project

Create a new Expo project for this quickstart.In your terminal:
npx create-expo-app Auth0ExpoSample --template blank
cd Auth0ExpoSample
This creates a minimal Expo app with the latest SDK, ready for native module integration. The --template blank flag provides a clean starting point without additional boilerplate.
This SDK is NOT compatible with Expo Go because it requires custom native code. You must use npx expo run:ios or npx expo run:android to create a development build.
2

Install the Auth0 SDK

Add the Auth0 React Native SDK to your project.
npx expo install react-native-auth0
Using npx expo install ensures version compatibility with your Expo SDK version. The SDK auto-configures through the Expo plugin system.
3

Configure the Expo Plugin

Configure the Auth0 plugin to handle native iOS and Android configuration automatically.Update your app.json to include the Auth0 plugin:
app.json
{
  "expo": {
    "name": "Auth0ExpoSample",
    "slug": "auth0-expo-sample",
    "version": "1.0.0",
    "ios": {
      "bundleIdentifier": "com.auth0.samples.expo",
      "supportsTablet": true
    },
    "android": {
      "package": "com.auth0.samples.expo",
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "plugins": [
      [
        "react-native-auth0",
        {
          "domain": "{yourDomain}",
          "customScheme": "auth0sample"
        }
      ]
    ]
  }
}
Replace {yourDomain} with your Auth0 domain (you’ll get this in the next step).
Important: You must define bundleIdentifier for iOS and package for Android in your app.json. These identifiers are required for the Auth0 SDK to configure the native projects properly. If you don’t specify a custom scheme, the SDK will use the bundle identifier as the URL scheme.
The customScheme must be lowercase with no special characters. This value is used to construct callback URLs and must be passed to authorize() and clearSession() methods.
4

Configure your Auth0 Application

Create and configure an Auth0 application to work with your Expo app.
  1. Head to the Auth0 Dashboard
  2. Click on Applications > Applications > Create Application
  3. In the popup, enter a name for your app (e.g., Auth0 Expo Sample), select Native as the app type and click Create
  4. Switch to the Settings tab on the Application Details page
  5. Note your Domain and Client ID values
  6. Update the domain value in your app.json plugin configuration with your Auth0 domain
Allowed Callback URLs:
auth0sample://{yourDomain}/ios/auth0sample/callback,
auth0sample://{yourDomain}/android/auth0sample/callback
Allowed Logout URLs:
auth0sample://{yourDomain}/ios/auth0sample/callback,
auth0sample://{yourDomain}/android/auth0sample/callback
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 customScheme must match exactly with the value in your app.json plugin configuration.
Important: Ensure the customScheme in your callback URLs matches exactly with the value in your app.json plugin configuration. Mismatched values will cause authentication to fail.
5

Setup App Component

Setup your main app component based on your chosen implementation approach.
Replace the contents of App.js and wrap your application with the Auth0Provider component:
App.js
import React from 'react';
import {Auth0Provider, useAuth0} from 'react-native-auth0';
import {
  StyleSheet,
  Text,
  View,
  Button,
  Image,
  ActivityIndicator,
} from 'react-native';

function HomeScreen() {
  const {authorize, clearSession, user, isLoading} = useAuth0();

  const handleLogin = async () => {
    try {
      await authorize({customScheme: 'auth0sample', scope: 'openid profile email'});
    } catch (e) {
      console.error('Login error:', e);
    }
  };

  const handleLogout = async () => {
    try {
      await clearSession({customScheme: 'auth0sample'});
    } catch (e) {
      console.error('Logout 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 Expo Sample</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={handleLogout} 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={handleLogin} color="#0066cc" />
          </View>
        </View>
      )}
    </View>
  );
}

export default function App() {
  return (
    <Auth0Provider domain="{yourDomain}" clientId="{yourClientId}">
      <HomeScreen />
    </Auth0Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    backgroundColor: '#fff',
  },
  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,
  },
});
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. The customScheme parameter must match the value in your app.json plugin configuration.
The authorize() method 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. The customScheme parameter must match the value in your app.json plugin configuration.
6

Run your app

Build and run your Expo application on a device or emulator.First, generate the native iOS and Android projects:
npx expo prebuild
Then run on your target platform:For iOS:
npx expo run:ios
For Android:
npx expo run:android
Expected flow:
  1. App launches showing “Log In” button
  2. Tap Log In → Browser opens with Auth0 Universal Login
  3. Complete login (sign up or sign in)
  4. Browser closes → Returns to app automatically
  5. User profile displays with name, email, and avatar
If you make changes to the plugin configuration in app.json, run npx expo prebuild --clean to regenerate the native projects with the updated configuration.
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.
CheckpointYou 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 in the device’s secure storage.

Troubleshooting & Advanced

”Invariant Violation: Native module cannot be null”

This error occurs when attempting to use the SDK with Expo Go.Solution:The Auth0 SDK requires custom native code that isn’t available in Expo Go. Use a development build instead:
npx expo run:ios
# or
npx expo run:android

Callback URL mismatch error

Solution:Verify all three of these match exactly:
  1. customScheme in app.json plugin configuration
  2. customScheme parameter passed to authorize() and clearSession()
  3. Callback URLs in Auth0 Dashboard (Applications → Your App → Settings → Application URIs)

“PKCE not allowed” error

Fix:
  1. Go to Auth0 Dashboard → Applications → Your Application
  2. Change application type to Native
  3. Save changes and try again

Prebuild fails or plugin not applied

Fix:
# Clean and regenerate native projects
npx expo prebuild --clean

iOS build fails with Pod errors

Fix:
cd ios
pod install --repo-update
cd ..
npx expo run:ios

User cancelled error

Handle gracefully in your login function:
const handleLogin = async () => {
  try {
    await authorize({customScheme: 'auth0sample', 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({customScheme: 'auth0sample', scope: 'openid profile email'}, {ephemeralSession: true});
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({customScheme: 'auth0sample', scope: 'openid profile email offline_access'}). This enables automatic token renewal.
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();
  }, []);
};
For production builds, use EAS Build instead of local development builds.Install EAS CLI:
npm install -g eas-cli
Create eas.json in your project root:
eas.json
{
  "cli": {
    "version": ">= 3.0.0"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal"
    },
    "production": {}
  }
}
Build for production:
# Build for both platforms
eas build --platform all

# Or build for specific platform
eas build --platform ios
eas build --platform android

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
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
For production apps, consider using HTTPS callback URLs with Universal Links (iOS) and App Links (Android) instead of custom schemes for enhanced security.