Saltar al contenido principal

Usa IA para integrar Auth0

Si usas un asistente de programación con IA como Claude Code, Cursor o GitHub Copilot, puedes añadir autenticación para tu API con Auth0 automáticamente en minutos mediante agent skills.Instala:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill go-jwt-middleware
Luego, pídele a tu asistente de IA:
Add Auth0 JWT authentication to my Go API
Tu asistente de IA creará automáticamente tu API en Auth0, obtendrá las credenciales, instalará go-jwt-middleware, configurará el validador y protegerá los endpoints de tu API con validación de JWT. Documentación completa sobre agent skills →
Requisitos previos: Antes de empezar, asegúrate de tener instalado lo siguiente:
  • Go 1.24 o posterior (necesario para la compatibilidad con genéricos en go-jwt-middleware v3)
  • Git para el control de versiones
Verifica la instalación: go version

Primeros pasos

Crearás una API de Go con tres endpoints que demuestran distintos niveles de protección: acceso público, autenticación con JWT y permisos según el scope. La implementación completa usa go-jwt-middleware v3 con la biblioteca estándar net/http de Go.

Ver ejemplo en GitHub

Ejemplo completo y funcional con pruebas
1

Crea un nuevo proyecto

Cree un directorio nuevo para su API de Go e inicialice un módulo.
mkdir myapi && cd myapi
go mod init github.com/yourorg/myapi
Instala las dependencias necesarias:
go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenv
go mod download
Cree la estructura del proyecto:
mkdir -p cmd/server internal/auth internal/config internal/handlers
touch .env cmd/server/main.go internal/config/auth.go internal/auth/claims.go internal/auth/validator.go internal/auth/middleware.go internal/handlers/api.go
go.mod
module github.com/yourorg/myapi

go 1.24

require (
    github.com/auth0/go-jwt-middleware/v3 v3.1.0
    github.com/joho/godotenv v1.5.1
)
2

Configura tu API de Auth0

A continuación, debes crear una nueva API en tu inquilino de Auth0 y agregar las variables de entorno a tu proyecto.Tienes dos opciones para configurar tu API de Auth0: usar un comando de la CLI o hacerlo manualmente desde el Dashboard:
Ejecuta el siguiente comando en el directorio raíz de tu proyecto para crear una API de Auth0:
# Instalar Auth0 CLI (si aún no está instalado)
brew tap auth0/auth0-cli && brew install auth0

# Crear API de Auth0
auth0 apis create \
  --name "My Go API" \
  --identifier https://my-go-api.example.com
Después de crearla, copia los valores de Identifier y dominio, y luego crea tu archivo .env:
Este comando hará lo siguiente:
  1. Verificará si ya iniciaste sesión (y te lo pedirá si es necesario)
  2. Creará una API de Auth0 con el identificador especificado
  3. Mostrará los detalles de la API, incluidos el dominio y el identificador
Seguridad: Nunca incluyas archivos .env en el control de versiones. Agrega .env a tu archivo .gitignore.
3

Definir permisos de la API

Los permisos (alcances) te permiten definir cómo se puede acceder a los recursos. Por ejemplo, otorga acceso read a los gerentes y acceso write a los administradores.
  1. En la configuración de tu API, haz clic en la pestaña Permissions
  2. Crea el siguiente permiso:
PermisoDescripción
read:messagesLeer mensajes de la API
Este tutorial usa el scope read:messages para proteger el endpoint restringido por alcance. Puedes definir permisos adicionales según las necesidades de tu aplicación.
4

Crear el cargador de configuración

Cree un paquete de configuración para cargar y validar las variables de entorno.
internal/config/auth.go
package config

import (
    "fmt"
    "os"
)

type AuthConfig struct {
    Domain   string
    Audience string
}

