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

> Esta guía muestra cómo integrar Auth0 en cualquier aplicación Next.js nueva o existente mediante el SDK de Auth0 Next.js v4.

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 codificació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).

  **Instalar:**

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

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

  ```text theme={null}
  Add Auth0 authentication to my Next.js app
  ```

  Tu asistente de IA creará automáticamente tu aplicación de Auth0, obtendrá las credenciales, instalará `@auth0/nextjs-auth0`, creará rutas de API y configurará las variables de entorno. [Documentación completa de agent skills →](/es/docs/quickstart/agent-skills)
</Accordion>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  **Requisitos previos:** Antes de comenzar, asegúrate de tener instalado lo siguiente:

  * **[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`
</Callout>

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

Este inicio rápido muestra cómo añadir autenticación de Auth0 a una aplicación Next.js 16. Crearás una aplicación web full-stack con renderizado del lado del servidor, inicio de sesión seguro y rutas protegidas mediante el SDK de Auth0 Next.js v4.

export function generateRandomString(length) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  return Array.from({
    length
  }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}

export const localEnvSnippet = `AUTH0_DOMAIN={yourDomain}
AUTH0_CLIENT_ID={yourClientId}
AUTH0_CLIENT_SECRET={yourClientSecret}
AUTH0_SECRET=${generateRandomString(32)}
APP_BASE_URL=http://localhost:3000`;

<Steps>
  <Step title="Cree un nuevo proyecto" stepNumber={1}>
    Crea un proyecto nuevo de Next.js para este inicio rápido

    ```shellscript theme={null}
    npx create-next-app@latest auth0-nextjs --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --yes
    ```

    Abra el proyecto

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

  <Step title="Instala el SDK de Auth0 para Next.js" stepNumber={2}>
    `shellscript npm install @auth0/nextjs-auth0 `
  </Step>

  <Step title="Crear los archivos del proyecto" stepNumber={3}>
    Cree todos los directorios y archivos necesarios para la integración con Auth0:

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/lib src/components && touch src/lib/auth0.ts src/proxy.ts src/components/LoginButton.tsx src/components/LogoutButton.tsx src/components/Profile.tsx
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/lib, src/components
      New-Item -ItemType File -Path src/lib/auth0.ts, src/proxy.ts, src/components/LoginButton.tsx, src/components/LogoutButton.tsx, src/components/Profile.tsx
      ```
    </CodeGroup>
  </Step>

  <Step title="Configura tu aplicación de Auth0" stepNumber={4}>
    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:

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

        <CreateInteractiveApp placeholderText="Next.js" appType="regular_web" allowedCallbackUrls={["http://localhost:3000/auth/callback"]} allowedLogoutUrls={["http://localhost:3000"]} allowedOriginUrls={["http://localhost:3000"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env.local" />
      </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 regular --framework nextjs --name "My App" --port 3000
          ```

          ```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 regular --framework nextjs --name "My App" --port 3000
          ```
        </CodeGroup>

        <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
          Este comando hará lo siguiente:

          1. Comprobará si has iniciado sesión (y te pedirá que inicies sesión si es necesario)
          2. Creará una aplicación web regular de Auth0 configurada para `http://localhost:3000`
          3. Generará un archivo `.env` con `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET`, `AUTH0_SECRET` y `APP_BASE_URL`
        </Callout>
      </Tab>

      <Tab title="Dashboard">
        Crea un archivo `.env.local` en el directorio raíz de tu proyecto:

        ```shellscript .env.local theme={null}
        AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
        AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
        AUTH0_CLIENT_SECRET=YOUR_AUTH0_APP_CLIENT_SECRET
        AUTH0_SECRET=YOUR_LONG_RANDOM_SECRET_HERE
        APP_BASE_URL=http://localhost:3000
        ```

        **Genera un AUTH0\_SECRET seguro:**

        ```shellscript theme={null}
        openssl rand -hex 32
        ```

        Copia el resultado y reemplaza `YOUR_LONG_RANDOM_SECRET_HERE` en `.env.local`. Debe tener exactamente 64 caracteres hexadecimales.

        Luego, configura tu aplicación de Auth0:

        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 `Regular Web Application` 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. Reemplaza `YOUR_AUTH0_APP_DOMAIN`, `YOUR_AUTH0_APP_CLIENT_ID` y `YOUR_AUTH0_APP_CLIENT_SECRET` en el archivo `.env.local` con los valores de **Domain**, **Client ID** y **Client Secret** del dashboard

        <Warning>
          **Crítico:** Si cambias `AUTH0_SECRET` después de que la aplicación haya estado ejecutándose, debes borrar las cookies de tu navegador para localhost. Las cookies de sesión antiguas cifradas con el secreto anterior provocarán un error `JWEDecryptionFailed`.
        </Warning>

        Por último, en la pestaña **Settings** de la página de detalles de tu aplicación, configura las siguientes URL:

        **Allowed Callback URLs:**

        ```
        http://localhost:3000/auth/callback
        ```

        **Allowed Logout URLs:**

        ```
        http://localhost:3000
        ```

        **Allowed Web Origins:**

        ```
        http://localhost:3000
        ```

        <Info>
          **Allowed Callback URLs** son una medida de seguridad crítica para garantizar que los usuarios vuelvan de forma segura a tu aplicación después de la autenticación. 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, en su lugar, permanecerá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 cuando actualicen la página o vuelvan a tu aplicación más tarde.
        </Info>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Crear la configuración de Auth0" stepNumber={5}>
    Agrega el código del cliente de Auth0 en `src/lib/auth0.ts`:

    ```typescript src/lib/auth0.ts lines theme={null}
    import { Auth0Client } from '@auth0/nextjs-auth0/server';

    export const auth0 = new Auth0Client();
    ```
  </Step>

  <Step title="Añadir proxy" stepNumber={6}>
    Agrega el código del proxy a `src/proxy.ts`:

    ```typescript src/proxy.ts expandable lines theme={null}
    import { auth0 } from "./lib/auth0";

    export async function proxy(request: Request) {
      return await auth0.middleware(request);
    }

    export const config = {
      matcher: [
        /*
         * Coincide con todas las rutas de solicitud excepto las que comienzan con:
         * - _next/static (archivos estáticos)
         * - _next/image (archivos de optimización de imágenes)
         * - favicon.ico, sitemap.xml, robots.txt (archivos de metadatos)
         */
        "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
      ],
    };
    ```

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      Como estamos usando un directorio `src/`, el archivo `proxy.ts` se crea dentro de `src/`. Si no usas un directorio `src/`, créalo en la raíz del proyecto.
    </Callout>

    <Info>
      Este proxy monta automáticamente las siguientes rutas de autenticación:

      * `/auth/login` - Ruta de inicio de sesión
      * `/auth/logout` - Ruta de cierre de sesión
      * `/auth/callback` - Ruta de callback
      * `/auth/profile` - Ruta del perfil de usuario
      * `/auth/access-token` - Ruta del token de acceso
      * `/auth/backchannel-logout` - Ruta de cierre de sesión por backchannel
    </Info>
  </Step>

  <Step title="Crear componentes de inicio de sesión, cierre de sesión y perfil" stepNumber={7}>
    Agrega el código del componente a los archivos creados en el paso 3:

    <AuthCodeGroup>
      ```typescript src/components/LoginButton.tsx lines theme={null}
      "use client";

      export default function LoginButton() {
        return (
          <a
            href="/auth/login"
            className="w-full text-center inline-block px-6 py-3 bg-gradient-to-b from-[#2d2d42] to-[#161620] hover:opacity-90 text-white font-medium rounded-full text-[14px] transition-opacity"
          >
            Iniciar sesión
          </a>
        );
      }
      ```

      ```typescript src/components/LogoutButton.tsx lines theme={null}
      "use client";

      export default function LogoutButton() {
        return (
          <a
            href="/auth/logout"
            className="w-full text-center inline-block px-6 py-3 bg-[#f0f0f0] hover:bg-gray-200 text-gray-600 font-medium rounded-full text-[14px] transition-colors"
          >
            Cerrar sesión
          </a>
        );
      }
      ```

      ```typescript src/components/Profile.tsx lines theme={null}
      "use client";

      import { useUser } from "@auth0/nextjs-auth0/client";

      function getInitials(name?: string | null, email?: string | null): string {
        if (name) {
          const parts = name.trim().split(" ");
          return parts.length >= 2
            ? `${parts[0][0]}${parts[1][0]}`.toUpperCase()
            : parts[0].slice(0, 2).toUpperCase();
        }
        if (email) return email.slice(0, 2).toUpperCase();
        return "U";
      }

      export default function Profile() {
        const { user, isLoading } = useUser();

        if (isLoading) return <p className="text-xs text-gray-500">Cargando...</p>;
        if (!user) return null;

        return (
          <>
            <div className="flex items-center gap-2 text-green-500 text-[13px] font-medium">
              <span className="w-5 h-5 bg-green-500 rounded-full flex items-center justify-center shrink-0">
                <svg width="10" height="8" viewBox="0 0 10 8" fill="none">
                  <path d="M1 4L3.5 6.5L9 1" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </span>
              Autenticación correcta
            </div>
            <div className="flex items-center gap-2 bg-gray-100 rounded-full py-1.5 pl-1.5 pr-4 text-[12px] text-gray-700 max-w-full">
              <span className="w-7 h-7 bg-gradient-to-b from-[#2d2d42] to-[#161620] rounded-full flex items-center justify-center text-white text-[10px] font-semibold shrink-0">
                {getInitials(user.name, user.email)}
              </span>
              <span className="truncate">{user.email}</span>
            </div>
          </>
        );
      }
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="Actualiza tu página principal" stepNumber={8}>
    Reemplaza `src/app/page.tsx` con:

    ```typescript src/app/page.tsx lines theme={null}
    import { auth0 } from "@/lib/auth0";
    import LoginButton from "@/components/LoginButton";
    import LogoutButton from "@/components/LogoutButton";
    import Profile from "@/components/Profile";

    export default async function Home() {
      const session = await auth0.getSession();
      const user = session?.user;

      return (
        <main className="min-h-screen bg-[#efefef] flex flex-col items-center justify-center gap-4 px-6 py-12">
          <div className="bg-white rounded-[28px] shadow-[0_4px_32px_rgba(0,0,0,0.08)] px-12 py-14 flex flex-col items-center gap-4 w-[360px]">
            <svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg" className="mb-1">
              <g filter="url(#filter0_di)">
                <rect x="2" y="2" width="64" height="64" rx="16" fill="url(#paint0_linear)"/>
                <rect x="2.5" y="2.5" width="63" height="63" rx="15.5" stroke="#252525"/>
                <path d="M34.0002 18C25.1572 18 18 25.1669 18 34C18 42.8432 25.1672 50 34.0002 50C42.8333 50 50 42.8331 50 34C50 25.1669 42.8433 18 34.0002 18ZM43.9172 43.8971C43.9172 43.9071 43.9069 43.9072 43.9069 43.9172C43.9069 43.9172 43.8969 43.9272 43.8868 43.9272C43.144 44.65 41.9796 44.7303 41.0662 44.2585L40.0228 43.7265C36.2487 41.7792 31.7619 41.7792 27.9777 43.7265L26.9338 44.2585C26.0103 44.7303 24.8459 44.65 24.1132 43.9272C24.1132 43.9272 24.1031 43.9172 24.0931 43.9172C24.0931 43.9172 24.0828 43.9071 24.0828 43.8971C23.3601 43.1543 23.2797 41.9899 23.7515 41.0765L24.2837 40.0326C26.231 36.2585 26.231 31.7717 24.2837 27.9975L23.7515 26.9536C23.2797 26.0302 23.3601 24.8657 24.0828 24.133C24.0828 24.1229 24.0931 24.123 24.0931 24.123C24.0931 24.123 24.1031 24.1129 24.1132 24.1129C24.856 23.3902 26.0204 23.3099 26.9338 23.7817L27.9777 24.3137C31.7518 26.261 36.2386 26.261 40.0228 24.3137L41.0662 23.7817C41.9897 23.3099 43.1541 23.3902 43.8868 24.1129C43.8868 24.1129 43.8969 24.123 43.9069 24.123L43.9172 24.133C44.6399 24.8758 44.7203 26.0402 44.2485 26.9536L43.7163 27.9975C41.769 31.7717 41.769 36.2585 43.7163 40.0326L44.2485 41.0765C44.7203 41.9899 44.6499 43.1543 43.9172 43.8971Z" fill="white"/>
              </g>
              <defs>
                <filter id="filter0_di" x="0" y="0" width="68" height="68" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
                  <feFlood floodOpacity="0" result="BackgroundImageFix"/>
                  <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
                  <feMorphology radius="2" operator="dilate" in="SourceAlpha" result="effect1_dropShadow"/>
                  <feOffset/>
                  <feComposite in2="hardAlpha" operator="out"/>
                  <feColorMatrix type="matrix" values="0 0 0 0 0.117647 0 0 0 0 0.129412 0 0 0 0 0.164706 0 0 0 0.12 0"/>
                  <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
                  <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
                  <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
                  <feOffset dy="-1"/>
                  <feGaussianBlur stdDeviation="0.5"/>
                  <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
                  <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0"/>
                  <feBlend mode="normal" in2="shape" result="effect2_innerShadow"/>
                </filter>
                <linearGradient id="paint0_linear" x1="34" y1="2" x2="34" y2="66" gradientUnits="userSpaceOnUse">
                  <stop/>
                  <stop offset="1" stopColor="#677190"/>
                </linearGradient>
              </defs>
            </svg>

            {user ? (
              <>
                <h1 className="text-[17px] font-bold text-gray-900 tracking-tight">Your account</h1>
                <div className="w-full h-px bg-gray-100" />
                <Profile />
                <LogoutButton />
              </>
            ) : (
              <>
                <h1 className="text-[17px] font-bold text-gray-900 tracking-tight">Welcome to Sample0</h1>
                <p className="text-[13px] text-gray-400 text-center leading-relaxed -mt-2">
                  Get started by logging in to your account
                </p>
                <div className="h-3" />
                <LoginButton />
              </>
            )}
          </div>

          <div className="flex items-center gap-1.5 text-[11px] text-gray-400">
            <span>Powered by</span>
            <img
              src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-onlight.svg"
              alt="Auth0"
              className="h-3 opacity-40"
            />
          </div>
        </main>
      );
    }
    ```
  </Step>

  <Step title="Actualiza el layout con Auth0Provider" stepNumber={9}>
    Actualiza `src/app/layout.tsx` para cargar la fuente Inter y envolver la aplicación con `Auth0Provider`:

    ```typescript src/app/layout.tsx lines theme={null}
    import { Inter } from "next/font/google";
    import type { Metadata } from "next";
    import { Auth0Provider } from "@auth0/nextjs-auth0/client";
    import "./globals.css";

    const inter = Inter({
      subsets: ["latin"],
      weight: ["300", "400", "500", "600", "700"],
    });

    export const metadata: Metadata = {
      title: "Auth0 Next.js App",
      description: "Next.js app with Auth0 authentication",
    };

    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html lang="en">
          <body className={inter.className}>
            <Auth0Provider>{children}</Auth0Provider>
          </body>
        </html>
      );
    }
    ```

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      En la v4, Auth0Provider es opcional. Solo lo necesitas si quieres pasar un usuario inicial durante el renderizado en el servidor para que esté disponible en el hook useUser().
    </Callout>
  </Step>

  <Step title="Configura Tailwind CSS" stepNumber={10}>
    Reemplaza el contenido de `src/app/globals.css` con:

    ```css src/app/globals.css lines theme={null}
    @import "tailwindcss";
    ```
  </Step>

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

    <Info>
      Tu aplicación estará disponible en [http://localhost:3000](http://localhost:3000). El SDK v4 de Auth0 configura automáticamente las rutas de autenticación en `/auth/*` (no en `/api/auth/*`, como en la v3).

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

<Check>
  **Punto de control**

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

***

<div id="troubleshooting">
  ## Solución de problemas
</div>

<Accordion title="Error JWEDecryptionFailed">
  Si ves el error `JWEDecryptionFailed: decryption operation failed`, suele deberse a que `AUTH0_SECRET` no es válido o a que hay una cookie de sesión antigua cifrada con un secreto distinto.

  **Solución:**

  1. Genera un secreto nuevo con:

  ```shellscript theme={null}
  openssl rand -hex 32
  ```

  2. Actualiza tu archivo `.env.local`:

  ```
  AUTH0_SECRET=<tu-nueva-cadena-hexadecimal-de-64-caracteres>
  ```

  3. **Borra las cookies del navegador** para `localhost:3000`:
     * Chrome/Edge: Presiona `F12` → pestaña Application → Cookies → elimina todas las cookies de localhost
     * Firefox: Presiona `F12` → pestaña Storage → Cookies → elimina todas las cookies de localhost
     * Safari: menú Develop → Show Web Inspector → pestaña Storage → Cookies → Delete all

  4. Reinicia el servidor de desarrollo:

  ```shellscript theme={null}
  npm run dev
  ```

  El secreto debe tener exactamente 32 bytes (64 caracteres hexadecimales). El error se produce cuando la aplicación intenta descifrar una cookie de sesión existente que se cifró con un secreto distinto.
</Accordion>

<Accordion title="Error 404 en /auth/login">
  Si al hacer clic en iniciar sesión llegas a una página 404, revisa estos problemas habituales:

  1. **Ubicación del proxy:** Asegúrate de que `src/proxy.ts` exista en la ubicación correcta
  2. **Código del proxy:** Verifica que el proxy coincida con el código del paso 6
  3. **Reinicia el servidor:** Después de crear el archivo proxy, reinicia el servidor de desarrollo
  4. **Comprueba las importaciones:** Asegúrate de que la ruta `import { auth0 } from "./lib/auth0"` sea correcta
</Accordion>

<Accordion title="Errores de módulo no encontrado">
  Si ves "Cannot find module '@/components/LoginButton'" o errores similares:

  1. **Verifica que los archivos existan:** Comprueba que se hayan creado todos los archivos del paso 3
  2. **Revisa las rutas:** Asegúrate de que los componentes estén en el directorio `src/components/`
  3. **Reinicia TypeScript:** Presiona `Cmd+Shift+P` (Mac) o `Ctrl+Shift+P` (Windows) y ejecuta "TypeScript: Restart TS Server"
  4. **Comprueba las importaciones:** Asegúrate de que estás usando `@/components/*` (no `~/components/*`)
</Accordion>

***

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

<Accordion title="Cambios importantes en v4">
  Este inicio rápido usa **Auth0 Next.js SDK v4**, que incorpora cambios importantes con respecto a v3:

  * **No se necesitan controladores de rutas dinámicas** - El proxy monta automáticamente las rutas de autenticación
  * **Configuración simplificada del cliente** - `new Auth0Client()` lee automáticamente las variables de entorno
  * **Nuevas rutas** - Las rutas están en `/auth/*` en lugar de `/api/auth/*`
  * **Proxy obligatorio** - Toda la funcionalidad de autenticación pasa por `proxy.ts`
  * **Usa etiquetas `<a>`** - La navegación debe usar `<a href="/auth/login">` en lugar de botones con onClick

  ### Rutas de autenticación

  El SDK monta automáticamente estas rutas a través del proxy:

  | Ruta                       | Propósito                                          |
  | -------------------------- | -------------------------------------------------- |
  | `/auth/login`              | Iniciar sesión                                     |
  | `/auth/logout`             | Cerrar la sesión del usuario                       |
  | `/auth/callback`           | Gestionar el callback de Auth0                     |
  | `/auth/profile`            | Obtener el perfil del usuario                      |
  | `/auth/access-token`       | Obtener el token de acceso                         |
  | `/auth/backchannel-logout` | Gestionar el cierre de sesión por canal secundario |

  <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
    Si ves errores 404 en estas rutas, asegúrate de que:

    1. El archivo `proxy.ts` esté en la ubicación correcta (la raíz del proyecto o dentro de `src/` si usas un directorio `src/`)
    2. El proxy esté configurado correctamente con el patrón matcher que se muestra en el paso 6
    3. El servidor de desarrollo se haya reiniciado después de crear el archivo del proxy
  </Callout>
</Accordion>

<Accordion title="Autenticación del lado del servidor">
  Auth0 Next.js SDK v4 admite tanto los patrones de App Router como los de Pages Router. Estos son algunos patrones habituales del lado del servidor:

  <Tabs>
    <Tab title="App Router - Componente del servidor">
      ```typescript app/protected/page.tsx theme={null}
      import { auth0 } from "@/lib/auth0";
      import { redirect } from "next/navigation";

      export default async function ProtectedPage() {
        const session = await auth0.getSession();

        if (!session) {
          redirect('/auth/login');
        }

        return (
          <div>
            <h1>Protected Content</h1>
            <p>Welcome, {session.user.name}!</p>
          </div>
        );
      }
      ```
    </Tab>

    <Tab title="App Router - Ruta de API">
      ```typescript app/api/protected/route.ts theme={null}
      import { auth0 } from "@/lib/auth0";
      import { NextResponse } from "next/server";

      export async function GET() {
        const session = await auth0.getSession();

        if (!session) {
          return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
        }

        return NextResponse.json({
          message: "This is a protected API route",
          user: session.user
        });
      }
      ```
    </Tab>

    <Tab title="Pages Router - Página">
      ```typescript pages/protected.tsx theme={null}
      import { auth0 } from "@/lib/auth0";
      import { GetServerSideProps } from "next";

      export default function ProtectedPage({ user }: { user: any }) {
        return (
          <div>
            <h1>Protected Content</h1>
            <p>Welcome, {user.name}!</p>
          </div>
        );
      }

      export const getServerSideProps: GetServerSideProps = auth0.withPageAuthRequired();
      ```
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="Autenticación del lado del cliente">
  Para gestionar el estado de autenticación del lado del cliente, usa el hook `useUser`:

  ```typescript components/UserProfile.tsx theme={null}
  "use client";

  import { useUser } from "@auth0/nextjs-auth0/client";

  export default function UserProfile() {
    const { user, error, isLoading } = useUser();

    if (isLoading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;
    if (!user) return <div>Not logged in</div>;

    return (
      <div>
        <h2>{user.name}</h2>
        <p>{user.email}</p>
        <img src={user.picture} alt="Profile" referrerPolicy="no-referrer" />
      </div>
    );
  }
  ```
</Accordion>

<Accordion title="Protección de rutas de API">
  Para proteger rutas de API, usa el método `withApiAuthRequired`:

  ```typescript app/api/protected/route.ts theme={null}
  import { auth0 } from "@/lib/auth0";

  export const GET = auth0.withApiAuthRequired(async function handler() {
    const session = await auth0.getSession();

    return Response.json({
      message: "This is a protected API route",
      user: session?.user
    });
  });
  ```
</Accordion>

<Accordion title="Uso de tokens de Auth0 con backends de terceros">
  Si usas un servicio backend de terceros (como Convex, Supabase o Firebase) que requiere tokens de autenticación de Auth0, tendrás que pasar el token de acceso desde tu aplicación Next.js al cliente del backend.

  ### Obtención del Token de acceso

  **En el servidor (App Router):**

  ```typescript app/api/token/route.ts theme={null}
  import { auth0 } from "@/lib/auth0";
  import { NextResponse } from "next/server";

  export async function GET() {
    const session = await auth0.getSession();

    if (!session) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const accessToken = session.accessToken;
    return NextResponse.json({ accessToken });
  }
  ```

  **En el cliente:**

  ```typescript lib/convex-client.ts theme={null}
  "use client";

  import { ConvexProviderWithAuth0 } from "convex/react-auth0";
  import { ConvexReactClient } from "convex/react";

  const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

  export function ConvexClientProvider({ children }: { children: React.ReactNode }) {
    return (
      <ConvexProviderWithAuth0
        client={convex}
        useAuth0={() => ({
          // Obtén el token de acceso desde la ruta de API de tu aplicación Next.js
          getAccessToken: async () => {
            const response = await fetch("/api/token");
            const { accessToken } = await response.json();
            return accessToken;
          },
        })}
      >
        {children}
      </ConvexProviderWithAuth0>
    );
  }
  ```

  ### Configuración del backend

  La mayoría de los servicios de terceros necesitan tu dominio y audiencia de Auth0 para verificar los tokens. En la configuración del backend:

  ```typescript convex/auth.config.ts theme={null}
  export default {
    providers: [
      {
        domain: process.env.AUTH0_DOMAIN,
        applicationID: process.env.AUTH0_CLIENT_ID,
      },
    ],
  };
  ```

  <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
    Asegúrate de que tu aplicación de Auth0 esté configurada con una audiencia de API si tu backend la requiere. Puedes configurarla en Auth0 Dashboard, en Applications → APIs, o agregar `AUTH0_AUDIENCE` a tu `.env.local` y configurar el SDK en consecuencia.
  </Callout>

  ### Solución de problemas con tokens

  Si `ctx.auth.getUserIdentity()` devuelve `null` en tu backend:

  1. **Verifica que se esté enviando el token:** revisa la pestaña Network de las herramientas de desarrollo del navegador para confirmar que el token se incluya en las solicitudes
  2. **Comprueba el formato del token:** asegúrate de enviar el `accessToken`, no el `idToken`
  3. **Verifica la configuración del backend:** confirma que tu backend tenga el dominio de Auth0 y el ID de cliente correctos
  4. **Comprueba la audiencia:** si usas una API de Auth0, asegúrate de que `AUTH0_AUDIENCE` esté configurado y coincida con el identificador de tu API
  5. **Inspecciona los claims del token:** decodifica tu JWT en [jwt.io](https://jwt.io) para verificar que contiene los claims esperados
</Accordion>
