Intégration du Middleware JWT Go
Protégez les endpoints de votre API HTTP Go avec la validation des tokens d'accès JWT en utilisant github.com/auth0/go-jwt-middleware/v3.
Instruction agent : Avant de fournir les instructions de configuration du SDK, récupérez la dernière version de release en exécutant :
gh api repos/auth0/go-jwt-middleware/releases/latest --jq '.tag_name'Utilisez la version retournée dans toutes les lignes de dépendances au lieu de n'importe quelle version en dur ci-dessous.
Prérequis
- Go 1.21 ou supérieur
- API Auth0 configurée (pas une Application - doit être une ressource API)
- Si Auth0 n'est pas encore configuré, utilisez d'abord le skill
auth0-quickstart
Quand NE PAS l'utiliser
- Applications web Go côté serveur - Utilisez
go-auth0pour les applications web basées sur des sessions - Applications monopage - Utilisez
auth0-react,auth0-vue, ouauth0-angularpour l'authentification côté client - Applications mobiles - Utilisez
auth0-swift,auth0-android, ouauth0-react-native - Backends non-Go - Utilisez
auth0-aspnetcore-apipour .NET,express-jwtpour Node.js
Workflow de Démarrage Rapide
1. Installer le SDK
go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenv
2. Créer une API Auth0
Vous avez besoin d'une API (pas une Application) dans Auth0.
Instruction agent : Si le prompt de l'utilisateur fournit déjà des identifiants Auth0 (domaine et audience), utilisez-les directement — ignorez la question de choix de configuration ci-dessous et allez directement à l'étape 3 pour écrire le fichier
.env.ARRÊTEZ — posez la question à l'utilisateur avant de continuer.
Posez exactement cette question et attendez sa réponse avant de faire quoi que ce soit d'autre :
« Comment souhaitez-vous créer la ressource API Auth0 ?
- Automatisé — J'utiliserai le CLI Auth0 pour créer la ressource API et écrire les valeurs exactes dans votre fichier .env automatiquement.
- Manuel — Vous créez l'API vous-même dans le Dashboard Auth0 (ou via
auth0 apis create) et me fournissez le Domaine et l'Audience.Lequel préférez-vous ? (1 = Automatisé / 2 = Manuel) »
Ne procédez AUCUNE étape de configuration tant que l'utilisateur n'a pas répondu. Ne choisissez PAS par défaut l'option manuelle.
Si l'utilisateur a choisi Automatisé, suivez le Guide de configuration pour la section « Configuration initiale » (étapes 1–6). Le chemin automatisé écrit .env pour vous — ignorez l'étape 3 ci-dessous et passez directement à l'étape 4.
Instruction agent (Points de contrôle chemin automatisé) :
Lors du suivi du chemin automatisé, vous DEVEZ compléter ces points de contrôle dans l'ordre. N'en ignorez AUCUN :
- Vérifier le CLI Auth0 — vérifiez que
auth0est installé.- Vérifier la connexion Auth0 — exécutez
auth0 tenants listpour vérifier l'authentification.- Confirmer le tenant actif — montrez à l'utilisateur quel tenant est actif et posez la question : « Votre tenant Auth0 actif est
<domain>. Est-ce le bon tenant ? » Attendez la confirmation. S'il dit non, demandez-lui d'exécuterauth0 tenants use <tenant>dans son terminal.- Demander le nom et l'identifiant de l'API — utilisez
AskUserQuestion: « Comment souhaitez-vous nommer votre API Auth0, et quel identifiant (audience) devrait-elle utiliser ? Par exemple : Nom : 'My Go API', Identifiant : 'https://my-api.example.com'. L'identifiant est un URI logique qui n'a pas besoin de se résoudre — il identifie simplement votre API de manière unique. » Attendez la réponse. Si l'utilisateur n'est pas sûr, suggérez de dériver l'identifiant du nom du module du projet dans go.mod (par exemple,https://<module-name>).- Demander les scopes — utilisez
AskUserQuestion: « Quels scopes (autorisations) votre API nécessite-t-elle ? Par exemple :read:users,write:users,read:products. Si vous n'êtes pas sûr pour le moment, je peux commencer avec les defaults courants et vous pouvez en ajouter d'autres plus tard. » Attendez la réponse.- Vérifier les API existantes — exécutez
auth0 apis listet vérifiez si une API avec l'identifiant prévu existe déjà. Si c'est le cas, demandez à l'utilisateur s'il souhaite la réutiliser ou en créer une nouvelle avec un identifiant différent.- Créer la ressource API — en utilisant le nom, l'identifiant et les scopes des étapes 4–5.
- Gérer .env — si un fichier
.envexiste déjà, demandez avant de le modifier. Ne lisez JAMAIS le contenu existant du.env(peut contenir des secrets). S'il n'existe pas, écrivez-en un avecAUTH0_DOMAINetAUTH0_AUDIENCE.- Ajouter
.envà.gitignore— s'il n'y est pas déjà.- Passer à l'intégration du code — ignorez l'étape 3 (déjà faite) et allez directement à l'étape 4 pour écrire le code du middleware.
Si l'utilisateur a choisi Manuel, suivez le Guide de configuration (section Configuration manuelle) pour les instructions complètes. Puis continuez avec l'étape 3 ci-dessous.
Référence rapide pour la création d'API manuelle :
# En utilisant le CLI Auth0
auth0 apis create \
--name "My Go API" \
--identifier https://my-api.example.com
Ou créez manuellement dans Auth0 Dashboard → Applications → APIs
3. Configurer .env
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://my-api.example.com
Important : Le domaine ne doit PAS inclure https://. Le middleware construit l'URL de l'émetteur automatiquement.
4. Configurer main.go
Instruction agent (intégration avec code existant) :
Avant d'écrire le code, déterminez si vous faites :
A) Ajouter l'auth à un projet existant — l'utilisateur a déjà un
main.goavec des routes définies. Dans ce cas, ne remplacez PAS son fichier par le modèle ci-dessous. À la place :
- Ajoutez les imports nécessaires (
jwtmiddleware,jwks,validator,godotenv,net/url,os,context,strings).- Ajoutez la struct
CustomClaimset ses méthodes.- Ajoutez le code de configuration du middleware (URL de l'émetteur, fournisseur JWKS, validator, middleware) près du début de
main().- Demandez quels endpoints protéger (voir ci-dessous).
- Enveloppez les handlers spécifiés avec
middleware.CheckJWT().B) Créer un nouveau projet de zéro — utilisez le modèle complet ci-dessous comme point de départ.
ARRÊTEZ — demandez quels endpoints protéger :
Si la demande de l'utilisateur ne spécifie PAS explicitement quels endpoints protéger, posez la question :
« Quels endpoints doivent nécessiter l'authentification ? Par exemple :
- Tous sauf health/public — protégez tout, ne laissez que des routes publiques spécifiques ouvertes
- Routes spécifiques — dites-moi quelles routes ont besoin d'auth
De plus, certains endpoints ont-ils besoin de vérifications de scope/permission spécifiques (par exemple,
write:userspour POST/DELETE), ou un JWT valide suffit-il pour tous ? »Attendez la réponse. Si l'utilisateur dit « tout » ou « tout sauf health », protégez tous les routes sauf
/health(ou ce qu'il spécifie comme public). S'il spécifie les exigences de scope par endpoint, implémentez des vérifications de scope par route en utilisantcustomClaims.HasScope().
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"net/url"
"os"
"strings"
jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
"github.com/auth0/go-jwt-middleware/v3/jwks"
"github.com/auth0/go-jwt-middleware/v3/validator"
"github.com/joho/godotenv"
)
// CustomClaims contient les données personnalisées que nous voulons du token.
type CustomClaims struct {
Scope string `json:"scope"`
Permissions []string `json:"permissions"`
}
func (c CustomClaims) Validate(ctx context.Context) error {
return nil
}
func (c CustomClaims) HasScope(expectedScope string) bool {
for _, scope := range strings.Split(c.Scope, " ") {
if scope == expectedScope {
return true
}
}
return false
}
func main() {
if err := godotenv.Load(); err != nil {
log.Fatalf("Error loading .env file: %v", err)
}
issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
if err != nil {
log.Fatalf("Failed to parse issuer URL: %v", err)
}
provider, err := jwks.NewCachingProvider(
jwks.WithIssuerURL(issuerURL),
)
if err != nil {
log.Fatalf("Failed to set up JWKS provider: %v", err)
}
jwtValidator, err := validator.New(
validator.WithKeyFunc(provider.KeyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer(issuerURL.String()),
validator.WithAudience(os.Getenv("AUTH0_AUDIENCE")),
validator.WithCustomClaims(func() validator.CustomClaims {
return &CustomClaims{}
}),
)
if err != nil {
log.Fatalf("Failed to set up JWT validator: %v", err)
}
middleware, err := jwtmiddleware.New(
jwtmiddleware.WithValidator(jwtValidator),
)
if err != nil {
log.Fatalf("Failed to set up JWT middleware: %v", err)
}
mux := http.NewServeMux()
// Endpoint public - pas d'authentification
mux.HandleFunc("/api/public", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "Hello from a public endpoint!"})
})
// Endpoint protégé - nécessite un JWT valide
mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
if err != nil {
http.Error(w, `{"message":"Failed to get token claims."}`, http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Hello from a private endpoint!",
"userId": claims.RegisteredClaims.Subject,
})
})))
// Endpoint protégé + scope - nécessite JWT avec scope spécifique
mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
if err != nil {
http.Error(w, `{"message":"Failed to get token claims."}`, http.StatusInternalServerError)
return
}
customClaims := claims.CustomClaims.(*CustomClaims)
if !customClaims.HasScope("read:messages") {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(map[string]string{"message": "Insufficient scope."})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "Hello from a scoped endpoint!"})
})))
log.Println("Server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
5. Protéger les Endpoints
Utilisez middleware.CheckJWT() pour envelopper les handlers qui nécessitent l'authentification :
// Endpoint public - pas d'authentification
mux.HandleFunc("/api/public", publicHandler)
// Endpoint protégé - nécessite un JWT valide
mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(privateHandler)))
// Protégé + scope - nécessite JWT avec une permission spécifique
mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(privateScopedHandler)))
6. Tester l'API
Instruction agent : Après avoir écrit le code, vérifiez que la compilation fonctionne :
go build ./...Si la compilation échoue, diagnostiquez l'erreur et corrigez-la. Répétez 5-6 fois.
Vérification d'échec : Si la compilation échoue toujours après 5-6 tentatives de correction, arrêtez et posez la question à l'utilisateur en utilisant
AskUserQuestion: « La compilation échoue toujours après plusieurs tentatives de correction. Comment souhaitez-vous procéder ? »
- Laissez-moi continuer à corriger de manière itérative
- Corrigez-le manuellement — J'affiche les erreurs restantes
- Ignorer la vérification de compilation — Continuez sans compilation réussie
Répétez cette vérification après chaque 5-6 itérations si les erreurs persistent.
Instruction agent : ARRÊTEZ — posez la question après le succès de la compilation.
Une fois que la compilation réussit, posez à l'utilisateur :
« Votre API est configurée et compile correctement. Souhaitez-vous que je vous aide à configurer les tests ?
- Oui — Je vous aiderai à configurer une application M2M pour obtenir des tokens de test.
- Non — J'ai terminé pour le moment.
Lequel préférez-vous ? »
Ne procédez PAS avec la configuration des tests à moins que l'utilisateur dise oui. S'il dit non, résumez ce qui a été fait et arrêtez.
Instruction agent (Configuration de l'application M2M — uniquement si l'utilisateur veut des tests) :
Si l'utilisateur a choisi de configurer les tests, posez la question :
« Pour tester vos endpoints protégés, vous avez besoin d'une application Machine-to-Machine (M2M) autorisée à demander des tokens pour cette API.
- Créer nouveau — Je vais créer une nouvelle application M2M et l'autoriser pour cette API.
- Utiliser un existant — Vous avez déjà une application M2M. Fournissez le Client ID et je l'autoriserai pour cette API.
Lequel préférez-vous ? (1 = Créer nouveau / 2 = Utiliser un existant) »
Ne procédez PAS jusqu'à ce que l'utilisateur réponde. Ne choisissez PAS silencieusement une application existante du tenant.
Si l'utilisateur a choisi « Créer nouveau » :
auth0 apps create \ --name "<PROJECT_NAME> (Test App)" \ --type m2m \ --no-input --jsonAnalysez le JSON pour extraire
client_id. N'utilisez PAS--reveal-secrets— ne jamais exposer les secrets client dans le contexte agent. Puis créez une subvention client :auth0 api post "client-grants" --data '{ "client_id": "<CLIENT_ID>", "audience": "<API_IDENTIFIER>", "scope": ["<SCOPES>"] }'Si l'utilisateur a choisi « Utiliser un existant » : Demandez le Client ID. Puis créez une subvention client pour l'autoriser pour cette API :
auth0 api post "client-grants" --data '{ "client_id": "<USER_PROVIDED_CLIENT_ID>", "audience": "<API_IDENTIFIER>", "scope": ["<SCOPES>"] }'Si la subvention existe déjà (409 conflit), c'est bien — l'application est déjà autorisée.
Instruction agent (ISOLATION DU TOKEN — CRITIQUE) :
L'agent ne DOIT JAMAIS voir ou afficher directement les valeurs des tokens d'accès. Règles de sécurité des tokens :
- N'exécutez PAS
auth0 test tokenseul — il affiche le token sur stdout- N'exécutez PAS les commandes
curlvers le endpoint/oauth/tokenseules- Ne demandez PAS à l'utilisateur de coller son token dans la conversation
- N'affichez, n'imprimez ou ne loggez PAS la valeur du token
- Ne stockez PAS le token dans un fichier
Approche de test sécurisée (chaîne de commande unique) :
Si l'utilisateur demande explicitement de tester les endpoints protégés, l'agent PEUT utiliser une chaîne de commande unique qui capture le token dans une variable shell et l'utilise immédiatement — la valeur du token n'est jamais imprimée ou visible à l'agent :
TEST_TOKEN=$(auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \ [ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \ curl -s http://localhost:8080/<ENDPOINT> -H "Authorization: Bearer $TEST_TOKEN"Garanties de sécurité de cette approche :
$(...)capture stdout — le token est consommé dans la variable, non affichégrep -oextrait uniquement le modèle JWT (ey...) — aucune sortie environnante ne s'échappeecho "Token acquired (${#TEST_TOKEN} chars)"confirme le succès en imprimant uniquement la LONGUEUR, jamais la valeur- La variable shell
$TEST_TOKENexiste uniquement pendant cette chaîne de commande unique — elle meurt immédiatement après- L'agent voit uniquement :
"Token acquired (834 chars)"+ le corps de la réponse API (JSON)- Aucun fichier n'est écrit, aucune env n'est exportée, rien ne persiste
Règles pour utiliser ce modèle :
- Utilisez UNIQUEMENT quand l'utilisateur demande explicitement de tester (par exemple, « testez-le », « exécutez les tests », « vérifiez que les endpoints fonctionnent »)
- Chaînez toujours l'acquisition du token + curl dans une SEULE commande
&&— ne les séparez jamais en deux appels Bash- Pour tester plusieurs endpoints, chaînez plusieurs curls dans la même commande :
TEST_TOKEN=$(auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \ [ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \ echo "=== GET /users ===" && \ curl -s http://localhost:8080/users -H "Authorization: Bearer $TEST_TOKEN" && \ echo "" && echo "=== POST /users ===" && \ curl -s -X POST http://localhost:8080/users -H "Authorization: Bearer $TEST_TOKEN" -d '{"id":"99","name":"Test","email":"test@example.com"}' && \ echo "" && echo "=== GET /products ===" && \ curl -s http://localhost:8080/products -H "Authorization: Bearer $TEST_TOKEN"- N'ajoutez JAMAIS
echo $TEST_TOKEN,printf $TEST_TOKEN, ou une commande qui afficherait la valeur du token brut- Si l'acquisition du token échoue (variable vide), la vérification
[ -n "$TEST_TOKEN" ]arrêtera la chaîne — signalez à l'utilisateur que l'application M2M peut ne pas être autorisée- Client ID est REQUIS — la commande
auth0 test tokennécessite un Client ID à passer comme premier argument. Ceci DOIT être leclient_idobtenu de l'étape de configuration de l'application M2M (créer nouveau ou utiliser existant). Si l'étape M2M n'a pas été complétée (pas de Client ID disponible), ne tentez PAS d'exécuter la commande test token. À la place, posez la question à l'utilisateur : « J'ai besoin d'un Client ID d'application M2M pour obtenir un token de test. Souhaitez-vous que j'en crée un ou avez-vous déjà un existant ? » — puis complétez d'abord la configuration M2M.Si l'utilisateur NE demande PAS de tester, fournissez simplement les commandes à exécuter manuellement :
auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> curl http://localhost:8080/<endpoint> -H "Authorization: Bearer <PASTE_TOKEN_HERE>"
Après la configuration M2M :
- Démarrez le serveur avec
go run .en arrière-plan - Vérifiez que les endpoints publics retournent 200 et les endpoints protégés retournent 401 (aucun token nécessaire)
- Si l'utilisateur a demandé de tester : utilisez la chaîne de commande unique sécurisée ci-dessus pour les demandes authentifiées
- Si l'utilisateur N'a PAS demandé de tester : fournissez les commandes manuelles et dites-lui d'exécuter dans son terminal
Testez l'endpoint public :
curl http://localhost:8080/api/public
Testez l'endpoint protégé sans token (doit retourner 401) :
curl http://localhost:8080/api/private
Testez l'endpoint protégé avec token (chaîne de commande unique sécurisée) :
TEST_TOKEN=$(auth0 test token <M2M_CLIENT_ID> --audience https://my-api.example.com --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \
[ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \
curl -s http://localhost:8080/api/private -H "Authorization: Bearer $TEST_TOKEN"
Erreurs Courantes
| Erreur | Solution |
|---|---|
| Créé une Application au lieu d'une API dans Auth0 | Doit créer une ressource API dans Auth0 Dashboard → Applications → APIs |
| L'Audience ne correspond pas à l'Identifiant API | Doit correspondre exactement à l'Identifiant API défini dans Auth0 Dashboard |
Le Domaine inclut https:// |
Utilisez uniquement le format your-tenant.auth0.com - l'URL de l'émetteur est construite automatiquement |
| Utiliser les paramètres positionnels v2 à la place des options v3 | v3 utilise validator.WithKeyFunc(), validator.WithAlgorithm() etc. |
| Slash finale manquante sur l'URL de l'émetteur | L'émetteur doit être https://domain/ avec slash finale |
Vérifier la claim scope au lieu de permissions pour RBAC |
Utilisez la struct de claims personnalisés avec le champ Permissions []string |
Appel godotenv.Load() manquant |
Ajoutez github.com/joho/godotenv et appelez godotenv.Load() avant de lire les variables d'env |
Utiliser ContextKey{} pour accéder aux claims (modèle v2) |
Utilisez plutôt les génériques type-safe jwtmiddleware.GetClaims[T]() |
Autorisation Basée sur les Scopes
Voir le Guide d'intégration pour définir et appliquer les politiques de scope et d'autorisation.
Configuration CORS
Pour les APIs appelées depuis des SPAs basées sur navigateur, configurez CORS avant tout middleware d'auth :
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
Appliquez-le comme le handler le plus externe enveloppant votre mux :
handler := corsMiddleware(mux)
log.Fatal(http.ListenAndServe(":8080", handler))
Voir le Guide d'intégration pour les modèles CORS détaillés.
Support DPoP
Liaison de token proof-of-possession intégrée selon RFC 9449. Voir le Guide d'intégration pour la configuration.
Skills Connexes
auth0-quickstart- Configuration basique Auth0auth0-mfa- Ajouter l'authentification multifacteur
Référence Rapide
Options de configuration :
validator.WithKeyFunc(provider.KeyFunc)- Fonction clé JWKS pour la vérification de signature (requis)validator.WithAlgorithm(validator.RS256)- Algorithme de signature attendu (requis)validator.WithIssuer(url)- URL de l'émetteur du token avec slash finale (requis)validator.WithAudience(aud)- Identifiant API à partir des paramètres API Auth0 (requis)validator.WithCustomClaims(fn)- Factory pour la struct de claims personnalisésvalidator.WithAllowedClockSkew(d)- Tolérance de décalage d'horloge
Accès aux Claims :
jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())- Récupération type-safe des claimsclaims.RegisteredClaims.Subject- ID utilisateur (sub)claims.CustomClaims.(*CustomClaims).Scope- Scopes séparés par espaceclaims.CustomClaims.(*CustomClaims).Permissions- Chaînes de permission
Cas d'usage courants :
- Protéger les routes →
middleware.CheckJWT(handler)(voir étape 5) - Application des permissions → Guide d'intégration
- Liaison de token DPoP → Guide d'intégration
- Adaptateurs de framework (Gin, Echo) → Guide d'intégration
- Configuration JWT avancée → Référence API
Documentation Détaillée
- Guide de configuration - Configuration du CLI Auth0, configuration d'environnement
- Guide d'intégration - Politiques de scope, DPoP, adaptateurs de framework, gestion d'erreurs
- Référence API - Options de configuration complètes et référence validator/middleware