func LoadAuthConfig() (*AuthConfig, error) {
    domain := os.Getenv("AUTH0_DOMAIN")
    if domain == "" {
        return nil, fmt.Errorf("AUTH0_DOMAIN environment variable required")
    }

    audience := os.Getenv("AUTH0_AUDIENCE")
    if audience == "" {
        return nil, fmt.Errorf("AUTH0_AUDIENCE environment variable required")
    }

    return &AuthConfig{
        Domain:   domain,
        Audience: audience,
    }, nil
}
Qué hace esto:
  • Carga el dominio y la audiencia de Auth0 desde las variables de entorno
  • Valida al inicio que la configuración requerida esté presente
  • Devuelve una estructura de configuración segura en cuanto a tipos para usarla en toda la aplicación
5

Crear claims personalizadas y un validador de JWT

Los claims personalizados le permiten extraer y validar datos específicos de la aplicación de los JWT. El validador es el componente principal que verifica los tokens emitidos por Auth0.
internal/auth/claims.go
package auth

import (
    "context"
    "fmt"
    "strings"
)

// CustomClaims contiene los datos personalizados que queremos extraer del JWT.
type CustomClaims struct {
    Scope string `json:"scope"`
}

// Validate garantiza que los claims personalizados tengan el formato correcto.
func (c *CustomClaims) Validate(ctx context.Context) error {
    if c.Scope == "" {
        return nil
    }

    if strings.TrimSpace(c.Scope) != c.Scope {
        return fmt.Errorf("scope claim has invalid whitespace")
    }

    if strings.Contains(c.Scope, "  ") {
        return fmt.Errorf("scope claim contains double spaces")
    }

    return nil
}

// HasScope comprueba si nuestros claims incluyen un scope específico.
func (c *CustomClaims) HasScope(expectedScope string) bool {
    if c.Scope == "" {
        return false
    }

    scopes := strings.Split(c.Scope, " ")
    for _, scope := range scopes {
        if scope == expectedScope {
            return true
        }
    }
    return false
}
Puntos clave:
  • El método Validate se invoca automáticamente mediante el middleware después de procesar el JWT
  • HasScope analiza alcances separados por espacios para el control de acceso basado en permisos
  • El validador usa almacenamiento en caché de JWKS (TTL de 5 min) y permite una desviación de reloj de 30 s
  • El algoritmo RS256 se establece explícitamente para evitar ataques de confusión de algoritmos
6

Crear middleware y controladores HTTP

El middleware encapsula el validador para las solicitudes HTTP. Los controladores muestran tres niveles de protección: público, privado y con permisos según el scope.
internal/auth/middleware.go
package auth

import (
    "log/slog"
    "net/http"

    jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
    "github.com/auth0/go-jwt-middleware/v3/validator"
)

func NewMiddleware(jwtValidator *validator.Validator) (*jwtmiddleware.JWTMiddleware, error) {
    return jwtmiddleware.New(
        jwtmiddleware.WithValidator(jwtValidator),
        jwtmiddleware.WithValidateOnOptions(false),
        jwtmiddleware.WithErrorHandler(func(w http.ResponseWriter, r *http.Request, err error) {
            slog.Error("JWT validation failed", "error", err, "path", r.URL.Path)
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message":"Failed to validate JWT."}`))
        }),
    )
}
Niveles de protección:
  • Público (/api/public) — No requiere autenticación
  • Privado (/api/private) — Requiere un JWT válido
  • Con scope (/api/private-scoped) — Requiere un JWT válido y el permiso read:messages
7

Crear el servidor principal

Integra todo en el punto de entrada principal con tiempos de espera aptos para producción y un cierre ordenado:
cmd/server/main.go
package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"

    "github.com/yourorg/myapi/internal/auth"
    "github.com/yourorg/myapi/internal/config"
    "github.com/yourorg/myapi/internal/handlers"
    "github.com/joho/godotenv"
)

