← Files Auth0ARCHIVED FILE

skills/auth0/references/framework-react-native/index.md

19.9 KB · Sep 30, 2026 · 23:15 UTC

↓ Download file

# Auth0 React Native Integration

Add authentication to React Native and Expo mobile applications using react-native-auth0.

## Prerequisites

- React Native or Expo application
- Auth0 account and application configured as Native type
- If Auth0 isn't set up yet, set it up first with the Auth0 CLI (`auth0 login`, then `auth0 apps create`)

## When NOT to Use

- **Expo managed workflow** - Use the Auth0 integration workflow for Expo (config plugin)
- **React web applications** - Use the Auth0 integration workflow for React SPAs (Vite/CRA)
- **React Server Components** - Use the Auth0 integration workflow for Next.js applications
- **Non-React native apps** - Use platform-specific SDKs (Swift for iOS, Kotlin for Android)
- **Backend APIs** - Use JWT validation libraries for your server language

## Files to Change

These are the files a bare React Native integration touches. Use the table as your starting scope: read
these paths directly instead of searching the project from scratch, but confirm the real entry point and
the actual iOS target directory name before editing — `ios/{YourApp}/` varies per project. You do not
need to read anything under `node_modules/` to confirm the SDK's API.

| File | Change |
|------|--------|
| `package.json` | add `react-native-auth0` |
| `.env` | `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID` |
| `App.tsx` (or your root component) | `Auth0Provider` + `useAuth0`, guarded by `isLoading` |
| `android/app/build.gradle` | `manifestPlaceholders` (`auth0Domain`, `auth0Scheme`) |
| `ios/{YourApp}/Info.plist` | `CFBundleURLTypes` entry |

You do **not** need to touch `MainActivity`, `MainApplication`, `AndroidManifest.xml`, `AppDelegate`,
or the Xcode project — the SDK's own manifest merge and autolinking handle those.

## Quick Start Workflow

### 1. Install SDK

**Expo:**
```bash
npx expo install react-native-auth0
```

**React Native CLI:**
```bash
npm install react-native-auth0
npx pod-install  # iOS only
```

### 2. Configure Environment

**For automated setup with Auth0 CLI**, see the Setup Guide section below for complete scripts.

**For manual setup:**

Create `.env`:

```bash
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-client-id
```

Read these with `react-native-dotenv`, which exposes them on `process.env` — the accessor every example
below uses. (If you prefer `react-native-config`, note it exposes values on an imported `Config` object
instead, so substitute `Config.AUTH0_CLIENT_ID` for `process.env.AUTH0_CLIENT_ID` throughout.)

Never hardcode the Client ID into a `.ts`/`.tsx` file. That includes a config module holding the
literal, and it includes fallbacks — write `process.env.AUTH0_CLIENT_ID!`, never
`process.env.AUTH0_CLIENT_ID ?? 'your-client-id'`, because a default puts the literal straight back
into source. Add `.env` to `.gitignore`.

The `!` only silences the TypeScript error; it is erased at compile time and does not check anything at
runtime. If the variable is unset the SDK receives `undefined` and login fails, so make sure `.env` is
loaded — or add an explicit throw at startup if you want to fail fast.

The Android `manifestPlaceholders` and iOS `Info.plist` values (step 3) are a separate matter — those
are native build config, not source, and the domain there is not a secret.

### 3. Configure Native Platforms

The callback URL scheme is derived from the bundle identifier / application id — you do not need to
read the SDK's Swift, Kotlin, or TypeScript sources to work this out. For a bundle id of
`com.example.app` and tenant `your-tenant.auth0.com`:

| Platform | Scheme | Callback URL to register in the Auth0 Dashboard |
|----------|--------|------------------------------------------------|
| iOS | `com.example.app.auth0` | `com.example.app.auth0://your-tenant.auth0.com/ios/com.example.app/callback` |
| Android | `com.example.app.auth0` | `com.example.app.auth0://your-tenant.auth0.com/android/com.example.app/callback` |

