> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Agrega el inicio de sesión a tu aplicación de Expo

> Esta guía muestra cómo integrar Auth0 en cualquier aplicación de Expo con el SDK de Auth0 para React Native.

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

<HowToSchema />

<Tip>
  Esta guía de inicio rápido es para aplicaciones de Expo. Para integrar Auth0 en tu aplicación de React Native, consulta la [guía de inicio rápido de React Native](https://auth0.com/docs/quickstart/native/react-native/interactive).
</Tip>

<Accordion title="Usa IA para integrar Auth0" icon="microchip-ai" iconType="solid" defaultOpen>
  Si usas un asistente de programación con IA como Claude Code, Cursor o GitHub Copilot, puedes añadir la autenticación de Auth0 automáticamente en cuestión de minutos con [agent skills](https://agentskills.io/home).

  **Instala:**

  ```bash theme={null}
  npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-expo
  ```

  **Luego pídele a tu asistente de IA:**

  ```text theme={null}
  Add Auth0 authentication to my Expo app
  ```

  Tu asistente de IA creará automáticamente tu aplicación en Auth0, obtendrá las credenciales, instalará el SDK `react-native-auth0`, configurará el plugin de Expo e implementará los flujos de inicio y cierre de sesión. [Documentación completa de agent skills →](/es/docs/quickstart/agent-skills)
</Accordion>

<div id="get-started">
  ## Primeros pasos
</div>

<Steps>
  <Step title="Crea un nuevo proyecto con Expo" stepNumber={1}>
    Cree un nuevo proyecto de Expo para esta guía de inicio rápido.

    **En su terminal:**

    ```bash theme={null}
    npx create-expo-app Auth0ExpoSample --template blank
    cd Auth0ExpoSample
    ```

    <Tip>
      Esto crea una aplicación mínima de Expo con la versión más reciente del SDK, lista para integrar módulos nativos. La marca `--template blank` proporciona un punto de partida limpio, sin código repetitivo adicional.
    </Tip>

    <Warning>
      Este SDK **NO es compatible con Expo Go** porque requiere código nativo personalizado. Debe usar `npx expo run:ios` o `npx expo run:android` para crear una build de desarrollo.
    </Warning>
  </Step>

  <Step title="Instala el SDK de Auth0" stepNumber={2}>
    Añade el SDK de Auth0 para React Native a tu proyecto.

    ```bash theme={null}
    npx expo install react-native-auth0
    ```

    <Tip>
      Usar `npx expo install` garantiza la compatibilidad de versiones con tu versión del SDK de Expo. El SDK se configura automáticamente mediante el sistema de plugins de Expo.
    </Tip>
  </Step>

  <Step title="Configura el plugin de Expo" stepNumber={3}>
    Configura el plugin de Auth0 para gestionar automáticamente la configuración nativa de iOS y Android.

    Actualiza tu `app.json` para incluir el plugin de Auth0:

    ```json app.json lines theme={null}
    {
      "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"
            }
          ]
        ]
      }
    }
    ```

    Reemplaza `{yourDomain}` por tu dominio de Auth0 (lo obtendrás en el siguiente paso).

    <Warning>
      **Importante**: Debes definir `bundleIdentifier` para iOS y `package` para Android en tu `app.json`. Estos identificadores son necesarios para que el SDK de Auth0 configure correctamente los proyectos nativos. Si no especificas un esquema personalizado, el SDK usará el identificador del paquete de iOS como esquema de URL.
    </Warning>

    <Info>
      `customScheme` debe estar en minúsculas y no puede contener caracteres especiales. Este valor se utiliza para construir las URL de devolución de llamada y debe pasarse a los métodos `authorize()` y `clearSession()`.
    </Info>
  </Step>

  <Step title="Configura tu aplicación en Auth0" stepNumber={4}>
    Crea y configura una aplicación de Auth0 para que funcione con tu aplicación de Expo.

    1. Ve al [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
    2. Haz clic en **Applications** > **Applications** > **Create Application**
    3. En la ventana emergente, introduce un nombre para tu aplicación (por ejemplo, `Auth0 Expo Sample`), selecciona `Native` como tipo de aplicación y haz clic en **Create**
    4. Ve a la pestaña **Settings** en la página de detalles de la aplicación
    5. Anota los valores de **dominio** y **ID de cliente**
    6. Actualiza el valor `domain` en la configuración del plugin de tu `app.json` con tu dominio de Auth0

    **Allowed Callback URLs:**

    ```
    auth0sample://{yourDomain}/ios/com.auth0.samples.expo/callback,
    auth0sample://{yourDomain}/android/com.auth0.samples.expo/callback
    ```

    **URL de cierre de sesión permitidas:**

    ```
    auth0sample://{yourDomain}/ios/com.auth0.samples.expo/callback,
    auth0sample://{yourDomain}/android/com.auth0.samples.expo/callback
    ```

    Reemplaza `{yourDomain}` por tu dominio real de Auth0 (por ejemplo, `dev-abc123.us.auth0.com`).

    <Info>
      **Allowed Callback URLs** son una medida de seguridad fundamental para garantizar que los usuarios regresen de forma segura a tu aplicación después de autenticarse. Sin una URL que coincida, el proceso de inicio de sesión fallará y los usuarios verán una página de error de Auth0 en lugar de acceder a tu aplicación.

      **Allowed Logout URLs** son esenciales para ofrecer una experiencia fluida al cerrar sesión. Sin una URL que coincida, los usuarios no serán redirigidos de vuelta a tu aplicación después de cerrar sesión y, en su lugar, permanecerán en una página genérica de Auth0.

      El formato de la URL de callback es: `{customScheme}://{yourDomain}/{platform}/{bundleIdentifier or packageName}/callback`. El esquema de la URL usa el `customScheme` de tu `app.json`, pero la ruta siempre contiene el `bundleIdentifier` (iOS) o `package` (Android), no el esquema personalizado. Si no especificas un `customScheme`, el SDK usa `{bundleIdentifier}.auth0` / `{packageName}.auth0` como esquema de URL de forma predeterminada.
    </Info>

    <Warning>
      **Importante**: Asegúrate de que el `customScheme` de tus URL de callback coincida exactamente con el valor de la configuración del plugin en tu `app.json` y de que la ruta contenga tu `bundleIdentifier` (iOS) o `package` (Android) reales. Si estos valores no coinciden, la autenticación fallará.
    </Warning>
  </Step>

  <Step title="Configura el componente App" stepNumber={5}>
    Configura el componente principal de tu aplicación según el enfoque de implementación elegido.

    <Tabs>
      <Tab title="Basado en Hooks (con Provider)">
        Reemplace el contenido de `App.js` y envuelva la aplicación con el componente `Auth0Provider`:

        ```jsx App.js lines theme={null}
        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,
          },
        });
        ```

        Reemplaza `{yourDomain}` por tu dominio de Auth0 y `{yourClientId}` por tu ID de cliente del Auth0 Dashboard.

        <Tip>
          `Auth0Provider` inicializa el SDK y proporciona el contexto de autenticación a todos los componentes secundarios mediante el hook `useAuth0`. El parámetro `customScheme` debe coincidir con el valor de la configuración del plugin en tu `app.json`.
        </Tip>
      </Tab>

      <Tab title="Basado en clases (sin Provider)">
        Reemplaza el contenido de `App.js` con un componente basado en clases:

        ```jsx App.js lines theme={null}
        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 AppState {
          user: User | null;
          isLoading: boolean;
        }

        class App extends Component<{}, AppState> {
          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',
              }, {customScheme: 'auth0sample'});
              
              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({}, {customScheme: 'auth0sample'});
              await auth0.credentialsManager.clearCredentials();
              this.setState({user: null});
            } catch (e) {
              console.error(e);
            }
          };

          render() {
            const {user, isLoading} = this.state;

            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={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>
            );
          }
        }

        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,
          },
        });

        export default App;
        ```

        Reemplaza `{yourDomain}` con tu dominio de Auth0 y `{yourClientId}` con tu ID de cliente del Auth0 Dashboard.

        <Tip>
          El enfoque basado en clases no requiere `Auth0Provider`, ya que utiliza directamente la instancia de la clase `Auth0`. La opción `customScheme` debe pasarse como segundo parámetro de `authorize()` y `clearSession()`.
        </Tip>
      </Tab>
    </Tabs>

    <Info>
      El método `authorize()` abre el Universal Login de Auth0 en un navegador seguro (ASWebAuthenticationSession en iOS, Chrome Custom Tabs en Android). El método `clearSession()` cierra la sesión del usuario y borra tanto la sesión del navegador como las credenciales almacenadas. El parámetro `customScheme` debe coincidir con el valor de la configuración del complemento en `app.json`.
    </Info>
  </Step>

  <Step title="Ejecuta la aplicación" stepNumber={6}>
    Compila y ejecuta tu aplicación de Expo en un dispositivo o un emulador.

    **Primero, genera los proyectos nativos de iOS y Android:**

    ```bash theme={null}
    npx expo prebuild
    ```

    **A continuación, ejecútalo en tu plataforma de destino:**

    **Para iOS:**

    ```bash theme={null}
    npx expo run:ios
    ```

    **Para Android:**

    ```bash theme={null}
    npx expo run:android
    ```

    **Flujo esperado:**

    1. La aplicación se inicia y muestra el botón "Iniciar sesión"
    2. Toca **Iniciar sesión** → Se abre el navegador con Auth0 Universal Login
    3. Completa el inicio de sesión (regístrate o inicia sesión)
    4. El navegador se cierra → Vuelve a la aplicación automáticamente
    5. Se muestra el perfil del usuario con nombre, correo electrónico y avatar

    <Tip>
      Si haces cambios en la configuración del plugin en `app.json`, ejecuta `npx expo prebuild --clean` para regenerar los proyectos nativos con la configuración actualizada.
    </Tip>

    <Warning>
      El simulador de iOS requiere una cuenta válida de Apple Developer para ASWebAuthenticationSession. Si quieres hacer pruebas en el simulador sin una cuenta, usa un dispositivo físico o un emulador de Android.
    </Warning>
  </Step>
</Steps>

<Check>
  **Punto de control**

  Ahora deberías tener una experiencia de inicio de sesión con Auth0 totalmente funcional en tu dispositivo o emulador. La aplicación usa autenticación segura en el navegador y gestiona automáticamente las credenciales en el almacenamiento seguro del dispositivo.
</Check>

***

<div id="troubleshooting-advanced">
  ## Solución de problemas y temas avanzados
</div>

<Accordion title="Problemas comunes y soluciones">
  ### "Invariant Violation: Native module cannot be null"

  Este error se produce al intentar usar el SDK con Expo Go.

  **Solución:**

  El SDK de Auth0 requiere código nativo personalizado que no está disponible en Expo Go. En su lugar, usa una compilación de desarrollo:

  ```bash theme={null}
  npx expo run:ios
  # o
  npx expo run:android
  ```

  ### Error de discrepancia en la URL de callback

  **Solución:**

  Verifica que estos tres elementos coincidan exactamente:

  1. `customScheme` en la configuración del plugin de `app.json`
  2. El parámetro `customScheme` que se pasa a `authorize()` y `clearSession()`
  3. Las URL de callback en Auth0 Dashboard (Applications → Your App → Settings → Application URIs)

  ### Error "PKCE not allowed"

  **Solución:**

  1. Ve a Auth0 Dashboard → Applications → Your Application
  2. Cambia el tipo de aplicación a **Native**
  3. Guarda los cambios y vuelve a intentarlo

  ### Prebuild falla o el plugin no se aplica

  **Solución:**

  ```bash theme={null}
  # Limpiar y regenerar proyectos nativos
  npx expo prebuild --clean
  ```

  ### La compilación de iOS falla con errores de Pod

  **Solución:**

  ```bash theme={null}
  cd ios
  pod install --repo-update
  cd ..
  npx expo run:ios
  ```

  ### Error de cancelación por parte del usuario

  Manéjalo correctamente en tu función de inicio de sesión:

  ```jsx expandable theme={null}
  const handleLogin = async () => {
    try {
      await authorize({customScheme: 'auth0sample', scope: 'openid profile email'});
    } catch (e) {
      if (e.message === 'a0.session.user_cancelled') {
        // El usuario cerró la pantalla de inicio de sesión; manéjalo correctamente
        console.log('Inicio de sesión cancelado por el usuario');
      } else {
        console.error('Error en el inicio de sesión:', e);
      }
    }
  };
  ```

  ### Cuadro de diálogo de alerta en iOS

  En iOS, los usuarios ven un cuadro de diálogo de permisos: *"App Name" Wants to Use "auth0.com" to Sign In*. Este comportamiento es el esperado de `ASWebAuthenticationSession`. Los usuarios deben tocar **Continuar** para seguir.

  Para personalizar este comportamiento, puedes usar sesiones efímeras (desactiva el SSO):

  ```jsx theme={null}
  await authorize({customScheme: 'auth0sample', scope: 'openid profile email'}, {ephemeralSession: true});
  ```
</Accordion>

<Accordion title="Recuperar tokens de acceso">
  Usa el método `getCredentials()` para recuperar tokens para llamadas a la API:

  ```jsx expandable lines theme={null}
  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}`,
          },
        });
        // Manejar la respuesta
      } catch (e) {
        console.error('No se pudieron obtener las credenciales', e);
      }
    };
  };
  ```

  <Tip>
    Incluye el scope `offline_access` durante el inicio de sesión para recibir un token de actualización: `authorize({customScheme: 'auth0sample', scope: 'openid profile email offline_access'})`. Esto permite la renovación automática del token.
  </Tip>
</Accordion>

<Accordion title="Comprobar el estado de autenticación al iniciar la aplicación">
  Usa `hasValidCredentials()` para comprobar si el usuario ya inició sesión:

  ```jsx expandable lines theme={null}
  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();
          // El usuario está autenticado; cargar sus datos
        }
      };
      checkAuth();
    }, []);
  };
  ```
</Accordion>

<Accordion title="Implementación en producción con EAS Build">
  Para las compilaciones de producción, usa EAS Build en lugar de compilaciones de desarrollo locales.

  **Instala EAS CLI:**

  ```bash theme={null}
  npm install -g eas-cli
  ```

  **Crea `eas.json` en la raíz de tu proyecto:**

  ```json eas.json lines theme={null}
  {
    "cli": {
      "version": ">= 3.0.0"
    },
    "build": {
      "development": {
        "developmentClient": true,
        "distribution": "internal"
      },
      "preview": {
        "distribution": "internal"
      },
      "production": {}
    }
  }
  ```

  **Compila para producción:**

  ```bash theme={null}
  # Compilar para ambas plataformas
  eas build --platform all

  # O compilar para una plataforma específica
  eas build --platform ios
  eas build --platform android
  ```

  ### Antes de implementar en producción

  **Usa URL de callback HTTPS** para mejorar la seguridad:

  ```text theme={null}
  https://{yourDomain}/ios/{bundleId}/callback
  https://{yourDomain}/android/{packageName}/callback
  ```

  **Configura Android App Links** en Auth0 Dashboard:

  * Settings → Advanced Settings → Device Settings
  * Agrega la huella digital SHA-256 de tu aplicación

  **Configura iOS Universal Links:**

  * Agrega la capacidad Associated Domains en Xcode
  * Agrega `webcredentials:{yourDomain}` a Associated Domains

  **Revisa la configuración de seguridad** en Auth0 Dashboard:

  * Habilita **OIDC Conformant** en Advanced Settings
  * Configura **Token Expiration** según sea necesario
  * Configura **Brute Force Protection**
  * Prueba en varios dispositivos y versiones del sistema operativo
  * Implementa un manejo de errores adecuado para fallos de red

  <Tip>
    Para aplicaciones de producción, considera usar URL de callback HTTPS con Universal Links (iOS) y App Links (Android) en lugar de esquemas personalizados para mejorar la seguridad.
  </Tip>
</Accordion>

* [Aplicación de ejemplo](https://github.com/auth0-samples/auth0-react-native-sample/tree/master/00-Login-Expo)
* [Guía de migración (v4 a v5)](https://github.com/auth0/react-native-auth0/blob/master/MIGRATION_GUIDE.md)