func main() {
    // Cargar variables de entorno desde el archivo .env
    if err := godotenv.Load(); err != nil {
        log.Println("No .env file found, using environment variables")
    }

    // Cargar configuración de Auth0
    cfg, err := config.LoadAuthConfig()
    if err != nil {
        log.Fatalf("Failed to load config: %v", err)
    }

    // Crear validador de JWT
    jwtValidator, err := auth.NewValidator(cfg.Domain, cfg.Audience)
    if err != nil {
        log.Fatalf("Failed to create validator: %v", err)
    }

    // Crear middleware HTTP
    middleware, err := auth.NewMiddleware(jwtValidator)
    if err != nil {
        log.Fatalf("Failed to create middleware: %v", err)
    }

    // Configurar rutas
    mux := http.NewServeMux()
    mux.HandleFunc("/api/public", handlers.PublicHandler)
    mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(handlers.PrivateHandler)))
    mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(handlers.ScopedHandler)))

    // Configurar servidor con tiempos de espera para producción
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    // Iniciar servidor en goroutine
    go func() {
        log.Println("Server starting on :8080")
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server failed: %v", err)
        }
    }()

    // Apagado controlado
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit

    log.Println("Shutting down server...")
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        log.Fatalf("Server forced to shutdown: %v", err)
    }

    log.Println("Server exited")
}
myapi/
├── cmd/
│   └── server/
│       └── main.go              # Punto de entrada de la aplicación
├── internal/
│   ├── auth/
│   │   ├── claims.go            # claims personalizados de JWT
│   │   ├── middleware.go         # Middleware de JWT
│   │   └── validator.go         # Validador de JWT
│   ├── config/
│   │   └── auth.go              # Cargador de configuración
│   └── handlers/
│       └── api.go               # Manejadores HTTP (públicos, privados, con scope)
├── .env                         # Variables de entorno (no incluido en el repositorio)
├── .gitignore
├── go.mod
└── go.sum
8

Ejecuta y prueba tu API

Inicia el servidor de desarrollo:
go run cmd/server/main.go
Deberías ver: Server starting on :8080Prueba el endpoint público (no se requiere autenticación):
curl http://localhost:8080/api/public
Deberías ver:
{
  "message": "Hello from a public endpoint! You don't need to be authenticated to see this."
}
Pruebe el endpoint privado sin un token (debería fallar):
curl http://localhost:8080/api/private
Deberías ver un error 401 Unauthorized:
{
  "message": "Failed to validate JWT."
}
Para probar con un token válido, vaya a su API en el Auth0 Dashboard, haga clic en la pestaña Test y copie el token de acceso. Luego, ejecute:
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     http://localhost:8080/api/private
Pruebe el endpoint con scope (requiere el permiso read:messages):
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     http://localhost:8080/api/private-scoped
ComprobaciónAhora deberías tener una API de Go protegida. Tu API:
  1. Acepta solicitudes a endpoints públicos sin autenticación
  2. Rechaza solicitudes a endpoints protegidos sin un token válido
  3. Valida los JWT con tu dominio y audiencia de Auth0
  4. Aplica un control de acceso basado en permisos mediante alcances

Llamar a su API

Puede llamar a su API protegida desde cualquier aplicación enviando un token de acceso en el encabezado Authorization como un token Bearer.

Ejemplos de código del cliente

curl --request GET \
  --url http://localhost:8080/api/private \
  --header 'authorization: Bearer YOUR_ACCESS_TOKEN'
Si llama a la API desde una aplicación de una sola página o una aplicación móvil/nativa, una vez completado el flujo de autorización, obtendrá un token de acceso. La forma de obtener el token y de llamar a la API dependerá del tipo de aplicación que esté desarrollando y del framework que esté usando.

Aplicaciones de una sola página

Inicios rápidos de React, Vue y Angular con ejemplos

Aplicaciones móviles/nativas

Inicios rápidos de iOS, Android y React Native

Uso avanzado

DPoP (Demonstrating Proof-of-Possession) según la RFC 9449 ofrece mayor seguridad al evitar el robo de tokens mediante la vinculación criptográfica a una clave.
internal/auth/middleware.go
func NewMiddleware(jwtValidator *validator.Validator) *jwtmiddleware.JWTMiddleware {
    return jwtmiddleware.New(
        jwtmiddleware.WithValidator(jwtValidator),
        jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPRequired),
        jwtmiddleware.WithLogger(slog.Default()),
    )
}
Modos de DPoP:
  • DPoPAllowed (predeterminado) — Acepta tokens Bearer y DPoP
  • DPoPRequired — Acepta solo tokens DPoP y rechaza Bearer
  • DPoPDisabled — Acepta solo tokens Bearer y rechaza DPoP