Both platforms append `.auth0` to the bundle id / application id — the SDK defines that suffix
internally (`APPLICATION_ID_SUFFIX = '.auth0'`), so `auth0Scheme` must be `${applicationId}.auth0`,
not the bare `${applicationId}`. On iOS the equivalent is `$(PRODUCT_BUNDLE_IDENTIFIER).auth0`.

All values are lowercase with no trailing slash. Pass a `customScheme` to `authorize()` /
`clearSession()` only when you deliberately override these defaults.

**iOS** - Update `ios/{YourApp}/Info.plist`:

```xml
<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>
```

**Android** — add `manifestPlaceholders` to `android/app/build.gradle` inside
`android { defaultConfig { ... } }`. This is the preferred route: the SDK ships its own
`RedirectActivity` and merges it into your manifest, so these two values are all it needs.

```groovy
android {
    defaultConfig {
        manifestPlaceholders = [
            auth0Domain: "YOUR_AUTH0_DOMAIN",
            auth0Scheme: "${applicationId}.auth0"
        ]
    }
}
```

Only declare the activity yourself if you need to override the merged one:

```xml
<activity
    android:name="com.auth0.android.provider.RedirectActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="YOUR_AUTH0_DOMAIN"
            android:pathPrefix="/android/${applicationId}/callback"
            android:scheme="${applicationId}.auth0" />
    </intent-filter>
</activity>
```

**Expo** - Update `app.json`:

```json
{
  "expo": {
    "scheme": "your-app-scheme",
    "ios": {
      "bundleIdentifier": "com.yourcompany.yourapp"
    },
    "android": {
      "package": "com.yourcompany.yourapp"
    }
  }
}
```

### 4. Add Authentication with Auth0Provider

Wrap your app with `Auth0Provider`:

```typescript
import React from 'react';
import { Auth0Provider } from 'react-native-auth0';
import App from './App';

export default function Root() {
  return (
    <Auth0Provider
      domain={process.env.AUTH0_DOMAIN}
      clientId={process.env.AUTH0_CLIENT_ID}
    >
      <App />
    </Auth0Provider>
  );
}
```

### 5. Use the useAuth0 Hook

```typescript
import React from 'react';
import { View, Button, Text, ActivityIndicator } from 'react-native';
import { useAuth0 } from 'react-native-auth0';

export default function App() {
  const { user, authorize, clearSession, isLoading } = useAuth0();

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

  const logout = async () => {
    try {
      await clearSession();
    } catch (error) {
      console.error('Logout error:', error);
    }
  };

  if (isLoading) {
    return <ActivityIndicator />;
  }

  return (
    <View>
      {user ? (
        <>
          <Text>Welcome, {user.name}!</Text>
          <Text>{user.email}</Text>
          <Button title="Logout" onPress={logout} />
        </>
      ) : (
        <Button title="Login" onPress={login} />
      )}
    </View>
  );
}
```

### 6. Verify

Verification has two parts, because a typecheck proves nothing about native configuration.

**Source** — run the cheapest check that proves the JavaScript compiles:

```bash
npm run typecheck   # or: npx tsc --noEmit
```

**Native configuration** — a typecheck never reads the native projects, so verify these by reading the
files back:

- `android/app/build.gradle` sets `auth0Domain` and `auth0Scheme` in `defaultConfig.manifestPlaceholders`
- `auth0Scheme` is `${applicationId}.auth0`, matching the callback URL registered in the Dashboard
- `ios/{YourApp}/Info.plist` has a `CFBundleURLTypes` entry with `$(PRODUCT_BUNDLE_IDENTIFIER).auth0`

