Authentification et routage basé sur le contenu avec les JWT et NGINX Plus

NGINX | 01 mars 2018

Version 10 de NGINX Plus a introduit la prise en charge de la délégation de l’authentification des services web et API à l’aide de jetons web JSON (JWT, prononcés « jots »). Depuis la sortie de R10, nous avons continué à enrichir les fonctionnalités à chaque nouvelle version.

À partir de la version NGINX Plus R14, NGINX Plus prend en charge les JWT contenant des revendications imbriquées et des données sous forme de tableaux. Lorsqu’il est utilisé comme passerelle d’API, NGINX Plus peut se servir des JWT pour authentifier les clients qui demandent à se connecter à des services backend et à des points de terminaison d’API.

On m’a parfois demandé de fournir une configuration de base utilisant NGINX Plus pour authentifier les JWT, puis prendre des décisions plus avancées en matière de répartition de charge en fonction des informations qu’ils contiennent. La solution la plus simple consiste à autoriser l’accès à un service si l’authentification réussit, et à bloquer ou rediriger la connexion dans le cas contraire.

Le guide présenté dans cet article constitue une preuve de concept complète de l’authentification JWT et du routage basé sur le contenu avec NGINX Plus. Afin de couvrir le plus large éventail de possibilités et de limiter les connaissances ou l’expérience préalables requises en matière de JWT, j’ai créé un guide d’initiation « JWT 101 » qui vous permet de déployer cette solution (avec des exemples et des explications contextuelles) sans aucune connaissance préalable des JWT.

Si vous connaissez déjà les JWT ou si votre environnement en utilise déjà, vous pouvez ignorer les deux premières sections et adapter les extraits de configuration NGINX Plus fournis à votre environnement afin de commencer à prendre des décisions avancées en matière d’équilibrage de charge en fonction des données contenues dans les revendications de vos JWT.

Prérequis

Ce document suppose une nouvelle installation de NGINX Plus, avec les fichiers de configuration par défaut aux emplacements suivants :

  • /etc/nginx/nginx.conf
  • /etc/nginx/conf.d/default.conf

Pour plus d’informations sur l’installation et la prise en main de NGINX Plus, consultez le Guide d’administration de NGINX Plus.

Toutes les commandes CLI supposent des privilèges root ; les utilisateurs non-root doivent donc disposer des autorisations sudo dans leur environnement.

Pour plus d’informations générales et pour découvrir d’autres cas d’utilisation des JWT avec NGINX Plus, consultez les liens suivants :

Création d’un JWT et de la clé de signature associée

Les instructions ci-dessous vous guident dans la création de A à Z d’un JWT contenant des données de charge utile propres à notre exemple, afin d’illustrer comment configurer NGINX Plus pour un traitement de base des revendications JWT. Si vous utilisez un JWT existant plutôt que celui fourni en exemple, vous devez vous assurer que votre fichier « secrets » contient la chaîne encodée en Base64URL correspondant à la clé de signature utilisée pour créer le JWT. Vous devrez probablement aussi modifier les revendications dans la charge utile du JWT.

Remarque : Quelle que soit la méthode utilisée pour générer votre JWT, vous devez utiliser l’encodage Base64URL, qui gère correctement le remplissage (effectué à l’aide du caractère =), ainsi que les autres caractères non conformes au protocole HTTP généralement utilisés dans l’encodage Base64. De nombreux outils s’en chargent automatiquement, contrairement aux encodeurs Base64 manuels en ligne de commande et à certains outils de création de JWT. Pour en savoir plus sur l’encodage Base64URL, consultez base64url encoding de Brock Allen et la RFC 4648.

Pour cet exemple, nous utilisons l’interface graphique de jwt.io (qui effectue correctement l’encodage Base64URL) pour créer un JWT HS256 symétrique. La capture d’écran suivante montre l’apparence de l’interface graphique une fois que vous avez saisi les valeurs indiquées dans les instructions ci-dessous et que la signature a été vérifiée.

