Saltar al contenido principal
Requisitos previos: Antes de empezar, asegúrese de tener instalado lo siguiente:
  • Go 1.25 o una versión posterior
  • Git para el control de versiones
Verifique la instalación: go version

Primeros pasos

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 y la biblioteca estándar net/http de Go.
1

Crear un nuevo proyecto

Cree un directorio nuevo para su aplicación Go e inicialice un módulo.
mkdir myapp && cd myapp
go mod init myapp
Instala las dependencias requeridas:
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:
mkdir templates
touch .env auth.go handlers.go main.go templates/home.html templates/user.html
go.mod
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
)
2

Configura tu aplicación en Auth0

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
Dashboard de la aplicación
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:
Crea una aplicación de Auth0 y copia el archivo .env precompletado con los valores de configuración correctos.
Verifica que tu archivo .env exista: cat .env (Mac/Linux) o type .env (Windows)
3

Crear el cliente de Auth0

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.
auth.go
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
4

Crear manejadores de ruta

Crea el archivo handlers.go con los controladores para inicio de sesión, callback, perfil de usuario y cierre de sesión.
handlers.go
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
5

Crear plantillas HTML

templates/home.html
<!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>
6

Cree el servidor principal

Integra todo en main.go:
main.go
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)
    }
}
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
7

Ejecuta y prueba tu aplicación

Inicia el servidor de desarrollo:
go run .
Deberías ver lo siguiente: Server listening on http://localhost:3000/Abre 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.
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.
Punto de controlAhora debería tener una aplicación web en Go totalmente funcional con autenticación de Auth0 ejecutándose en su localhost. 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

Uso avanzado

Cree un middleware IsAuthenticated para proteger las rutas que requieren autenticación. Agregue esto a su handlers.go:
handlers.go
// 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:
main.go
mux.Handle("/user", IsAuthenticated(http.HandlerFunc(UserHandler)))
Después de autenticarse, puede usar el token de acceso almacenado en la sesión para llamar a una API protegida:
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)
}
Para solicitar un token de acceso para su API, agregue el parámetro audience a la URL de autorización en auth.go:
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},
}
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:
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:
"scope": {"openid profile email offline_access"},

Solución de problemas

”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

Siguientes pasos

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

Recursos