> ## 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.

# Añada Login a su aplicación Ionic React con Capacitor

> Esta guía muestra cómo integrar Auth0 en una aplicación de Ionic React y Capacitor mediante el SDK de Auth0 para React.

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

export const CreateInteractiveApp = ({placeholderText = "Auth0", appType = "regular_web", allowedCallbackUrls = ["localhost:3000"], allowedLogoutUrls = ["localhost:3000"], allowedOriginUrls = ["localhost:3000"]}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === "undefined" || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = "", children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError("El nombre de la aplicación es obligatorio");
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: "quickstart-docs-app-creation-component"
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error("Error creating client:", err);
        const errorMessage = err instanceof Error ? err.message : "No se pudo crear la aplicación";
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">Crear aplicación de Auth0</span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>{isLoading ? "Creando..." : "Crear"}</Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>Iniciar sesión</Button> <span>para crear la aplicación</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****ENMASCARADO*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Accordion title="Usa la 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 usando [agent skills](https://agentskills.io/home).

  Primero, instala las agent skills de Auth0:

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

  Luego, pídele a tu asistente de IA:

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

  Tu asistente de IA creará automáticamente tu aplicación de Auth0, obtendrá las credenciales, instalará el SDK de Auth0 para React y los plugins de Capacitor, configurará el deep linking e implementará los flujos de inicio y cierre de sesión con integración con el navegador nativo. Obtén más información sobre [Auth0 agent skills](/es/docs/quickstart/agent-skills).
</Accordion>

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

Este inicio rápido muestra cómo añadir la autenticación de Auth0 a una aplicación de Ionic React con Capacitor. Crearás una aplicación preparada para dispositivos móviles con funciones de inicio de sesión, cierre de sesión y perfil de usuario mediante el SDK de Auth0 para React y la integración con el navegador nativo de Capacitor.

export const localEnvSnippet = `VITE_AUTH0_DOMAIN={yourDomain}
VITE_AUTH0_CLIENT_ID={yourClientId}`;

<Steps>
  <Step title="Crear un proyecto nuevo" stepNumber={1}>
    Crea una nueva aplicación de Ionic React con Capacitor

    ```shellscript theme={null}
    npx ionic start auth0-ionic-react tabs --type=react --capacitor
    ```

    Abre el proyecto

    ```shellscript theme={null}
    cd auth0-ionic-react
    ```

    <Warning>
      Asegúrate de usar el paquete `@ionic/cli` (no el paquete `ionic` obsoleto). Si aparece algún error relacionado con `--npm-client` durante la creación del proyecto, actualiza tu CLI:

      ```shellscript theme={null}
      npm uninstall -g ionic
      npm i -g @ionic/cli
      ```
    </Warning>

    <Info>
      Si ya tienes una aplicación de Ionic React, asegúrate de que Capacitor esté habilitado. Puedes agregarlo con `ionic integrations enable capacitor` y luego `npx cap init`.
    </Info>
  </Step>

  <Step title="Instala el SDK de React para Auth0 y los complementos de Capacitor" stepNumber={2}>
    La plantilla inicial de Ionic puede generar `react@19.0.0`, que no es compatible con el SDK de React de Auth0. Primero, asegúrate de tener una versión compatible de React:

    ```shellscript theme={null}
    npm install react@^19.0.1 react-dom@^19.0.1
    ```

    Luego, instala el SDK de Auth0 y los complementos de Capacitor:

    ```shellscript theme={null}
    npm install @auth0/auth0-react @capacitor/browser @capacitor/app && npx cap sync
    ```

    * [`@auth0/auth0-react`](https://github.com/auth0/auth0-react): Proporciona hooks y componentes de React para la autenticación con Auth0
    * [`@capacitor/browser`](https://capacitorjs.com/docs/apis/browser): Abre la página de Login en el navegador del sistema del dispositivo ([SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) en iOS, [Chrome Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs) en Android)
    * [`@capacitor/app`](https://capacitorjs.com/docs/apis/app): Gestiona los callbacks de enlaces profundos cuando Auth0 redirige a tu aplicación

    <Info>
      El plugin Browser de Capacitor en iOS usa `SFSafariViewController`, que en iOS 11+ no comparte cookies con Safari en el dispositivo. Esto significa que [SSO](https://auth0.com/docs/sso) no funcionará en esos dispositivos. Si necesitas SSO, usa un plugin compatible que utilice [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession).
    </Info>
  </Step>

  <Step title="Configura tu aplicación de Auth0" stepNumber={3}>
    A continuación, debes crear una nueva aplicación en tu tenant de Auth0 y agregar las variables de entorno a tu proyecto.

    Tienes tres opciones para configurar tu aplicación de Auth0: usar la herramienta Quick Setup (recomendado), ejecutar un comando de CLI o configurar manualmente a través del Dashboard.

    <Info>
      A lo largo de este inicio rápido, `YOUR_PACKAGE_ID` es el ID de paquete de su aplicación. Este corresponde al campo `appId` en su archivo `capacitor.config.ts` (por ejemplo, `io.ionic.starter`). Consulte el [esquema de configuración de Capacitor](https://capacitorjs.com/docs/config#schema) para obtener más información.
    </Info>

    <Tabs>
      <Tab title="Configuración rápida (recomendada)">
        Cree una aplicación en Auth0 y copie el archivo `.env` rellenado previamente con los valores de configuración correctos.

        <CreateInteractiveApp placeholderText="Ionic React" appType="native" allowedCallbackUrls={["YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback"]} allowedLogoutUrls={["YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback"]} allowedOriginUrls={["capacitor://localhost", "http://localhost"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />

        Después de crear la aplicación, vaya a **Settings** en el [Auth0 Dashboard](https://manage.auth0.com/dashboard/) y actualice **Allowed Callback URLs** y **Allowed Logout URLs** para sustituir `YOUR_PACKAGE_ID` por su ID de paquete real y `YOUR_AUTH0_DOMAIN` por su dominio de Auth0:

        ```text theme={null}
        YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback
        ```

        Asegúrate también de que **Application Type** esté configurado como **Native** y de que **Token Endpoint Authentication Method** esté configurado como **None**.
      </Tab>

      <Tab title="CLI">
        Ejecute el siguiente comando en el directorio raíz de su proyecto para crear una aplicación de Auth0 y generar un archivo `.env`:

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Instale Auth0 CLI (si todavía no está instalado)
          brew tap auth0/auth0-cli && brew install auth0

          # Cree una aplicación nativa de Auth0
          auth0 apps create \
            --name "Ionic React App" \
            --type native \
            --callbacks "YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback" \
            --logout-urls "YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback" \
            --origins "capacitor://localhost,http://localhost"
          ```

          ```powershell Windows theme={null}
          # Instale Auth0 CLI (si todavía no está instalado)
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Cree una aplicación nativa de Auth0
          auth0 apps create `
            --name "Ionic React App" `
            --type native `
            --callbacks "YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback" `
            --logout-urls "YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback" `
            --origins "capacitor://localhost,http://localhost"
          ```
        </CodeGroup>

        <Note>
          Este comando hará lo siguiente:

          1. Comprobar si está autenticado (y pedirle que inicie sesión si es necesario)
          2. Crear una aplicación nativa de Auth0
          3. Mostrar el **Dominio** y el **ID de cliente**; copie estos valores en un archivo `.env` en la raíz de su proyecto
        </Note>

        Cree un archivo `.env` con los valores de la salida de la CLI:

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />
      </Tab>

      <Tab title="Dashboard">
        Antes de empezar, crea un archivo `.env` en el directorio raíz de tu proyecto

        ```shellscript .env theme={null}
        VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        ```

        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, selecciona `Native` como tipo de aplicación y haz clic en **Create**
        4. Ve a la pestaña **Settings** en la página **Application Details**
        5. Reemplaza `YOUR_AUTH0_APP_DOMAIN` y `YOUR_AUTH0_APP_CLIENT_ID` en el archivo `.env` por los valores de **dominio** e **ID de cliente** del Dashboard
        6. Establece **Token Endpoint Authentication Method** en `None`

        Por último, en la pestaña **Settings** de la página **Application Details**, configura las siguientes URL (reemplaza `YOUR_PACKAGE_ID` por el valor de `appId` en `capacitor.config.ts`):

        **Allowed Callback URLs:**

        ```text theme={null}
        YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback
        ```

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

        ```text theme={null}
        YOUR_PACKAGE_ID://YOUR_AUTH0_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback
        ```

        **Orígenes permitidos:**

        ```text theme={null}
        capacitor://localhost, http://localhost
        ```

        <Info>
          **Allowed Callback URLs** son una medida de seguridad fundamental para garantizar que los usuarios vuelvan de forma segura a tu aplicación después de autenticarse. Sin una URL que coincida, el proceso de Login 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 del Logout y, en su lugar, se quedarán en una página genérica de Auth0.

          **Allowed Origins** (`capacitor://localhost` para iOS y `http://localhost` para Android) son necesarias para que tu aplicación nativa pueda comunicarse con la API de Auth0.
        </Info>
      </Tab>
    </Tabs>

    <Tip>
      Verifica que exista tu archivo `.env`: `cat .env` (Mac/Linux) o `type .env` (Windows)
    </Tip>
  </Step>

  <Step title="Configura el Auth0Provider" stepNumber={4}>
    Abre `src/main.tsx` y envuelve el componente `App` con `Auth0Provider`. Los ajustes específicos para móviles `useRefreshTokens` y `useRefreshTokensFallback` son obligatorios para las aplicaciones de Ionic en iOS y Android.

    ```javascript src/main.tsx {5,6,12,13,14,15,16,17,18,19} lines theme={null}
    import React from 'react';
    import { createRoot } from 'react-dom/client';
    import App from './App';
    import { Auth0Provider } from '@auth0/auth0-react';

    // Debe coincidir con el appId en tu capacitor.config.ts
    const appId = 'io.ionic.starter';

    createRoot(document.getElementById('root')!).render(
      <React.StrictMode>
        <Auth0Provider
          domain={import.meta.env.VITE_AUTH0_DOMAIN}
          clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
          useRefreshTokens={true}
          useRefreshTokensFallback={false}
          authorizationParams={{
            redirect_uri: `${appId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${appId}/callback`
          }}
        >
          <App />
        </Auth0Provider>
      </React.StrictMode>
    );
    ```

    * `useRefreshTokens`: Obligatorio para Ionic en Android e iOS. Los navegadores móviles bloquean las cookies de terceros, por lo que el SDK usa tokens de actualización en lugar de autenticación silenciosa basada en `iframe`.
    * `useRefreshTokensFallback`: Debe ser `false` para evitar que el SDK intente usar autenticación silenciosa basada en `iframe`, ya que no está disponible en dispositivos móviles.
    * `authorizationParams.redirect_uri`: Usa el ID del paquete como esquema de URL personalizado para que el sistema operativo pueda dirigir el callback de Auth0 a tu aplicación.

    <Warning>
      Para conservar la autenticación después de cerrar y volver a abrir la aplicación, quizá quieras establecer `cacheLocation` en `localstorage`, pero ten en cuenta [los riesgos de almacenar tokens en localstorage](https://auth0.com/docs/libraries/auth0-single-page-app-sdk#change-storage-options). En Capacitor, `localstorage` debe tratarse como **transitorio**: el sistema operativo puede borrarlo inesperadamente. Consulta [la guía de Capacitor sobre almacenamiento](https://capacitorjs.com/docs/guides/storage#why-cant-i-just-use-localstorage-or-indexeddb).

      Recomendamos **no** usar [el plugin Preferences de Capacitor](https://capacitorjs.com/docs/apis/preferences) para almacenar tokens, ya que se basa en `UserDefaults` (iOS) y `SharedPreferences` (Android), que no están cifrados y podrían sincronizarse con la nube. El SDK admite [implementaciones de caché personalizadas](https://github.com/auth0/auth0-spa-js/blob/master/EXAMPLES.md#creating-a-custom-cache) si necesitas un mecanismo de almacenamiento más seguro y persistente.
    </Warning>
  </Step>

  <Step title="Crear los controladores de Login, Logout, Profile y callback" stepNumber={5}>
    Crear archivos

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      touch src/LoginButton.tsx && touch src/LogoutButton.tsx && touch src/Profile.tsx
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType File -Path src/LoginButton.tsx
      New-Item -ItemType File -Path src/LogoutButton.tsx
      New-Item -ItemType File -Path src/Profile.tsx
      ```
    </CodeGroup>

    Y agrega los siguientes fragmentos de código

    <AuthCodeGroup>
      ```javascript src/LoginButton.tsx lines theme={null}
      import { useAuth0 } from '@auth0/auth0-react';
      import { Browser } from '@capacitor/browser';
      import { IonButton } from '@ionic/react';

      const LoginButton: React.FC = () => {
        const { loginWithRedirect } = useAuth0();

        const login = async () => {
          await loginWithRedirect({
            async openUrl(url) {
              await Browser.open({
                url,
                windowName: "_self"
              });
            }
          });
        };

        return <IonButton onClick={login}>Log in</IonButton>;
      };

      export default LoginButton;
      ```

      ```javascript src/LogoutButton.tsx lines theme={null}
      import { useAuth0 } from '@auth0/auth0-react';
      import { Browser } from '@capacitor/browser';
      import { IonButton } from '@ionic/react';

      const appId = 'io.ionic.starter';

      const LogoutButton: React.FC = () => {
        const { logout } = useAuth0();

        const logoutUri = `${appId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${appId}/callback`;

        const doLogout = async () => {
          await logout({
            logoutParams: {
              returnTo: logoutUri,
            },
            async openUrl(url) {
              await Browser.open({
                url,
                windowName: "_self"
              });
            }
          });
        };

        return <IonButton onClick={doLogout} color="danger">Log out</IonButton>;
      };

      export default LogoutButton;
      ```

      ```javascript src/Profile.tsx lines theme={null}
      import { useAuth0 } from '@auth0/auth0-react';
      import { IonAvatar, IonItem, IonLabel } from '@ionic/react';

      const Profile: React.FC = () => {
        const { user, isAuthenticated, isLoading } = useAuth0();

        if (isLoading) {
          return <IonItem><IonLabel>Loading profile...</IonLabel></IonItem>;
        }

        return (
          isAuthenticated && user ? (
            <IonItem lines="none">
              <IonAvatar slot="start">
                <img
                  src={user.picture || `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`}
                  alt={user.name || 'User'}
                  onError={(e) => {
                    const target = e.target as HTMLImageElement;
                    target.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`;
                  }}
                />
              </IonAvatar>
              <IonLabel>
                <h2>{user.name}</h2>
                <p>{user.email}</p>
              </IonLabel>
            </IonItem>
          ) : null
        );
      };

      export default Profile;
      ```

      ```javascript src/App.tsx expandable lines theme={null}
      import { useEffect } from 'react';
      import { Redirect, Route } from 'react-router-dom';
      import {
        IonApp,
        IonIcon,
        IonLabel,
        IonRouterOutlet,
        IonTabBar,
        IonTabButton,
        IonTabs,
        setupIonicReact,
      } from '@ionic/react';
      import { IonReactRouter } from '@ionic/react-router';
      import { personCircle, logIn } from 'ionicons/icons';
      import { App as CapApp } from '@capacitor/app';
      import { Browser } from '@capacitor/browser';
      import { useAuth0 } from '@auth0/auth0-react';
      import HomePage from './pages/Home';

      /* CSS de Ionic */
      import '@ionic/react/css/core.css';
      import '@ionic/react/css/normalize.css';
      import '@ionic/react/css/structure.css';
      import '@ionic/react/css/typography.css';
      import '@ionic/react/css/padding.css';
      import '@ionic/react/css/float-elements.css';
      import '@ionic/react/css/text-alignment.css';
      import '@ionic/react/css/text-transformation.css';
      import '@ionic/react/css/flex-utils.css';
      import '@ionic/react/css/display.css';

      setupIonicReact();

      const App: React.FC = () => {
        const { handleRedirectCallback } = useAuth0();

        useEffect(() => {
          CapApp.addListener('appUrlOpen', async ({ url }) => {
            if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
              await handleRedirectCallback(url);
            }
            await Browser.close();
          });
        }, [handleRedirectCallback]);

        return (
          <IonApp>
            <IonReactRouter>
              <IonTabs>
                <IonRouterOutlet>
                  <Route exact path="/home">
                    <HomePage />
                  </Route>
                  <Route exact path="/">
                    <Redirect to="/home" />
                  </Route>
                </IonRouterOutlet>
                <IonTabBar slot="bottom">
                  <IonTabButton tab="home" href="/home">
                    <IonIcon aria-hidden="true" icon={personCircle} />
                    <IonLabel>Home</IonLabel>
                  </IonTabButton>
                </IonTabBar>
              </IonTabs>
            </IonReactRouter>
          </IonApp>
        );
      };

      export default App;
      ```

      ```javascript src/pages/Home.tsx expandable lines theme={null}
      import {
        IonContent,
        IonHeader,
        IonPage,
        IonTitle,
        IonToolbar,
      } from '@ionic/react';
      import { useAuth0 } from '@auth0/auth0-react';
      import LoginButton from '../LoginButton';
      import LogoutButton from '../LogoutButton';
      import Profile from '../Profile';

      const HomePage: React.FC = () => {
        const { isAuthenticated, isLoading, error } = useAuth0();

        return (
          <IonPage>
            <IonHeader>
              <IonToolbar>
                <IonTitle>Auth0 + Ionic React</IonTitle>
              </IonToolbar>
            </IonHeader>
            <IonContent className="ion-padding" fullscreen>
              {isLoading && <p>Loading...</p>}

              {error && <p>Error: {error.message}</p>}

              {!isLoading && !isAuthenticated && (
                <div className="ion-text-center ion-padding-top">
                  <h2>Welcome</h2>
                  <p>Sign in to get started</p>
                  <LoginButton />
                </div>
              )}

              {!isLoading && isAuthenticated && (
                <div className="ion-padding-top">
                  <Profile />
                  <div className="ion-text-center ion-padding-top">
                    <LogoutButton />
                  </div>
                </div>
              )}
            </IonContent>
          </IonPage>
        );
      };

      export default HomePage;
      ```
    </AuthCodeGroup>

    El callback `openUrl` en `LoginButton` y `LogoutButton` utiliza el plugin Browser de Capacitor para abrir las páginas de Login y Logout de Auth0 en el componente de navegador del sistema del dispositivo, en lugar de abandonar la aplicación por completo.

    El componente `App` escucha el evento `appUrlOpen`, que se activa cuando Auth0 redirige a tu aplicación mediante el esquema de URL personalizado. Llama a `handleRedirectCallback` para intercambiar el código de autorización por tokens y, luego, cierra el navegador.

    <Info>
      De forma predeterminada, el método `loginWithRedirect` del SDK usa `window.location.href` para ir a la página de Login, lo que abre la aplicación de navegador predeterminada del dispositivo. Configurar `openUrl` para usar `Browser.open` mantiene el flujo de autenticación dentro del contexto de tu aplicación para ofrecer una mejor experiencia de usuario.
    </Info>
  </Step>

  <Step title="Ejecuta tu aplicación" stepNumber={6}>
    Primero, prueba en el navegador

    ```shellscript theme={null}
    ionic serve
    ```

    <Warning>
      Al ejecutar en el navegador con `ionic serve`, la redirección con el esquema de URL personalizado (`io.ionic.starter://...`) no funcionará porque los navegadores no admiten esquemas de URL personalizados. Para probar en el navegador, cambia temporalmente `redirect_uri` a `http://localhost:8100` en `src/main.tsx` y agrega `http://localhost:8100` a **Allowed Callback URLs** y **Allowed Logout URLs** en el Dashboard de tu aplicación de Auth0. Recuerda deshacer este cambio antes de compilar para nativo.
    </Warning>

    Para ejecutar en un dispositivo o simulador, primero agrega las plataformas nativas:

    ```shellscript theme={null}
    npx cap add ios
    npx cap add android
    ```

    Luego, compila, sincroniza y ejecuta:

    <CodeGroup>
      ```shellscript iOS theme={null}
      ionic build && npx cap sync && npx cap run ios
      ```

      ```shellscript Android theme={null}
      ionic build && npx cap sync && npx cap run android
      ```
    </CodeGroup>

    <Info>
      Debes agregar las plataformas nativas con `npx cap add` antes de poder ejecutarlas. Esto solo debe hacerse una vez por plataforma. Después, `npx cap sync` copia los recursos web compilados y actualiza los plugins nativos.
    </Info>
  </Step>
</Steps>

<Check>
  **Punto de control**

  Ahora deberías tener un inicio de sesión de Auth0 completamente funcional en tu aplicación Ionic. Al ejecutarla en un dispositivo, al tocar "Iniciar sesión" se abre la página de Universal Login de Auth0 en el navegador del sistema y, después de autenticarte, se te redirige a tu aplicación, donde se muestra el perfil del usuario.
</Check>

***

<div id="advanced-usage">
  ## Uso avanzado
</div>

<Accordion title="Configuración del esquema de URL personalizado (iOS y Android)">
  Para que los callbacks de Auth0 funcionen en dispositivos, registra el ID de tu paquete como un esquema de URL personalizado en cada plataforma.

  **iOS** — agrega esto a `ios/App/App/Info.plist`:

  ```xml ios/App/App/Info.plist theme={null}
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>io.ionic.starter</string>
      </array>
    </dict>
  </array>
  ```

  **Android** — agrega un filtro intent dentro de la `<activity>` principal en `android/app/src/main/AndroidManifest.xml`:

  ```xml android/app/src/main/AndroidManifest.xml theme={null}
  <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:scheme="io.ionic.starter" />
  </intent-filter>
  ```

  Reemplaza `io.ionic.starter` por el valor real de `appId` en `capacitor.config.ts`.

  <Info>
    Para obtener más información, consulta [Defining a Custom URL Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) para iOS o [Create Deep Links to App Content](https://developer.android.com/training/app-links/deep-linking) para Android.
  </Info>
</Accordion>

<Accordion title="Protección de rutas con Ionic Router">
  Usa el estado de autenticación de Auth0 para proteger rutas específicas en tu aplicación Ionic:

  ```jsx src/App.tsx theme={null}
  import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';
  import { IonReactRouter } from '@ionic/react-router';
  import { IonRouterOutlet } from '@ionic/react';
  import { Route, Redirect } from 'react-router-dom';
  import HomePage from './pages/Home';
  import DashboardPage from './pages/Dashboard';

  const ProtectedRoute = ({ component, ...args }: any) => {
    const Component = withAuthenticationRequired(component, {
      onRedirecting: () => <div className="ion-padding">Loading...</div>,
    });
    return <Route {...args} render={() => <Component />} />;
  };

  const App: React.FC = () => {
    // ... manejador de callback del paso anterior

    return (
      <IonApp>
        <IonReactRouter>
          <IonRouterOutlet>
            <Route exact path="/home" component={HomePage} />
            <ProtectedRoute exact path="/dashboard" component={DashboardPage} />
            <Route exact path="/">
              <Redirect to="/home" />
            </Route>
          </IonRouterOutlet>
        </IonReactRouter>
      </IonApp>
    );
  };
  ```

  El HOC `withAuthenticationRequired` redirige automáticamente a los usuarios no autenticados a la página de Login de Auth0 cuando intentan acceder a una ruta protegida.
</Accordion>

<Accordion title="Llamadas a APIs protegidas">
  Configura tu Auth0Provider para incluir la audiencia de una API y usa el método `getAccessTokenSilently` para obtener tokens de acceso para tu backend:

  ```jsx src/main.tsx theme={null}
  <Auth0Provider
    domain={import.meta.env.VITE_AUTH0_DOMAIN}
    clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
    useRefreshTokens={true}
    useRefreshTokensFallback={false}
    authorizationParams={{
      redirect_uri: `${appId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${appId}/callback`,
      audience: "YOUR_API_IDENTIFIER"
    }}
  >
    <App />
  </Auth0Provider>
  ```

  Luego, realiza llamadas autenticadas a la API desde tus componentes:

  ```jsx src/ApiCall.tsx theme={null}
  import { useState } from 'react';
  import { useAuth0 } from '@auth0/auth0-react';
  import { IonButton, IonText } from '@ionic/react';

  const ApiCall: React.FC = () => {
    const { getAccessTokenSilently } = useAuth0();
    const [result, setResult] = useState<string | null>(null);

    const callApi = async () => {
      try {
        const token = await getAccessTokenSilently();

        const response = await fetch('https://your-api.example.com/protected', {
          headers: {
            Authorization: `Bearer ${token}`
          }
        });

        const data = await response.json();
        setResult(JSON.stringify(data, null, 2));
      } catch (error) {
        console.error('API call failed:', error);
      }
    };

    return (
      <div className="ion-padding">
        <IonButton onClick={callApi}>Call Protected API</IonButton>
        {result && (
          <IonText>
            <pre>{result}</pre>
          </IonText>
        )}
      </div>
    );
  };

  export default ApiCall;
  ```
</Accordion>