Launching a simulator (`npx react-native run-ios` / `run-android`) takes many minutes and needs a
configured native toolchain, so only do it when the user asks to exercise the login flow on a device —
it is the only way to prove the redirect actually resolves. See the Testing section below for the manual
flow to walk through when you do.

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Forgot to wrap app with Auth0Provider | All components using useAuth0() must be children of Auth0Provider |
| Forgot to configure deep linking | Add URL scheme to iOS Info.plist and Android AndroidManifest.xml (see Step 3) |
| Callback URL mismatch | Ensure callback URL in Auth0 Dashboard matches your app's URL scheme (e.g., `com.yourapp.auth0://YOUR_DOMAIN/ios/com.yourapp/callback`) |
| iOS build fails after install | Run `npx pod-install` to link native dependencies |
| App created as SPA type in Auth0 | Must be Native application type for mobile apps |
| Not handling auth errors | Wrap authorize/clearSession calls in try-catch blocks |
| Deep link not working on Android | Verify `android:exported="true"` is set on RedirectActivity |

## Related Capabilities

- Auth0 setup — run the CLI: `auth0 login`, then `auth0 apps create`
- Migrate from another auth provider → ask for migration (migrate)
- Multi-factor authentication → ask for MFA (feature:mfa)
- Manage Auth0 resources from the terminal → the Auth0 CLI (`tooling-cli`)

## Quick Reference

**Core Hook API** (v5) — this is the complete surface for a login/logout integration. Treat it as
authoritative: you do not need to read `.d.ts` files, the SDK README, or anything else under
`node_modules/` to confirm these names.

- `useAuth0()` - Main hook for authentication
- `authorize()` - Initiate login
- `clearSession()` - Logout
- `user` - User profile object
- `error` - Last authentication error, or `null`
- `getCredentials()` - Get tokens for API calls
- `isLoading` - Loading state; guard auth-dependent UI on this

**Common Use Cases:**
- Login/Logout → See Step 5 above
- Secure token storage → Automatic with `Auth0Provider`
- Biometric authentication → see the Biometric Auth section below
- API calls with tokens → see the Calling APIs section below
- Token refresh → Automatic with `getCredentials()`

## References