Dans l’interface graphique de jwt.io, générez un JWT HS256 en vérifiant ou en saisissant les valeurs indiquées dans les champs de la colonne « Décodé » située à droite :

  1. Vérifiez que la valeur par défaut suivante apparaît dans le champ « HEADER », et modifiez son contenu si nécessaire pour qu’il corresponde :
{  "alg": "HS256",
  "typ": "JWT"
}
  1. Dans le champ « VERIFY SIGNATURE », remplacez la valeur indiquée dans la zone (par défaut, secret) par nginx123. (Effectuez cette modification avant de saisir des données dans le champ « PAYLOAD » afin d’éviter un problème qui se produit si vous inversez l’ordre de ces deux étapes.)
  2. Remplacez le contenu du champ « PAYLOAD » par ce qui suit :
{  "exp": 1545091200,
  "name": "Create New User",
  "sub": "cuser",
  "gname": "wheel",
  "guid": "10",
  "fullName": "John Doe",
  "uname": "jdoe",
  "uid": "222",
  "sudo": true,
  "dept": "IT",
  "url": "http://secure.example.com"
}
  1. Remarque : La revendication exp définit la date et l’heure d’expiration du JWT sous la forme d’un horodatage UNIX (le nombre de secondes écoulées depuis le 1er janvier 1970 à minuit UTC). La valeur donnée en exemple correspond au 18 décembre 2018 à minuit UTC. Pour modifier la date d’expiration, changez l’horodatage UNIX.
  2. Vérifiez que la barre située sous les champs est bleue et affiche « Signature vérifiée ».
  3. Copiez la valeur de la colonne Encodé située à gauche dans un fichier ou une mémoire tampon. Il s’agit du texte intégral du JWT que l’utilisateur jdoe doit présenter pour accéder à http://secure.example.com, et nous l’utiliserons dans nos tests ci-dessous. Pour faciliter l’affichage, le JWT est présenté ici avec des sauts de ligne, mais il doit être transmis à NGINX Plus sous la forme d’une chaîne sur une seule ligne.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOi
    JDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkI
    joiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIy
    Iiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGx
    lLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Sur l’hôte NGINX Plus, suivez ces étapes pour créer le fichier de clé que NGINX Plus utilise afin de vérifier les JWT signés avec « nginx123 » :

  1. Exécutez cette commande pour générer la chaîne encodée en Base64URL correspondant à la chaîne de signature. (Les commandes tr effectuent les substitutions de caractères requises pour l’encodage Base64URL.)
# echo -n nginx123 | base64 | tr '+/' '-_' | tr -d '='bmdpbngxMjM
  1. Dans le répertoire /etc/nginx/ créez le fichier de clé nommé api_secret.jwk que NGINX Plus utilisera pour vérifier les signatures JWT. Insérez le contenu suivant. La valeur du champ k correspond à la forme encodée en Base64URL de nginx123, que nous avons générée à l’étape précédente.
{"keys":    [{
        "k":"bmdpbngxMjM",
        "kty":"oct"
    }]
}

Configuration de NGINX Plus pour gérer les JWT

Les instructions de cette section permettent de configurer NGINX Plus afin de valider le JWT inclus dans une requête et de présenter une ressource protégée si le client est autorisé (au lieu de la page par défaut affichée aux clients non autorisés). Nous définissons également un nouveau format de journalisation qui consigne les informations relatives aux JWT.

Configuration de la validation des JWT et du routage basé sur le contenu

Dans ces instructions, nous suivons la bonne pratique standard qui consiste à renommer le fichier de configuration default.conf afin que NGINX Plus ne le lise pas, puis à créer une nouvelle configuration spécialement destinée aux tests. Vous pourrez ainsi restaurer facilement la configuration par défaut une fois les tests terminés ou en cas de problème pendant ceux-ci.

  1. Renommez default.conf :
# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
  1. Créez un nouveau fichier de configuration nommé jwt-test.conf dans /etc/nginx/conf.d/, avec le contenu suivant. Celui-ci configure la journalisation propre aux JWT, la validation des JWT et le routage basé sur le contenu (une analyse complète est présentée après l’extrait de code).
server {
    listen       80;
    access_log  /var/log/nginx/host.access.log jwt;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;

        # JWT validation
        auth_jwt "JWT Test Realm" token=$arg_myjwt;
        auth_jwt_key_file /etc/nginx/api_secret.jwk;
        error_log /var/log/nginx/host.jwt.error.log debug;

        if ( $jwt_claim_uid = 222 ) {
            add_header X-jwt-claim-uid "$jwt_claim_uid" always;
            add_header X-jwt-status "Redirect to $jwt_claim_url" always;
            return 301 $jwt_claim_url;
        }

        if ( $jwt_claim_uid != 222 ) {
            add_header X-jwt-claim-uid "$jwt_claim_uid" always;
            add_header X-jwt-status "Invalid user, no redirect" always;
        }
    }
}

