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

> Esta guía muestra cómo añadir el inicio de sesión de usuario a una aplicación web de Go con el SDK de go-auth0.

# Añade el inicio de sesión a tu aplicación Go

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 />

<Note>
  **Requisitos previos:** Antes de empezar, asegúrese de tener instalado lo siguiente:

  * **[Go](https://go.dev/doc/install)** 1.25 o una versión posterior
  * **[Git](https://git-scm.com/downloads)** para el control de versiones

  Verifique la instalación: `go version`
</Note>

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

En esta guía de inicio rápido, aprenderás a agregar autenticación de Auth0 a una aplicación web en Go. Crearás una aplicación del lado del servidor con funciones de inicio de sesión, cierre de sesión y perfil de usuario mediante el SDK [go-auth0](https://github.com/auth0/go-auth0) y la biblioteca estándar `net/http` de Go.

export const localEnvSnippet = `# La URL del dominio de tu Tenant de Auth0.
# Si usas un dominio personalizado, usa ese valor en su lugar.
AUTH0_DOMAIN={yourDomain}

# El ID de cliente de tu aplicación de Auth0.
AUTH0_CLIENT_ID={yourClientId}

# El Secreto del cliente de tu aplicación de Auth0.
AUTH0_CLIENT_SECRET={yourClientSecret}

# La URL de callback de tu aplicación.
AUTH0_CALLBACK_URL=http://localhost:3000/callback

# Un secreto largo y aleatorio que se usa para cifrar las cookies de sesión.
SESSION_SECRET=a-long-random-secret-key-for-cookie-encryption`;

<Steps>
  <Step title="Crear un nuevo proyecto" stepNumber={1}>
    Cree un directorio nuevo para su aplicación Go e inicialice un módulo.

    ```shellscript theme={null}
    mkdir myapp && cd myapp
    go mod init myapp
    ```

    Instala las dependencias requeridas:

    ```shellscript theme={null}
    go get github.com/auth0/go-auth0/v2
    go get github.com/gorilla/sessions
    go get github.com/joho/godotenv
    ```

    Cree la estructura del proyecto:

    ```shellscript theme={null}
    mkdir templates
    touch .env auth.go handlers.go main.go templates/home.html templates/user.html
    ```

    <Accordion title="Ver el archivo go.mod esperado">
      ```go go.mod theme={null}
      module myapp

      go 1.25

      require (
          github.com/auth0/go-auth0/v2 v2.10.0
          github.com/gorilla/sessions v1.4.0
          github.com/joho/godotenv v1.5.1
      )
      ```
    </Accordion>
  </Step>

  <Step title="Configura tu aplicación en Auth0" stepNumber={2}>
    Para usar los servicios de Auth0, necesitas una aplicación configurada en el Auth0 Dashboard. En la aplicación de Auth0 configuras la autenticación de tu proyecto.

    Necesitas la siguiente información de la pestaña **Settings** de tu aplicación:

    * **Dominio**
    * **ID de cliente**
    * **Secreto del cliente**

    <Frame>![Dashboard de la aplicación](https://cdn2.auth0.com/docs/1.14550.0/media/articles/dashboard/client_settings.png)</Frame>

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

    <Tabs>
      <Tab title="Quick Setup (recomendado)">
        Crea una aplicación de Auth0 y copia el archivo `.env` precompletado con los valores de configuración correctos.

        <CreateInteractiveApp placeholderText="Go" appType="regular_web" allowedCallbackUrls={["http://localhost:3000/callback"]} allowedLogoutUrls={["http://localhost:3000"]} />

        <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:

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

          # Crea una aplicación de Auth0
          auth0 apps create \
            --name "My Go App" \
            --type regular \
            --callbacks http://localhost:3000/callback \
            --logout-urls http://localhost: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

          # Crea una aplicación de Auth0
          auth0 apps create `
            --name "My Go App" `
            --type regular `
            --callbacks http://localhost:3000/callback `
            --logout-urls http://localhost:3000
          ```
        </CodeGroup>

        Después de crearla, copia los valores de **Dominio**, **ID de cliente** y **Secreto del cliente**, y luego crea tu archivo `.env`:

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

        <Note>
          Este comando hará lo siguiente:

          1. Comprobará si has iniciado sesión (y te pedirá que lo hagas si es necesario)
          2. Creará una aplicación web regular de Auth0 configurada para `http://localhost:3000`
          3. Mostrará los detalles de la aplicación, incluidos el dominio, el ID de cliente y el secreto del cliente
        </Note>
      </Tab>

      <Tab title="Dashboard">
        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 Applications` 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. Toma nota de los valores de **Dominio**, **ID de cliente** y **Secreto del cliente**

        ### Configurar las URL de callback

        Una URL de callback es la URL de tu aplicación a la que Auth0 redirige al usuario después de autenticarse. Si no está configurada, los usuarios no volverán a tu aplicación después de iniciar sesión.

        Configura **Allowed Callback URLs** en:

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

        ### Configurar las URL de cierre de sesión

        Una URL de cierre de sesión es la URL de tu aplicación a la que Auth0 puede redirigir al usuario después de cerrar sesión. Si no está configurada, los usuarios no podrán cerrar sesión desde tu aplicación y recibirán un error.

        Configura **Allowed Logout URLs** en:

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

        ### Crea tu archivo de entorno

        Crea un archivo `.env` en el directorio raíz de tu proyecto con los valores del dashboard:

        ```shellscript .env theme={null}
        AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
        AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
        AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
        AUTH0_CALLBACK_URL=http://localhost:3000/callback
        SESSION_SECRET=a-long-random-secret-key-for-cookie-encryption
        ```

        <Warning>
          Sustituye `YOUR_AUTH0_DOMAIN`, `YOUR_AUTH0_CLIENT_ID` y `YOUR_AUTH0_CLIENT_SECRET` por los valores reales de la pestaña **Settings** de tu aplicación.
        </Warning>
      </Tab>
    </Tabs>

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

  <Step title="Crear el cliente de Auth0" stepNumber={3}>
    Cree el archivo `auth.go`. Este encapsula el cliente de autenticación `go-auth0` y proporciona un asistente para generar la URL de autorización.

    ```go auth.go lines theme={null}
    package main

    import (
        "context"
        "fmt"
        "net/url"
        "os"

        "github.com/auth0/go-auth0/v2/authentication"
    )

    // Authenticator envuelve el cliente de autenticación go-auth0.
    type Authenticator struct {
        *authentication.Authentication
        Domain      string
        ClientID    string
        CallbackURL string
    }

    // NewAuthenticator crea y configura un nuevo Authenticator.
    func NewAuthenticator() (*Authenticator, error) {
        domain := os.Getenv("AUTH0_DOMAIN")
        clientID := os.Getenv("AUTH0_CLIENT_ID")
        clientSecret := os.Getenv("AUTH0_CLIENT_SECRET")
        callbackURL := os.Getenv("AUTH0_CALLBACK_URL")

        authClient, err := authentication.New(
            context.Background(),
            domain,
            authentication.WithClientID(clientID),
            authentication.WithClientSecret(clientSecret),
        )
        if err != nil {
            return nil, fmt.Errorf("failed to initialize authentication client: %w", err)
        }

        return &Authenticator{
            Authentication: authClient,
            Domain:         domain,
            ClientID:       clientID,
            CallbackURL:    callbackURL,
        }, nil
    }

    // AuthorizationURL construye la URL /authorize para redirigir a los usuarios
    // a la página de Universal Login de Auth0.
    func (a *Authenticator) AuthorizationURL(state string) string {
        u, _ := url.Parse("https://" + a.Domain + "/authorize")
        params := url.Values{
            "response_type": {"code"},
            "client_id":     {a.ClientID},
            "redirect_uri":  {a.CallbackURL},
            "scope":         {"openid profile email"},
            "state":         {state},
        }
        u.RawQuery = params.Encode()
        return u.String()
    }
    ```

    **Qué hace esto:**

    * Inicializa el cliente de autenticación `go-auth0` con el dominio de tu inquilino, el ID de cliente y el secreto del cliente
    * Proporciona un asistente `AuthorizationURL` que genera la URL de redirección `/authorize` con los parámetros de OAuth2 necesarios
  </Step>

  <Step title="Crear manejadores de ruta" stepNumber={4}>
    Crea el archivo `handlers.go` con los controladores para inicio de sesión, callback, perfil de usuario y cierre de sesión.

    ```go handlers.go expandable lines theme={null}
    package main

    import (
        "crypto/rand"
        "encoding/base64"
        "encoding/gob"
        "net/http"
        "net/url"
        "os"

        "github.com/auth0/go-auth0/v2/authentication/oauth"
        "github.com/gorilla/sessions"
    )

    var store *sessions.CookieStore

    func init() {
        gob.Register(map[string]interface{}{})
    }

    func initSessionStore() {
        store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_SECRET")))
        store.Options = &sessions.Options{
            Path:     "/",
            MaxAge:   86400,
            HttpOnly: true,
            Secure:   false, // Establecer en true en producción (requiere HTTPS)
            SameSite: http.SameSiteLaxMode,
        }
    }

    // HomeHandler renderiza la página de inicio o redirige a /user si el usuario ya inició sesión.
    func HomeHandler(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "auth-session")
        if session.Values["profile"] != nil {
            http.Redirect(w, r, "/user", http.StatusSeeOther)
            return
        }
        if err := templates.ExecuteTemplate(w, "home.html", nil); err != nil {
            http.Error(w, "Internal error", http.StatusInternalServerError)
        }
    }

    // LoginHandler redirige al usuario a la página de Universal Login de Auth0.
    func LoginHandler(auth *Authenticator) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            state, err := generateRandomState()
            if err != nil {
                http.Error(w, "Internal error", http.StatusInternalServerError)
                return
            }

            session, _ := store.Get(r, "auth-session")
            session.Values["state"] = state
            if err := session.Save(r, w); err != nil {
                http.Error(w, "Internal error", http.StatusInternalServerError)
                return
            }

            http.Redirect(w, r, auth.AuthorizationURL(state), http.StatusTemporaryRedirect)
        }
    }

    // CallbackHandler gestiona el callback de Auth0 después de la autenticación.
    func CallbackHandler(auth *Authenticator) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            session, _ := store.Get(r, "auth-session")

            // Verificar el parámetro state para prevenir ataques CSRF.
            if r.URL.Query().Get("state") != session.Values["state"] {
                http.Error(w, "Invalid state parameter", http.StatusBadRequest)
                return
            }

            // Intercambiar el código de autorización por tokens.
            tokenSet, err := auth.OAuth.LoginWithAuthCode(r.Context(), oauth.LoginWithAuthCodeRequest{
                Code:        r.URL.Query().Get("code"),
                RedirectURI: auth.CallbackURL,
            }, oauth.IDTokenValidationOptions{})
            if err != nil {
                http.Error(w, "Failed to exchange authorization code for token", http.StatusUnauthorized)
                return
            }

            // Recuperar la información del perfil del usuario.
            userInfo, err := auth.UserInfo(r.Context(), tokenSet.AccessToken)
            if err != nil {
                http.Error(w, "Failed to get user info", http.StatusInternalServerError)
                return
            }

            session.Values["access_token"] = tokenSet.AccessToken
            session.Values["profile"] = map[string]interface{}{
                "nickname": userInfo.Nickname,
                "name":     userInfo.Name,
                "picture":  userInfo.Picture,
                "email":    userInfo.Email,
            }
            if err := session.Save(r, w); err != nil {
                http.Error(w, "Internal error", http.StatusInternalServerError)
                return
            }

            http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
        }
    }

    // UserHandler muestra el perfil del usuario autenticado.
    func UserHandler(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "auth-session")
        profile, ok := session.Values["profile"].(map[string]interface{})
        if !ok {
            http.Redirect(w, r, "/", http.StatusSeeOther)
            return
        }

        if err := templates.ExecuteTemplate(w, "user.html", profile); err != nil {
            http.Error(w, "Internal error", http.StatusInternalServerError)
        }
    }

    // LogoutHandler borra la sesión y redirige al endpoint de cierre de sesión de Auth0.
    func LogoutHandler(auth *Authenticator) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            session, _ := store.Get(r, "auth-session")
            session.Options.MaxAge = -1
            session.Save(r, w)

            logoutURL, _ := url.Parse("https://" + auth.Domain + "/v2/logout")
            scheme := "http"
            if r.TLS != nil {
                scheme = "https"
            }

            returnTo, _ := url.Parse(scheme + "://" + r.Host)
            params := url.Values{}
            params.Add("returnTo", returnTo.String())
            params.Add("client_id", auth.ClientID)
            logoutURL.RawQuery = params.Encode()

            http.Redirect(w, r, logoutURL.String(), http.StatusTemporaryRedirect)
        }
    }

    func generateRandomState() (string, error) {
        b := make([]byte, 32)
        _, err := rand.Read(b)
        if err != nil {
            return "", err
        }
        return base64.RawURLEncoding.EncodeToString(b), nil
    }
    ```

    **Puntos clave:**

    * **LoginHandler** genera un parámetro de estado aleatorio para proteger contra CSRF y redirige al Universal Login de Auth0
    * **CallbackHandler** intercambia el code de autorización por tokens con `go-auth0` y luego llama a `UserInfo` para obtener el perfil del usuario
    * **LogoutHandler** borra la sesión y redirige al endpoint `/v2/logout` de Auth0
    * **UserHandler** recupera el perfil de la sesión y muestra la plantilla
  </Step>

  <Step title="Crear plantillas HTML" stepNumber={5}>
    <Tabs>
      <Tab title="home.html">
        ```html templates/home.html expandable lines theme={null}
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Auth0 Go Web App</title>
            <style>
                * { margin: 0; padding: 0; box-sizing: border-box; }
                body {
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    background: #0a0a0a;
                    color: #e2e8f0;
                    min-height: 100vh;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
                .container {
                    text-align: center;
                    padding: 3rem;
                    background: #1a1a2e;
                    border-radius: 16px;
                    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
                    max-width: 440px;
                    width: 90%;
                }
                .logo {
                    width: 48px;
                    height: 48px;
                    margin: 0 auto 1.5rem;
                    background: linear-gradient(135deg, #635bff, #00d4aa);
                    border-radius: 12px;
                }
                h1 {
                    font-size: 2rem;
                    font-weight: 700;
                    margin-bottom: 0.5rem;
                    color: #f8fafc;
                }
                p {
                    color: #94a3b8;
                    font-size: 1.05rem;
                    margin-bottom: 2rem;
                    line-height: 1.6;
                }
                .btn {
                    display: inline-block;
                    padding: 0.85rem 2.5rem;
                    background: linear-gradient(135deg, #635bff, #4f46e5);
                    color: #fff;
                    text-decoration: none;
                    border-radius: 8px;
                    font-size: 1rem;
                    font-weight: 600;
                    transition: transform 0.2s, box-shadow 0.2s;
                    box-shadow: 0 4px 14px rgba(99, 91, 255, 0.4);
                }
                .btn:hover {
                    transform: translateY(-2px);
                    box-shadow: 0 6px 20px rgba(99, 91, 255, 0.6);
                }
                .footer {
                    margin-top: 2rem;
                    font-size: 0.85rem;
                    color: #475569;
                }
                .footer a { color: #635bff; text-decoration: none; }
            </style>
        </head>
        <body>
            <div class="container">
                <div class="logo"></div>
                <h1>Go + Auth0</h1>
                <p>Secure authentication for your Go web application, powered by Auth0.</p>
                <a href="/login" class="btn">Sign In</a>
                <div class="footer">
                    Protected by <a href="https://auth0.com">Auth0</a>
                </div>
            </div>
        </body>
        </html>
        ```
      </Tab>

      <Tab title="user.html">
        ```html templates/user.html expandable lines theme={null}
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Profile</title>
            <style>
                * { margin: 0; padding: 0; box-sizing: border-box; }
                body {
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    background: #0a0a0a;
                    color: #e2e8f0;
                    min-height: 100vh;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
                .container {
                    text-align: center;
                    padding: 3rem;
                    background: #1a1a2e;
                    border-radius: 16px;
                    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
                    max-width: 440px;
                    width: 90%;
                }
                .avatar {
                    display: block;
                    width: 96px;
                    height: 96px;
                    border-radius: 50%;
                    border: 3px solid #635bff;
                    margin: 0 auto 1.5rem;
                    object-fit: cover;
                    background: #2d2d5e;
                }
                .avatar[src=""], .avatar:not([src]) {
                    display: none;
                }
                .avatar-fallback {
                    width: 96px;
                    height: 96px;
                    border-radius: 50%;
                    border: 3px solid #635bff;
                    margin: 0 auto 1.5rem;
                    background: linear-gradient(135deg, #635bff, #4f46e5);
                    display: none;
                    align-items: center;
                    justify-content: center;
                    font-size: 2rem;
                    font-weight: 700;
                    color: #fff;
                }
                .badge {
                    display: inline-block;
                    padding: 0.3rem 0.75rem;
                    background: rgba(0, 212, 170, 0.15);
                    color: #00d4aa;
                    border-radius: 20px;
                    font-size: 0.8rem;
                    font-weight: 600;
                    margin-bottom: 1rem;
                }
                h2 {
                    font-size: 1.75rem;
                    font-weight: 700;
                    color: #f8fafc;
                    margin-bottom: 0.25rem;
                }
                .email {
                    color: #94a3b8;
                    font-size: 1rem;
                    margin-bottom: 2rem;
                }
                .info-card {
                    background: #0f0f23;
                    border-radius: 10px;
                    padding: 1.25rem;
                    margin-bottom: 1.5rem;
                    text-align: left;
                }
                .info-row {
                    display: flex;
                    justify-content: space-between;
                    padding: 0.5rem 0;
                    border-bottom: 1px solid #1e293b;
                }
                .info-row:last-child { border-bottom: none; }
                .info-label { color: #64748b; font-size: 0.875rem; }
                .info-value { color: #e2e8f0; font-size: 0.875rem; font-weight: 500; }
                .btn-logout {
                    display: inline-block;
                    padding: 0.75rem 2rem;
                    background: transparent;
                    color: #f87171;
                    text-decoration: none;
                    border-radius: 8px;
                    font-size: 0.95rem;
                    font-weight: 600;
                    border: 1px solid #f87171;
                    transition: all 0.2s;
                }
                .btn-logout:hover {
                    background: #f87171;
                    color: #0a0a0a;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <img class="avatar" src="{{ .picture }}" alt="{{ .name }}" onerror="this.style.display='none';document.getElementById('avatar-fallback').style.display='flex';" />
                <div id="avatar-fallback" class="avatar-fallback">{{ if .nickname }}{{ slice .nickname 0 1 }}{{ else }}?{{ end }}</div>
                <span class="badge">Autenticado</span>
                <h2>{{ .nickname }}</h2>
                <p class="email">{{ .email }}</p>
                <div class="info-card">
                    <div class="info-row">
                        <span class="info-label">Nombre completo</span>
                        <span class="info-value">{{ .name }}</span>
                    </div>
                    <div class="info-row">
                        <span class="info-label">Apodo</span>
                        <span class="info-value">{{ .nickname }}</span>
                    </div>
                    <div class="info-row">
                        <span class="info-label">Correo electrónico</span>
                        <span class="info-value">{{ .email }}</span>
                    </div>
                </div>
                <a href="/logout" class="btn-logout">Cerrar sesión</a>
            </div>
        </body>
        </html>
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Cree el servidor principal" stepNumber={6}>
    Integra todo en `main.go`:

    ```go main.go lines theme={null}
    package main

    import (
        "html/template"
        "log"
        "net/http"

        "github.com/joho/godotenv"
    )

    var templates *template.Template

    func main() {
        // El archivo .env es opcional; en Docker, las variables de entorno provienen del flag --env-file.
        godotenv.Load()

        initSessionStore()

        auth, err := NewAuthenticator()
        if err != nil {
            log.Fatalf("Failed to initialize the authenticator: %v", err)
        }

        templates = template.Must(template.ParseGlob("templates/*.html"))

        mux := http.NewServeMux()
        mux.HandleFunc("/", HomeHandler)
        mux.HandleFunc("/login", LoginHandler(auth))
        mux.HandleFunc("/callback", CallbackHandler(auth))
        mux.HandleFunc("/user", UserHandler)
        mux.HandleFunc("/logout", LogoutHandler(auth))

        log.Print("Server listening on http://localhost:3000/")
        if err := http.ListenAndServe("0.0.0.0:3000", mux); err != nil {
            log.Fatalf("There was an error with the http server: %v", err)
        }
    }
    ```

    <Accordion title="Estructura del proyecto">
      ```
      myapp/
      ├── main.go          # Punto de entrada de la aplicación y rutas
      ├── auth.go          # Configuración del cliente de Auth0 y URL de autorización
      ├── handlers.go      # Manejadores HTTP (inicio de sesión, callback, cierre de sesión, perfil)
      ├── templates/
      │   ├── home.html    # Página principal con enlace de inicio de sesión
      │   └── user.html    # Página de perfil del usuario
      ├── .env             # Variables de entorno (no se incluye en el commit)
      ├── go.mod
      └── go.sum
      ```
    </Accordion>
  </Step>

  <Step title="Ejecuta y prueba tu aplicación" stepNumber={7}>
    Inicia el servidor de desarrollo:

    ```shellscript theme={null}
    go run .
    ```

    Deberías ver lo siguiente: `Server listening on http://localhost:3000/`

    Abre [http://localhost:3000](http://localhost:3000) en tu navegador. Haz clic en **Sign In** para que se te redirija a la página de Universal Login de Auth0. Después de autenticarte, volverás a tu aplicación y verás la información de tu perfil.

    <Info>
      Si el puerto 3000 ya está en uso, actualiza `AUTH0_CALLBACK_URL` en tu archivo `.env`, y **Allowed Callback URLs** y **Allowed Logout URLs** en la configuración de tu aplicación de Auth0 para usar el nuevo puerto.
    </Info>
  </Step>
</Steps>

<Check>
  **Punto de control**

  Ahora debería tener una aplicación web en Go totalmente funcional con autenticación de Auth0 ejecutándose en su [localhost](http://localhost:3000/). Su aplicación:

  1. Redirige a los usuarios al Universal Login de Auth0 para autenticarse
  2. Intercambia el código de autorización por tokens mediante el SDK `go-auth0`
  3. Recupera y muestra la información del perfil del usuario
  4. Admite el cierre de sesión con limpieza de los datos de sesión
</Check>

***

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

<Accordion title="Protección de rutas con middleware">
  Cree un middleware `IsAuthenticated` para proteger las rutas que requieren autenticación. Agregue esto a su `handlers.go`:

  ```go handlers.go theme={null}
  // IsAuthenticated es un middleware que comprueba si
  // el usuario se ha autenticado.
  func IsAuthenticated(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
          session, _ := store.Get(r, "auth-session")
          if session.Values["profile"] == nil {
              http.Redirect(w, r, "/", http.StatusSeeOther)
              return
          }
          next.ServeHTTP(w, r)
      })
  }
  ```

  Aplique el middleware a las rutas protegidas en `main.go`:

  ```go main.go theme={null}
  mux.Handle("/user", IsAuthenticated(http.HandlerFunc(UserHandler)))
  ```
</Accordion>

<Accordion title="Llamar a una API protegida">
  Después de autenticarse, puede usar el token de acceso almacenado en la sesión para llamar a una API protegida:

  ```go theme={null}
  func ApiHandler(w http.ResponseWriter, r *http.Request) {
      session, _ := store.Get(r, "auth-session")
      accessToken, ok := session.Values["access_token"].(string)
      if !ok {
          http.Error(w, "Unauthorized", http.StatusUnauthorized)
          return
      }

      req, _ := http.NewRequest("GET", "https://your-api.example.com/api/private", nil)
      req.Header.Add("Authorization", "Bearer "+accessToken)

      res, err := http.DefaultClient.Do(req)
      if err != nil {
          http.Error(w, "API request failed", http.StatusInternalServerError)
          return
      }
      defer res.Body.Close()

      w.Header().Set("Content-Type", "application/json")
      io.Copy(w, res.Body)
  }
  ```

  <Note>
    Para solicitar un token de acceso para su API, agregue el parámetro `audience` a la URL de autorización en `auth.go`:

    ```go theme={null}
    params := url.Values{
        "response_type": {"code"},
        "client_id":     {a.ClientID},
        "redirect_uri":  {a.CallbackURL},
        "scope":         {"openid profile email"},
        "audience":      {"https://your-api.example.com"},
        "state":         {state},
    }
    ```
  </Note>
</Accordion>

<Accordion title="Renovar tokens">
  Si su aplicación usa tokens de actualización, puede intercambiar un token de actualización por un nuevo conjunto de tokens con el SDK `go-auth0`:

  ```go theme={null}
  newTokenSet, err := auth.OAuth.RefreshToken(r.Context(), oauth.RefreshTokenRequest{
      RefreshToken: storedRefreshToken,
  })
  ```

  Para recibir un token de actualización, agregue `offline_access` a los alcances en su URL de autorización:

  ```go theme={null}
  "scope": {"openid profile email offline_access"},
  ```
</Accordion>

***

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

<AccordionGroup>
  <Accordion title="Problemas comunes y soluciones">
    ### "No se pudo intercambiar el código de autorización por un token"

    **Problema:** El manejador de callback no puede intercambiar el código de autorización.

    **Soluciones:**

    1. Verifica que `AUTH0_CLIENT_SECRET` sea correcto en tu archivo `.env`
    2. Asegúrate de que `AUTH0_CALLBACK_URL` coincida exactamente con **Allowed Callback URLs** en la configuración de tu aplicación de Auth0
    3. Comprueba que el código de autorización no haya expirado (los códigos son de un solo uso y de corta duración)

    ### "No se pudo obtener la información del usuario"

    **Problema:** El endpoint `/userinfo` devuelve un error.

    **Soluciones:**

    1. Asegúrate de que el scope `openid` esté incluido en la URL de autorización
    2. Verifica que el token de acceso sea válido y no haya expirado
    3. Comprueba la conectividad de red con tu dominio de Auth0

    ### "Parámetro state no válido"

    **Problema:** El parámetro state en el callback no coincide con la sesión.

    **Soluciones:**

    1. Asegúrate de que las cookies estén habilitadas en tu navegador
    2. Comprueba que el secreto del almacén de sesiones no haya cambiado entre solicitudes
    3. Verifica que no estés usando varias pestañas del navegador durante el flujo de inicio de sesión

    ### Los usuarios no pueden cerrar sesión

    **Problema:** Después de hacer clic en cerrar sesión, los usuarios ven una página de error de Auth0.

    **Soluciones:**

    1. Verifica que `http://localhost:3000` esté en **Allowed Logout URLs** en la configuración de tu aplicación de Auth0
    2. Asegúrate de que el parámetro `client_id` coincida con el ID de cliente de tu aplicación
    3. Comprueba que la URL `returnTo` coincida exactamente con una de las URL de cierre de sesión permitidas

    ### Los datos de la sesión no se conservan

    **Problema:** Los datos del perfil del usuario desaparecen entre solicitudes.

    **Soluciones:**

    1. Asegúrate de llamar a `gob.Register(map[string]interface{}{})` antes de almacenar datos
    2. Comprueba que se llame a `session.Save(r, w)` después de modificar los valores de la sesión
    3. Verifica que la configuración del navegador no esté bloqueando las cookies
  </Accordion>
</AccordionGroup>

***

<div id="next-steps">
  ## Siguientes pasos
</div>

Ahora que ya tiene la autenticación en funcionamiento, considere consultar lo siguiente:

* **[Perfiles de usuario](/es/docs/manage-users/user-accounts/user-profiles)** — Conozca la información del usuario disponible después de la autenticación
* **[Configurar otros proveedores de identidad](/es/docs/authenticate/identity-providers)** — Agregue opciones de inicio de sesión social o empresariales
* **[Habilitar la autenticación multifactor](/es/docs/secure/multi-factor-authentication)** — Agregue una capa adicional de seguridad
* **[Prácticas recomendadas para tokens de acceso](/es/docs/secure/tokens/access-tokens)** — Obtenga información sobre la seguridad de los tokens
* **[Lista de verificación de producción](/es/docs/deploy-monitor/pre-deployment-checks/how-to-run-production-checks)** — Revisión de seguridad previa al lanzamiento

***

<div id="resources">
  ## Recursos
</div>

* **[go-auth0 GitHub](https://github.com/auth0/go-auth0)** — Código fuente y documentación
* **[Ejemplo de aplicación web en Go](https://github.com/auth0-samples/auth0-golang-web-app/tree/master/01-Login-Quickstart)** — Ejemplo funcional completo
* **[Comunidad de Auth0](https://community.auth0.com/)** — Obtén ayuda de la comunidad