- [Auth0 React Native SDK Documentation](https://auth0.com/docs/libraries/react-native-auth0)
- [Auth0 React Native Quickstart](https://auth0.com/docs/quickstart/native/react-native)
- [SDK GitHub Repository](https://github.com/auth0/react-native-auth0)

---

## Testing

### iOS Testing

1. Run the app: `npx react-native run-ios` or `npx expo run:ios`
2. Tap "Login" button
3. Safari opens with Auth0 Universal Login
4. Complete authentication
5. App opens via deep link with user authenticated
6. Tap "Logout" and verify session cleared

### Android Testing

1. Run the app: `npx react-native run-android` or `npx expo run:android`
2. Tap "Login" button
3. Chrome Custom Tabs opens with Auth0 login
4. Complete authentication
5. App resumes via intent filter with user authenticated
6. Tap "Logout" and verify session cleared

---

## Common Issues

| Issue | Solution |
|-------|----------|
| Deep link not working (iOS) | Check `CFBundleURLSchemes` matches bundle identifier exactly |
| Deep link not working (Android) | Verify `android:scheme` and `android:host` in AndroidManifest.xml |
| "Invalid state" error | Clear app data and reinstall. Check callback URLs match configuration |
| Login opens but doesn't return to app | Ensure deep linking is properly configured and tested |
| Expo build fails | Run `npx expo prebuild` to generate native configuration |
| iOS builds fail after pod install | Run `cd ios && pod deintegrate && pod install` |

---

## Security Considerations

- **Use secure storage** - Credentials are stored securely using Keychain (iOS) and Keystore (Android)
- **HTTPS only** - Auth0 requires HTTPS callback URLs (except localhost for dev)
- **Validate tokens on backend** - Never trust client-side token validation
- **Use PKCE** - Enabled by default with react-native-auth0
- **Implement biometric authentication** - Use react-native-biometrics with Auth0 for enhanced security
- **Handle token expiration** - Implement refresh token logic with `getCredentials()`

---

## Related Capabilities

- Auth0 setup — run the CLI: `auth0 login`, then `auth0 apps create`
- Migrate from another auth provider → ask for migration (migrate)
- Multi-factor authentication → ask for MFA (feature:mfa)
- Passkey authentication → ask for MFA/passkeys (feature:mfa)
- B2B multi-tenancy support → ask for Organizations (feature:organizations)

---

## References

- [React Native Auth0 SDK Documentation](https://auth0.com/docs/libraries/auth0-react-native)
- [React Native Auth0 SDK GitHub](https://github.com/auth0/react-native-auth0)
- [Auth0 React Native Quickstart](https://auth0.com/docs/quickstart/native/react-native)
- [React Native Deep Linking](https://reactnative.dev/docs/linking)
- [Expo Deep Linking](https://docs.expo.dev/guides/deep-linking/)

---

## Common Patterns

### Protected Screen with Navigation

```tsx
import { useAuth0 } from 'react-native-auth0';
import { useEffect } from 'react';
import { NavigationProp } from '@react-navigation/native';

export function ProtectedScreen({ navigation }: { navigation: NavigationProp<any> }) {
  const { user, isLoading } = useAuth0();

  useEffect(() => {
    if (!isLoading && !user) {
      navigation.navigate('Login');
    }
  }, [isLoading, user, navigation]);

  if (isLoading) {
    return <ActivityIndicator />;
  }

  if (!user) {
    return null;
  }

  return (
    <View>
      <Text>Protected Content</Text>
      <Text>User ID: {user.sub}</Text>
    </View>
  );
}
```

---

### Get User Profile

```tsx
import { useAuth0 } from 'react-native-auth0';
import { View, Text, Image } from 'react-native';

export function ProfileScreen() {
  const { user } = useAuth0();

  if (!user) {
    return <Text>Please log in</Text>;
  }

  return (
    <View>
      {user.picture && (
        <Image
          source={{ uri: user.picture }}
          style={{ width: 100, height: 100, borderRadius: 50 }}
        />
      )}
      <Text>Name: {user.name}</Text>
      <Text>Email: {user.email}</Text>
      <Text>Email Verified: {user.email_verified ? 'Yes' : 'No'}</Text>
      <Text>User ID: {user.sub}</Text>
    </View>
  );
}
```

---

### Call Protected API

```tsx
import { useAuth0 } from 'react-native-auth0';
import { useState } from 'react';
import { View, Button, Text } from 'react-native';

export function ApiTestScreen() {
  const { getCredentials } = useAuth0();
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  const callApi = async () => {
    try {
      const credentials = await getCredentials();

      const response = await fetch('https://api.example.com/data', {
        headers: {
          Authorization: `Bearer ${credentials.accessToken}`
        }
      });

      const json = await response.json();
      setData(json);
    } catch (err) {
      setError(err.message);
    }
  };

  return (
    <View>
      <Button title="Call API" onPress={callApi} />
      {error && <Text>Error: {error}</Text>}
      {data && <Text>{JSON.stringify(data, null, 2)}</Text>}
    </View>
  );
}
```

**Note:** To call APIs, configure `audience` parameter:

```tsx
const login = async () => {
  await authorize({
    audience: 'https://your-api-identifier',
    scope: 'openid profile email'
  });
};
```

---

### Silent Authentication

```tsx
import { useAuth0 } from 'react-native-auth0';
import { useEffect } from 'react';

export function App() {
  const { getCredentials, user } = useAuth0();

  useEffect(() => {
    // Attempt silent authentication on app start
    const checkAuth = async () => {
      try {
        await getCredentials();
      } catch (e) {
        // User not logged in, do nothing
      }
    };

    if (!user) {
      checkAuth();
    }
  }, []);

  // Rest of your app...
}
```

---

### Custom Login Options

```tsx
const login = async () => {
  await authorize({
    scope: 'openid profile email offline_access',
    audience: 'https://your-api-identifier',
    connection: 'google-oauth2', // Optional: force specific connection
    prompt: 'login', // Force re-authentication
  });
};
```

---

## Configuration Options

### Complete Auth0Provider Configuration

```tsx
<Auth0Provider
  domain="your-tenant.auth0.com"
  clientId="your-client-id"
>
  <App />
</Auth0Provider>
```

### Complete authorize() Options

```tsx
await authorize({
  scope: 'openid profile email offline_access',
  audience: 'https://your-api-identifier',
  connection: 'Username-Password-Authentication', // Optional
  prompt: 'login', // or 'consent'
  screen_hint: 'signup', // Show signup instead of login
  max_age: 300, // Force re-auth if session older than 5 minutes
});
```

---

---

# Auth0 React Native Setup Guide

Setup instructions for React Native and Expo mobile applications.

---

## Quick Setup

### For Expo

```bash
# Install SDK
npx expo install react-native-auth0

# Configure app.json
# Add scheme, bundleIdentifier, and package
```

### For React Native CLI

```bash
# Install SDK
npm install react-native-auth0

# iOS: Install pods
cd ios && pod install && cd ..

# Configure iOS Info.plist and Android AndroidManifest.xml
```

---

## Manual Setup

### 1. Install SDK

**Expo:**
```bash
npx expo install react-native-auth0
```

**React Native CLI:**
```bash
npm install react-native-auth0
npx pod-install  # iOS only
```

### 2. Create Auth0 Native Application

Via CLI:
```bash
auth0 login
auth0 apps create --name "My Mobile App" --type native \
  --auth-method None \
  --callbacks "com.yourcompany.yourapp.auth0://YOUR_DOMAIN/ios/com.yourcompany.yourapp/callback,com.yourcompany.yourapp.auth0://YOUR_DOMAIN/android/com.yourcompany.yourapp/callback" \
  --logout-urls "com.yourcompany.yourapp.auth0://YOUR_DOMAIN/ios/com.yourcompany.yourapp/callback,com.yourcompany.yourapp.auth0://YOUR_DOMAIN/android/com.yourcompany.yourapp/callback" \
  --metadata "created_by=agent_skills"
```

Via Dashboard:
1. Create **Native** application type
2. Configure callback URLs with your app scheme
3. Copy domain and client ID

### 3. Configure iOS

Update `ios/{YourApp}/Info.plist`:

```xml
<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>
```

### 4. Configure Android

Preferred — set `manifestPlaceholders` in `android/app/build.gradle` (the SDK merges in its own
`RedirectActivity`, so this is all it needs):

```groovy
android {
    defaultConfig {
        manifestPlaceholders = [
            auth0Domain: "YOUR_DOMAIN",
            auth0Scheme: "${applicationId}.auth0"
        ]
    }
}
```

Only declare the activity yourself in `android/app/src/main/AndroidManifest.xml` if you need to
override the merged one:

```xml
<activity android:name="com.auth0.android.provider.RedirectActivity" android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
      android:host="YOUR_DOMAIN"
      android:pathPrefix="/android/${applicationId}/callback"
      android:scheme="${applicationId}.auth0" />
  </intent-filter>
</activity>
```

### 5. Configure Expo

Update `app.json`:

```json
{
  "expo": {
    "scheme": "myappscheme",
    "ios": {
      "bundleIdentifier": "com.mycompany.myapp"
    },
    "android": {
      "package": "com.mycompany.myapp"
    }
  }
}
```

---

## Troubleshooting

**Callback not working:**
- Verify scheme matches bundle ID/package name
- Check Auth0 allowed callbacks include your scheme

**Build errors on iOS:**
- Run `pod install` in ios/ directory
- Clean build folder in Xcode

**Android redirect issues:**
- Ensure RedirectActivity is exported
- Check scheme matches package name

---

SHA-256: 10e006921ecc2ef56704e35cd8b01e7e5647c59742f0decb8b2a3e2796c2c517