Se recomienda DPoP para API del sector financiero, API sanitarias y aplicaciones empresariales de alta seguridad. Más información en la documentación de DPoP.
Habilita CORS para permitir solicitudes desde aplicaciones web. Puedes usar un middleware sencillo o una biblioteca como rs/cors:
go get github.com/rs/cors
cmd/server/main.go
import "github.com/rs/cors"

// Envuelve el mux con un middleware de CORS
handler := cors.New(cors.Options{
    AllowedOrigins:   []string{"http://localhost:3000"},
    AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE"},
    AllowedHeaders:   []string{"Authorization", "Content-Type"},
    AllowCredentials: true,
}).Handler(mux)

srv := &http.Server{
    Addr:    ":8080",
    Handler: handler,
}
Para producción, especifica orígenes exactos en lugar de comodines.
Habilita el registro detallado para depurar la validación de tokens:
internal/auth/middleware.go
middleware := jwtmiddleware.New(
    jwtmiddleware.WithValidator(jwtValidator),
    jwtmiddleware.WithLogger(slog.Default()),
)
Agrega una verificación al inicio:
cmd/server/main.go
log.Printf("Validator configured:")
log.Printf("  Issuer: https://%s/", cfg.Domain)
log.Printf("  Audience: %s", cfg.Audience)
log.Printf("  Algorithm: RS256")

Solución de problemas

”No se pudo validar el JWT” o 401 No autorizado

Problema: La API no puede encontrar o validar el token de acceso.Soluciones:
  1. Asegúrate de que la cabecera Authorization esté presente: Authorization: Bearer YOUR_TOKEN
  2. Comprueba que “Bearer” esté incluido antes del token
  3. Verifica que el token no haya expirado
  4. Asegúrate de que estás usando un token de acceso, no un token de ID

”la claim aud no coincide”

Problema: La audiencia del token no coincide con tu API.Solución: Verifica que AUTH0_AUDIENCE coincida exactamente con el Identificador de API en el Auth0 Dashboard. La audiencia NO debe tener una barra diagonal al final:
# Correcto
AUTH0_AUDIENCE=https://my-go-api.example.com

# Incorrecto (con barra diagonal al final)
AUTH0_AUDIENCE=https://my-go-api.example.com/
La aplicación cliente también debe solicitar un token con el parámetro audience correcto.

”método de firma inesperado”

Problema: El algoritmo del token no coincide con la configuración del validador.Soluciones:
  1. Auth0 usa RS256 de forma predeterminada (asimétrico)
  2. Asegúrate de que tu validador especifique validator.RS256
  3. Nunca uses validator.HS256 para tokens de Auth0, salvo que se haya configurado específicamente

Endpoint JWKS inaccesible

Problema: El proveedor de caché de JWKS no puede acceder al endpoint de clave pública de Auth0.Soluciones:
  1. Comprueba la conectividad de red con Auth0 (configuración de firewall/proxy)
  2. Prueba el endpoint de JWKS manualmente: curl https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json
  3. Verifica que la región de Auth0 sea correcta (us/eu/au)

Ruta de importación incorrecta

Problema: cannot find package "github.com/auth0/go-jwt-middleware/v3/..."Solución: Asegúrate de que todas las importaciones usen el sufijo /v3:
// Correcto
import "github.com/auth0/go-jwt-middleware/v3/validator"

// Incorrecto
import "github.com/auth0/go-jwt-middleware/validator"

Errores de desfase del reloj / token expirado

Problema: El reloj del servidor está desincronizado, lo que hace que los tokens válidos parezcan expirados.Solución: El validador ya incluye una tolerancia de 30 s para el desfase del reloj. Si necesitas más, ajusta:
validator.WithAllowedClockSkew(60*time.Second)

Error al extraer claims

Problema: Failed to retrieve claims al usar genéricos.Solución: Asegúrate de usar el parámetro de tipo correcto:
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())

Próximos pasos

Ahora que tiene una API protegida, le recomendamos explorar:

Recursos