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

> Esta guía muestra cómo integrar Auth0, agregar autenticación y mostrar información del perfil del usuario en cualquier aplicación de Vue mediante el SDK de Auth0 para Vue.

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 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 minutos con [agent skills](https://agentskills.io/home).

  **Instala:**

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

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

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

  Tu asistente de IA creará automáticamente tu aplicación de Auth0, recuperará las credenciales, instalará `@auth0/auth0-vue`, creará el plugin de autenticación y configurará tus rutas. [Documentación completa sobre agent skills →](/es/docs/quickstart/agent-skills)
</Accordion>

<Note>
  **Requisitos previos:** Antes de comenzar, asegúrate de tener instalados los siguientes elementos:

  * **[Node.js](https://nodejs.org/en/download)** 20 LTS o una versión posterior
  * **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10+ o **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+ o **[pnpm](https://pnpm.io/installation)** 8+

  Verifica la instalación: `node --version && npm --version`

  **Compatibilidad de versiones de Vue:** Esta guía de inicio rápido funciona con **Vue 3.x** y versiones posteriores.
</Note>

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

En esta guía de inicio rápido se muestra cómo integrar la autenticación de Auth0 en una aplicación de Vue.js 3. Crearás una aplicación de página única adaptable con autenticación segura de usuario mediante la API de composición de Vue y el SDK de Auth0 para Vue.

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

<Steps>
  <Step title="Crear un nuevo proyecto" stepNumber={1}>
    Crea un nuevo proyecto de Vue 3 para esta guía de inicio rápido

    ```shellscript theme={null}
    npm create vue@latest auth0-vue -- --typescript --router --pinia
    ```

    Abre el proyecto

    ```shellscript theme={null}
    cd auth0-vue
    ```
  </Step>

  <Step title="Instala el SDK de Auth0 para Vue" stepNumber={2}>
    ```shellscript theme={null}
    npm add @auth0/auth0-vue && npm install
    ```
  </Step>

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

    Tienes tres opciones para configurar tu aplicación de Auth0: usar la herramienta de configuración rápida (recomendado), ejecutar un comando de la CLI o configurarla manualmente desde el Dashboard:

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

        <CreateInteractiveApp placeholderText="Vue" appType="spa" allowedCallbackUrls={["http://localhost:5173"]} allowedLogoutUrls={["http://localhost:5173"]} allowedOriginUrls={["http://localhost:5173"]} />

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

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

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

          # Configura la aplicación de Auth0 y genera el archivo .env
          auth0 qs setup --app --type spa --framework vue --build-tool vite --name "My App" --port 5173
          ```

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

          # Configura la aplicación de Auth0 y genera el archivo .env
          auth0 qs setup --app --type spa --framework vue --build-tool vite --name "My App" --port 5173
          ```
        </CodeGroup>

        <Note>
          Este comando hará lo siguiente:

          1. Comprobará si ya has iniciado sesión (y te pedirá que la inicies si es necesario)
          2. Creará una aplicación web de una sola página de Auth0 configurada para `http://localhost:5173`
          3. Generará un archivo `.env` con `VITE_AUTH0_DOMAIN` y `VITE_AUTH0_CLIENT_ID`
        </Note>
      </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 `Single Page Web Application` 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 **Domain** y **Client ID** del dashboard

        Por último, en la pestaña **Settings** de la página **Application Details**, configura las siguientes URL:

        **Allowed Callback URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Logout URLs:**

        ```
        http://localhost:5173
        ```

        **Allowed Web Origins:**

        ```
        http://localhost:5173
        ```

        <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 coincidente, 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 coincidente, los usuarios no serán redirigidos de vuelta a tu aplicación después de cerrar sesión y se quedarán en una página genérica de Auth0.

          **Allowed Web Origins** es fundamental para la autenticación silenciosa. Sin esta configuración, los usuarios cerrarán sesión al actualizar la página o al volver más tarde a tu aplicación.
        </Info>
      </Tab>
    </Tabs>

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

  <Step title="Configura el plugin de Auth0" stepNumber={4}>
    ```typescript src/main.ts {6,11,12,13,14,15,16,17} lines theme={null}
    import './assets/main.css'

    import { createApp } from 'vue'
    import { createPinia } from 'pinia'
    import router from './router'
    import { createAuth0 } from '@auth0/auth0-vue'

    import App from './App.vue'

    const app = createApp(App)

    app.use(createAuth0({
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
      authorizationParams: {
        redirect_uri: window.location.origin
      }
    }))

    app.use(createPinia())
    app.use(router)

    app.mount('#app')
    ```
  </Step>

  <Step title="Crear componentes de autenticación" stepNumber={5}>
    Crear archivos de componentes

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      touch src/components/LoginButton.vue && touch src/components/LogoutButton.vue && touch src/components/UserProfile.vue
      ```

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

    Y agrega los siguientes fragmentos de código

    <AuthCodeGroup>
      ```vue src/components/LoginButton.vue lines theme={null}
      <template>
        <button 
          @click="handleLogin" 
          class="button login"
          :disabled="isLoading"
        >
          {{ isLoading ? 'Loading...' : 'Log In' }}
        </button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { loginWithRedirect, isLoading } = useAuth0()

      const handleLogin = () => {
        loginWithRedirect()
      }
      </script>
      ```

      ```vue src/components/LogoutButton.vue lines theme={null}
      <template>
        <button
          @click="handleLogout"
          class="button logout"
          :disabled="isLoading"
        >
          {{ isLoading ? 'Loading...' : 'Log Out' }}
        </button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { logout, isLoading } = useAuth0()

      const handleLogout = () => {
        logout({
          logoutParams: {
            returnTo: window.location.origin
          }
        })
      }
      </script>
      ```

      ```vue src/components/UserProfile.vue expandable lines theme={null}
      <template>
        <div v-if="isLoading" class="loading-text">
          Loading profile...
        </div>
        <div 
          v-else-if="isAuthenticated && user" 
          class="profile-container"
        >
          <img 
            :src="user.picture || placeholderImage" 
            :alt="user.name || 'User'" 
            class="profile-picture"
            @error="handleImageError"
          />
          <div class="profile-info">
            <div class="profile-name">
              {{ user.name }}
            </div>
            <div class="profile-email">
              {{ user.email }}
            </div>
          </div>
        </div>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'

      const { user, isAuthenticated, isLoading } = useAuth0()

      const placeholderImage = `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`

      function handleImageError(e: Event) {
        const target = e.target as HTMLImageElement
        target.src = placeholderImage
      }
      </script>

      <style scoped>
      .profile-container {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1rem;
      }

      .profile-picture {
        width: 110px;
        height: 110px;
        border-radius: 50%;
        object-fit: cover;
        border: 3px solid #63b3ed;
        transition: transform 0.3s ease-in-out;
      }

      .profile-picture:hover {
        transform: scale(1.05);
      }

      .profile-info {
        text-align: center;
      }

      .profile-name {
        font-size: 2rem;
        font-weight: 600;
        color: #f7fafc;
        margin-bottom: 0.5rem;
      }

      .profile-email {
        font-size: 1.15rem;
        color: #a0aec0;
      }
      </style>
      ```

      ```vue src/App.vue expandable lines theme={null}
      <template>
        <div class="app-container">
          <div v-if="isLoading" class="loading-state">
            <div class="loading-text">Loading...</div>
          </div>
          
          <div v-else-if="error" class="error-state">
            <div class="error-title">Oops!</div>
            <div class="error-message">Something went wrong</div>
            <div class="error-sub-message">{{ error.message }}</div>
          </div>
          
          <div v-else class="main-card-wrapper">
            <img 
              src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
              alt="Auth0 Logo" 
              class="auth0-logo"
              @error="handleImageError"
            />
            <h1 class="main-title">Welcome to Vue0</h1>
            
            <div v-if="isAuthenticated" class="logged-in-section">
              <div class="logged-in-message">✅ Successfully authenticated!</div>
              <h2 class="profile-section-title">Your Profile</h2>
              <div class="profile-card">
                <UserProfile />
              </div>
              <LogoutButton />
            </div>
            
            <div v-else class="action-card">
              <p class="action-text">Get started by signing in to your account</p>
              <LoginButton />
            </div>
          </div>
        </div>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'
      import LoginButton from './components/LoginButton.vue'
      import LogoutButton from './components/LogoutButton.vue'
      import UserProfile from './components/UserProfile.vue'

      const { isAuthenticated, isLoading, error } = useAuth0()

      const handleImageError = (e: Event) => {
        const target = e.target as HTMLImageElement
        target.style.display = 'none'
      }
      </script>

      <style>
      @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: 'Inter', sans-serif;
        background-color: #1a1e27;
        min-height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #e2e8f0;
        overflow: hidden;
      }

      #app {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .app-container {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        width: 100%;
        padding: 1rem;
      }

      .loading-state, .error-state {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
        padding: 3rem;
        text-align: center;
      }

      .loading-text {
        font-size: 1.8rem;
        font-weight: 500;
        color: #a0aec0;
        animation: pulse 1.5s infinite ease-in-out;
      }

      .error-state {
        background-color: #c53030;
        color: #fff;
      }

      .error-title {
        font-size: 2.8rem;
        font-weight: 700;
        margin-bottom: 0.5rem;
      }

      .error-message {
        font-size: 1.3rem;
        margin-bottom: 0.5rem;
      }

      .error-sub-message {
        font-size: 1rem;
        opacity: 0.8;
      }

      .main-card-wrapper {
        background-color: #262a33;
        border-radius: 20px;
        box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 2rem;
        padding: 3rem;
        max-width: 500px;
        width: 90%;
        animation: fadeInScale 0.8s ease-out forwards;
      }

      .auth0-logo {
        width: 160px;
        margin-bottom: 1.5rem;
        opacity: 0;
        animation: slideInDown 1s ease-out forwards 0.2s;
      }

      .main-title {
        font-size: 2.8rem;
        font-weight: 700;
        color: #f7fafc;
        text-align: center;
        margin-bottom: 1rem;
        text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.4s;
      }

      .action-card {
        background-color: #2d313c;
        border-radius: 15px;
        box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
        padding: 2.5rem;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.8rem;
        width: calc(100% - 2rem);
        opacity: 0;
        animation: fadeIn 1s ease-out forwards 0.6s;
      }

      .action-text {
        font-size: 1.25rem;
        color: #cbd5e0;
        text-align: center;
        line-height: 1.6;
        font-weight: 400;
      }

      .button {
        padding: 1.1rem 2.8rem;
        font-size: 1.2rem;
        font-weight: 600;
        border-radius: 10px;
        border: none;
        cursor: pointer;
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
        text-transform: uppercase;
        letter-spacing: 0.08em;
        outline: none;
      }

      .button:focus {
        box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
      }

      .button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
        transform: none;
      }

      .button.login {
        background-color: #63b3ed;
        color: #1a1e27;
      }

      .button.login:hover:not(:disabled) {
        background-color: #4299e1;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .button.logout {
        background-color: #fc8181;
        color: #1a1e27;
      }

      .button.logout:hover:not(:disabled) {
        background-color: #e53e3e;
        transform: translateY(-5px) scale(1.03);
        box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
      }

      .logged-in-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 1.5rem;
        width: 100%;
      }

      .logged-in-message {
        font-size: 1.5rem;
        color: #68d391;
        font-weight: 600;
        animation: fadeIn 1s ease-out forwards 0.8s;
      }

      .profile-section-title {
        font-size: 2.2rem;
        animation: slideInUp 1s ease-out forwards 1s;
      }

      .profile-card {
        padding: 2.2rem;
        animation: scaleIn 0.8s ease-out forwards 1.2s;
      }

      @keyframes fadeIn {
        from { opacity: 0; }
        to { opacity: 1; }
      }

      @keyframes fadeInScale {
        from { opacity: 0; transform: scale(0.95); }
        to { opacity: 1; transform: scale(1); }
      }

      @keyframes slideInDown {
        from { opacity: 0; transform: translateY(-70px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes slideInUp {
        from { opacity: 0; transform: translateY(50px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.6; }
      }

      @keyframes scaleIn {
        from { opacity: 0; transform: scale(0.8); }
        to { opacity: 1; transform: scale(1); }
      }

      @media (max-width: 600px) {
        .main-card-wrapper {
          padding: 2rem;
          margin: 1rem;
        }
        
        .main-title {
          font-size: 2.2rem;
        }
        
        .button {
          padding: 1rem 2rem;
          font-size: 1.1rem;
        }
        
        .auth0-logo {
          width: 120px;
        }
      }
      </style>
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="Ejecuta la aplicación" stepNumber={6}>
    ```shellscript theme={null}
    npm run dev
    ```

    <Info>
      Si el puerto 5173 está en uso, ejecuta: `npm run dev -- --port 5174` y actualiza las URL de devolución de llamada de tu aplicación de Auth0 a `http://localhost:5174`
    </Info>
  </Step>
</Steps>

<Check>
  **Punto de control**

  Ahora deberías tener una página de inicio de sesión de Auth0 completamente funcional ejecutándose en tu [localhost](http://localhost:5173/)
</Check>

***

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

<Accordion title="Protección de rutas con Vue Router">
  Usa las guardas de navegación de Vue Router para proteger rutas específicas:

  ```typescript src/router/index.ts theme={null}
  import { createRouter, createWebHistory } from 'vue-router'
  import { authGuard } from '@auth0/auth0-vue'
  import Home from '../views/Home.vue'
  import Profile from '../views/Profile.vue'
  import Dashboard from '../views/Dashboard.vue'

  const routes = [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/profile',
      name: 'Profile',
      component: Profile,
      beforeEnter: authGuard
    },
    {
      path: '/dashboard',
      name: 'Dashboard',
      component: Dashboard,
      beforeEnter: authGuard
    }
  ]

  const router = createRouter({
    history: createWebHistory(),
    routes
  })

  export default router
  ```
</Accordion>

<Accordion title="Llamadas a APIs protegidas">
  Configura tu plugin de Auth0 para incluir la audiencia de una API y realizar solicitudes autenticadas:

  ```typescript src/main.ts theme={null}
  import { createAuth0 } from '@auth0/auth0-vue'

  const app = createApp(App)

  app.use(
    createAuth0({
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
      authorizationParams: {
        redirect_uri: window.location.origin,
        audience: "YOUR_API_IDENTIFIER"
      }
    })
  )
  ```

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

  ```vue src/components/ApiCall.vue theme={null}
  <template>
    <div>
      <button @click="callProtectedApi" :disabled="isLoading">
        Llamar a la API protegida
      </button>
      <pre v-if="apiResponse">{{ JSON.stringify(apiResponse, null, 2) }}</pre>
    </div>
  </template>

  <script setup lang="ts">
  import { ref } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'

  const { getAccessTokenSilently } = useAuth0()
  const apiResponse = ref(null)
  const isLoading = ref(false)

  const callProtectedApi = async () => {
    try {
      isLoading.value = true
      const token = await getAccessTokenSilently()
      
      const response = await fetch('/api/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      })
      
      apiResponse.value = await response.json()
    } catch (error) {
      console.error('La llamada a la API falló:', error)
    } finally {
      isLoading.value = false
    }
  }
  </script>
  ```
</Accordion>

<Accordion title="Uso de composables para la lógica de autenticación">
  Crea composables reutilizables para patrones de autenticación comunes:

  ```typescript src/composables/useAuthenticatedUser.ts theme={null}
  import { computed, ref, watchEffect } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'

  export function useAuthenticatedUser() {
    const { user, isAuthenticated, isLoading, getAccessTokenSilently } = useAuth0()
    const accessToken = ref<string | null>(null)

    watchEffect(async () => {
      if (isAuthenticated.value && !isLoading.value) {
        try {
          accessToken.value = await getAccessTokenSilently()
        } catch (error) {
          console.error('No se pudo obtener el token de acceso:', error)
        }
      }
    })

    return {
      user: computed(() => user.value),
      accessToken: computed(() => accessToken.value),
      isAuthenticated: computed(() => isAuthenticated.value),
      isLoading: computed(() => isLoading.value)
    }
  }
  ```

  Uso en componentes:

  ```vue src/components/UserDashboard.vue theme={null}
  <template>
    <div v-if="!isLoading">
      <h1>Te damos la bienvenida, {{ user?.name }}</h1>
      <p>Token de acceso: {{ accessToken ? 'Disponible' : 'No disponible' }}</p>
    </div>
    <div v-else>Cargando...</div>
  </template>

  <script setup lang="ts">
  import { useAuthenticatedUser } from '../composables/useAuthenticatedUser'

  const { user, accessToken, isLoading } = useAuthenticatedUser()
  </script>
  ```
</Accordion>