Les directives du bloc location indiquent à NGINX Plus comment traiter les requêtes HTTP qui contiennent un JWT. (Pour plus d’informations sur la configuration de la journalisation définie par la directive access_log, consultez la section suivante.) NGINX Plus effectue les étapes suivantes :

  1. Extrait le JWT de l’argument « myjwt » dans la chaîne de requête (comme indiqué par l’argument « token » de la directive auth_jwt).
  2. Décode le JWT à l’aide de la clé de signature spécifiée par la directive auth_jwt_key-file (ici, api_secret.jwk). Il traite la charge utile comme suit (ces actions sont inhérentes au traitement des JWT et n’ont pas de directives NGINX Plus correspondantes) :
    • Vérifie que le JWT n’a pas expiré, c’est-à-dire que la date d’expiration indiquée par la revendication « exp » dans la charge utile n’est pas dépassée.
    • Crée une paire clé-valeur pour chaque revendication de la charge utile. Le nom de la clé est une variable de la forme « $jwt_claim_claim-name » (par exemple, « $jwt_claim_uid » pour la revendication « uid »).
  3. Consigne toutes les erreurs dans /var/log/nginx/host.jwt.error.log au niveau debug.
  4. Vérifie si la valeur de $jwt_claim_uid est 222 (comme indiqué par les deux directives if) et envoie la réponse appropriée au client. C’est ainsi que les informations contenues dans le JWT sont utilisées pour effectuer un routage basé sur le contenu.Remarque : l’utilisation de la directive if pour évaluer des variables n’est généralement pas considérée comme une bonne pratique, et nous recommandons habituellement d’utiliser plutôt la directive map. Toutefois, dans le cadre de cet exemple simple, la directive if fonctionne comme prévu.
    • Si la valeur est 222, NGINX Plus envoie une réponse qui redirige le client (à l’aide de la directive return) vers l’URL indiquée dans la revendication url du JWT. À des fins de débogage, il ajoute deux en-têtes à la réponse (à l’aide des directives add_header) : le premier contient la valeur de la revendication uid et le second indique que le client a été redirigé.
    • Si la valeur n’est pas égale à « 222 », NGINX Plus affiche la page d’index par défaut (telle que définie par les directives root et index dans le même bloc location). Là encore, à des fins de débogage, il ajoute des en-têtes qui enregistrent la valeur de la revendication uid et indiquent que le client n’a pas pu accéder à l’URL spécifiée dans le JWT.

En pratique, la configuration permet uniquement aux utilisateurs autorisés d’accéder aux ressources protégées. Autrement dit, les utilisateurs disposant d’un JWT valide peuvent accéder à l’URL spécifiée dans le JWT, tandis que ceux qui ne disposent pas d’un JWT valide accèdent à une page par défaut.

Journalisation des données JWT

Nous terminons la configuration de la gestion des JWT pour le routage basé sur le contenu en définissant un format de journalisation appelé « jwt », auquel fait référence la directive « access_log » dans jwt-test.conf. Ce format consigne les données JWT dans le journal d’accès.

  1. Ajoutez la directive log_format suivante à /etc/nginx/nginx.conf :
log_format jwt '$remote_addr - $remote_user [$time_local] "$request" '               '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
               '$jwt_header_alg $jwt_claim_uid $jwt_claim_url';
  1. Ce format inclut les deux revendications JWT utilisées dans ce guide (uid et url), mais vous pouvez consigner les données de n’importe quelle revendication JWT à l’aide du nom de variable correspondant à la revendication, sous la forme $jwt_claim_claim‑name.
  2. Enregistrez nginx.conf, puis exécutez la commande suivante pour vérifier la validité syntaxique de la configuration complète (y compris le nouveau fichier jwt-test.conf). Corrigez toutes les erreurs signalées.
# nginx -t
  1. Rechargez NGINX Plus.
# nginx -s reload

Tester la configuration

À l’aide d’un navigateur ou d’un outil en ligne de commande tel que « curl », nous pouvons vérifier que NGINX Plus valide correctement le JWT, authentifie le client qui le présente et effectue un routage basé sur le contenu. (Pour tester uniquement l’authentification et la validation, sans le routage basé sur le contenu, mettez en commentaire les deux blocs « if » dans jwt-test.conf.)

Pour effectuer le test, nous incluons l’argument « myjwt » dans l’URL de la requête, en fournissant le texte intégral du JWT dans lequel la revendication « url » a pour valeur « 222 ». Là encore, nous avons ajouté des sauts de ligne pour faciliter l’affichage.

http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5le
GFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Voici la commande curl correspondante (sans sauts de ligne, afin que vous puissiez la copier-coller si vous le souhaitez) :

# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Comme la valeur de la revendication « uid » dans le JWT est 222, nous nous attendons à ce que NGINX Plus affiche le contenu de la page à accès restreint, http://secure.example.com.

Nous effectuons maintenant un test pour vérifier que lorsque la revendication url du JWT n’est pas égale à 222, NGINX Plus n’affiche pas le contenu de la page à accès restreint, mais présente à la place la page index.html du serveur local, dans ce cas http://example.com/index.html.

Nous commençons par générer un autre JWT sur jwt.io avec une revendication uid différente de 222 ; pour les besoins de l’exemple, nous lui attribuons la valeur 111. Voici l’URL de la requête avec ce JWT :

http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5l
eGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg

La commande « curl » est la suivante :

# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg

Dans ce cas, nous nous attendons à ce que NGINX Plus serve « http://example.com/index.html ».

Dans les deux conditions de test, vous pouvez utiliser un outil d’inspection des en-têtes (tel que curl ou les outils de développement fournis avec certains navigateurs) pour vérifier que les nouveaux en-têtes X-jwt-claim-uid et X-jwt-status ont été ajoutés à la réponse.

Si vous rencontrez des problèmes pendant les tests, consultez les journaux d’accès et d’erreurs dans /var/log/nginx/host.jwt*. Le journal d’erreurs permet notamment d’identifier les problèmes liés à la vérification, à l’accès à votre fichier de vérification, etc.

Transmission du JWT dans un cookie

Dans notre exemple de base, NGINX Plus extrait le JWT de l’argument myjwt dans l’URL de la requête. NGINX Plus permet également de transmettre le JWT dans un cookie (pour plus de détails, consultez la documentation de référence NGINX JWT). Dans jwt-test.conf, modifiez la directive auth_jwt afin que le premier élément du paramètre token soit $cookie au lieu de $arg :

auth_jwt "JWT Test Realm" token=$cookie_myjwt;

Pour fournir le JWT dans un cookie nommé « myjwt », la commande curl appropriée est :

# curl -v --cookie myjwt=JWT-text example.com/index.html

Testez par vous-même le routage basé sur le contenu avec des JWT : commencez dès aujourd’hui votre essai gratuit de 30 jours de NGINX ou contactez-nous pour discuter de vos cas d’utilisation.


Partager

À propos de l’auteur

Articles de blog associés

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA
NGINX | 13/10/2025

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA

Découvrez les principaux enseignements tirés des réponses à l’enquête annuelle F5 NGINX sur les dernières tendances en matière de technologies cloud natives, de sécurité, d’ingénierie de plateforme et d’IA.

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?
NGINX | 03/11/2023

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?

Lorsque de nouvelles CVE sont signalées, il est facile de ne plus savoir si elles concernent un outil de contrôleur Ingress NGINX particulier, car il existe plusieurs projets. Cet article présente les CVE et explique comment distinguer les différents projets de contrôleurs Ingress afin de dissiper toute confusion.

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX
NGINX | 11/03/2020

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX

Votre API répond-elle aux requêtes en moins de 30 ms ? Si ce n’est pas le cas, il ne s’agit pas d’une API en temps réel offrant la faible latence exigée par la grande majorité des organisations. Mesurez la latence de votre API avec l’outil rtapi de NGINX et découvrez comment NGINX peut l’aider à offrir une réactivité en temps réel.

Un testeur d’expressions régulières pour NGINX et NGINX Plus
NGINX | 11/06/2019

Un testeur d’expressions régulières pour NGINX et NGINX Plus

Le testeur d’expressions régulières (regex) pour NGINX et NGINX Plus vous évite de tâtonner en vous indiquant si une expression régulière utilisée dans un bloc location ou map correspond aux valeurs comme prévu. Il génère la configuration appropriée et affiche les valeurs des groupes de capture.

Caches partagés avec les clusters de cache NGINX Plus, partie 1
NGINX | 24/01/2017

Caches partagés avec les clusters de cache NGINX Plus, partie 1

Optimisez la capacité de votre cache web en le répartissant entre plusieurs serveurs de cache web NGINX ou NGINX Plus. Découvrez comment procéder dans cet article.

Introduction aux microservices
NGINX | 19/05/2015

Introduction aux microservices

Les microservices suscitent actuellement beaucoup d’intérêt. Cet article de blog est le premier d’une série de sept consacrée à la conception, au développement et au déploiement de microservices.