jwt-verification: Bibliothèque de vérification JWT pour nginx-module-lua avec intégration JWKS
Installation
Si vous n'avez pas configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.
CentOS/RHEL 7 ou Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-jwt-verification
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-jwt-verification
Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.
Ce document décrit lua-resty-jwt-verification v0.7.0 publié le 30 octobre 2025.
Bibliothèque de vérification JWT pour OpenResty.
Description
Bibliothèque de vérification JWT pour OpenResty.
L'objectif du projet est d'être un remplacement moderne et plus léger pour lua-resty-jwt avec un support intégré pour JWKS.
Ce projet ne fournit pas de fonctionnalités de manipulation ou de création de JWT : vous pouvez uniquement vérifier/déchiffrer les jetons.
Objectifs non visés par la bibliothèque
- Création/modification de JWT
- Fonctionnalité complète pour l'exhaustivité des RFC.
- Les fonctionnalités RFC sans sens et non sécurisées (par exemple, alg none) ne seront pas mises en œuvre.
Différences par rapport à lua-resty-jwt
Les principales différences sont : - Pas de manipulation de JWT de quelque nature que ce soit (vous ne pouvez que les déchiffrer/vérifier). - Structure interne plus simple reposant sur des versions plus récentes de lua-resty-openssl et OpenSSL. - Prend en charge différents algorithmes JWE (voir les tableaux ci-dessus). - Vérification automatique des JWT donnée un point de terminaison HTTP JWKS.
Si l'un des points ci-dessus pose problème, ou si vous avez besoin de compatibilité avec des versions plus anciennes d'OpenResty, je recommande de rester avec lua-resty-jwt.
Types
Les types et les vérifications nulles sont fournis avec une utilisation extensive des annotations EmmyLua.
Intégrations de plugins IDE pour EmmyLua : - Idea - VSCode
Le fichier ngx.d.lua à la racine du projet fournit quelques stubs ngx.
Fonctionnalités prises en charge
- Vérification JWS : avec des clés symétriques ou asymétriques.
- Déchiffrement JWE : avec des clés symétriques ou asymétriques.
- Formats de clés asymétriques pris en charge :
- PEM
- DER
- JWK
- Validation des revendications JWT.
- Récupération automatique de JWKS et validation de JWT.
- stratégies de mise en cache optionnelles.
- JWT imbriqués (JWS dans JWE)
Vérification JWS
| Revendications | Implémenté |
|---|---|
| alg | |
| jku | |
| jwk | |
| kid | |
| x5u | |
| x5c | |
| x5t | |
| x5t#S256 | |
| typ | |
| cty | |
| crit |
| alg | Implémenté | Exigences de mise en œuvre JOSE | Exigences |
|---|---|---|---|
| HS256 | Requis | ||
| HS384 | Optionnel | ||
| HS512 | Optionnel | ||
| RS256 | Recommandé | ||
| RS384 | Optionnel | ||
| RS512 | Optionnel | ||
| ES256 | Recommandé+ | ||
| ES384 | Optionnel | ||
| ES512 | Optionnel | ||
| PS256 | Optionnel | ||
| PS384 | Optionnel | ||
| PS512 | Optionnel | ||
| none | Optionnel | ||
| EdDSA | Obsolète | ||
| ES256K | Optionnel | ||
| Ed25519 | Optionnel | *OpenSSL 3.0+ | |
| Ed448 | Optionnel |
Déchiffrement JWE
| Revendications | Implémenté |
|---|---|
| alg | |
| enc | |
| zip | |
| jku | |
| jwk | |
| kid | |
| x5u | |
| x5c | |
| x5t | |
| x5t#S256 | |
| typ | |
| cty | |
| crit |
| kty | Implémenté | Exigences de mise en œuvre JOSE |
|---|---|---|
| EC | Recommandé+ | |
| RSA | Requis | |
| oct | Requis | |
| OKP | Optionnel |
| alg | Implémenté | Exigences de mise en œuvre JOSE | Exigences |
|---|---|---|---|
| RSA1_5 | Recommandé- | ||
| RSA-OAEP | Recommandé+ | ||
| RSA-OAEP-256 | Optionnel | ||
| RSA-OAEP-384 | Optionnel | ||
| RSA-OAEP-512 | Optionnel | ||
| A128KW | Recommandé | *OpenSSL 3.0+ | |
| A192KW | Optionnel | *OpenSSL 3.0+ | |
| A256KW | Recommandé | *OpenSSL 3.0+ | |
| dir | Recommandé | ||
| ECDH-ES | Recommandé+ | ||
| ECDH-ES+A128KW | Recommandé | *OpenSSL 3.0+ | |
| ECDH-ES+A192KW | Optionnel | *OpenSSL 3.0+ | |
| ECDH-ES+A256KW | Recommandé | *OpenSSL 3.0+ | |
| A128GCMKW | Optionnel | ||
| A192GCMKW | Optionnel | ||
| A256GCMKW | Optionnel | ||
| PBES2-HS256+A128KW | Optionnel | ||
| PBES2-HS384+A192KW | Optionnel | ||
| PBES2-HS512+A256KW | Optionnel |
*La première version officielle d'OpenResty incluant OpenSSL 3.0+ est OpenResty 1.27.1.1 qui a été livrée avec OpenSSL 3.0.15 (Oui, la terriblement lente série OpenSSL 3.0...).
Donc, s'il vous plaît, optez pour OpenResty 1.27.1.2 comme minimum, qui a été livré avec OpenSSL 3.4.1.
| enc | Implémenté | Exigences de mise en œuvre JOSE |
|---|---|---|
| A128CBC-HS256 | Requis | |
| A192CBC-HS384 | Optionnel | |
| A256CBC-HS512 | Requis | |
| A128GCM | Recommandé | |
| A192GCM | Optionnel | |
| A256GCM | Recommandé |
Stratégies de mise en cache de récupération JWKS
| Stratégie de cache | Implémenté |
|---|---|
| pas de cache | |
| local (shared_dict) |
Utilisation de la vérification JWT
jwt.decode_header_unsafe
syntax: header, err = jwt.decode_header_unsafe(token)
Lire un en-tête jwt et le convertir en table lua.
Important : cette méthode ne valide pas la signature JWT ! Utilisez-la uniquement si vous avez besoin d'inspecter l'en-tête du jeton sans avoir à effectuer la validation complète.
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NDkwNzJ9._MwFdsBPSyci9iARpoAaulReGcn1q7mKiPZjR2JDvdY"
local header, err = jwt.decode_header_unsafe(token)
if not header then
return nil, "jwt malformé : " .. err
end
print("alg: " .. header.alg) -- alg: HS256
jwt.verify
syntax: decoded_token, err = jwt.verify(token, secret, options?)
Valider un jeton JWS et le convertir en table lua.
Le paramètre optionnel options peut être passé pour configurer le validateur de jetons. Les champs valides sont :
- valid_signing_algorithms (dictalg autorisées utilisées pour valider le JWT.
- typ (string | nil) : si non nul, assurez-vous que la revendication JWT typ correspond à la valeur passée.
- issuer (string | nil) : si non nul, assurez-vous que la revendication JWT iss correspond à la valeur passée.
- audiences (string | table | nil) : si non nul, assurez-vous que la revendication JWT aud correspond à l'une des valeurs fournies.
- subject (string | nil) : si non nul, assurez-vous que la revendication JWT sub correspond à la valeur passée.
- jwtid (string | nil) : si non nul, assurez-vous que la revendication JWT jti correspond à la valeur passée.
- ignore_not_before (bool) : Si vrai, la revendication JWT nbf sera ignorée.
- ignore_expiration (bool) : Si vrai, la revendication JWT exp sera ignorée.
- current_unix_timestamp (datetime | nil) : les revendications JWT nbf et exp seront validées par rapport à cet horodatage. Si nul, utilisera la date et l'heure actuelles fournies par ngx.time().
- timestamp_skew_seconds (int) : Combien de secondes de marge la bibliothèque peut-elle utiliser pour vérifier l'expiration du jeton par rapport à l'heure actuelle. Utile lorsque les horloges ne sont pas toujours exactement synchronisées. Définir cette valeur trop élevée peut poser des problèmes de sécurité.
Valeurs par défaut pour les champs options :
local verify_default_options = {
valid_signing_algorithms = {
["HS256"]="HS256", ["HS384"]="HS384", ["HS512"]="HS512",
["RS256"]="RS256", ["RS384"]="RS384", ["RS512"]="RS512",
["ES256"]="ES256", ["ES384"]="ES384", ["ES512"]="ES512",
["PS256"]="PS256", ["PS384"]="PS384", ["PS512"]="PS512",
["ES256K"]="ES256K", ["Ed25519"]="Ed25519", ["Ed448"]="Ed448",
},
typ = nil,
issuer = nil,
audiences = nil,
subject = nil,
jwtid = nil,
ignore_not_before = false,
ignore_expiration = false,
current_unix_timestamp = nil,
timestamp_skew_seconds = 1,
}
Exemple minimal avec des clés symétriques :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0"
local decoded_token, err = jwt.verify(token, "superSecretKey")
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar
Exemple minimal avec des clés asymétriques :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2Njg2Mzd9.H6PE-zLizMMqefx8DG4X5glVjyxR9UNT225Tq2yufHhu4k9K0IGttpykjMCG8Ck_4Qt2ezEWIgoiWhSn1rv_zwxe7Pv-B09fDs7h1hbASi5MZ0YVAmK9ID1RCKM_NTBEnPLot_iopKZRj2_J5F7lvXwJDZSzEAFJZdrgjKeBS4saDZAv7SIL9Nk75rdhgY-RgRwsjmTYSksj7eioRJJLHifrMnlQDbdrBD5_Qk5tD6VPcssO-vIVBUAYrYYTa7M7A_v47UH84zDtzNYBbk9NrDbyq5-tYs0lZwNhIX8t-0VAxjuCyrrGZvv8_O01pdi90kQmntFIbaiDiD-1WlGcGA"
local decoded_token, err = jwt.verify(token, "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXFhNyhFWuWtFSJqfOAw\np42lLIn9kB9oaciiKgNAYZ8SYw5t9Fo+Zh7IciVijn+cVS2/aoBNg2HhfdYgfpQ/\nsb6jwbRqFMln2GmG+X2aJ2wXMJ/QfxrPFdO9L36bAEwkubUTYXwgMSm1KqWRN8xX\n+oBu+dbyzw7iUbrmw0ybzXKZLJvetCvmt0reU5TvdwoczOWFBSKeYnzBrC6hISD8\n8TYDJ4tiw1EWVOupQGqgel0KjC7iwdIYi7PROn6/1MMnF48zlBbT/7/zORj84Z/y\nDnmxZu1MQ07kHqXDRYumSfCerg5Xw5vde7Tz8O0TWtaYV3HJXNa0VpN5OI3L4y7P\nhwIDAQAB\n-----END PUBLIC KEY-----")
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- RS256
print(decoded_token.payload.foo) -- bar
Exemples avec des options personnalisées :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0"
local decoded_token, err = jwt.verify(token, "superSecretKey", {
valid_signing_algorithms = {["HS256"]="HS256", ["HS384"]="HS384", ["HS512"]="HS512"}, -- n'autoriser que les algorithmes de la famille HS
audiences = {"user", "admin"}, -- `aud` doit être l'une des valeurs suivantes
ignore_not_before = true -- ignorer la revendication `nbf` (non recommandé)
})
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar
jwt.decrypt
syntax: decoded_token, err = jwt.decrypt(token, secret, options?)
Déchiffrer et valider un jeton JWE et le convertir en table lua.
Le paramètre optionnel options peut être passé pour configurer le validateur de jetons. Les champs valides sont :
- valid_encryption_alg_algorithms (dictalg autorisées utilisées pour déchiffrer le JWT.
- valid_encryption_enc_algorithms (dictenc autorisées utilisées pour déchiffrer le JWT.
- typ (string | nil) : si non nul, assurez-vous que la revendication JWT typ correspond à la valeur passée.
- issuer (string | nil) : si non nul, assurez-vous que la revendication JWT iss correspond à la valeur passée.
- audiences (string | table | nil) : si non nul, assurez-vous que la revendication JWT aud correspond à l'une des valeurs fournies.
- subject (string | nil) : si non nul, assurez-vous que la revendication JWT sub correspond à la valeur passée.
- jwtid (string | nil) : si non nul, assurez-vous que la revendication JWT jti correspond à la valeur passée.
- ignore_not_before (bool) : Si vrai, la revendication JWT nbf sera ignorée.
- ignore_expiration (bool) : Si vrai, la revendication JWT exp sera ignorée.
- current_unix_timestamp (datetime | nil) : les revendications JWT nbf et exp seront validées par rapport à cet horodatage. Si nul, utilisera la date et l'heure actuelles fournies par ngx.time().
- timestamp_skew_seconds (int) : Combien de secondes de marge la bibliothèque peut-elle utiliser pour vérifier l'expiration du jeton par rapport à l'heure actuelle. Utile lorsque les horloges ne sont pas toujours exactement synchronisées. Définir cette valeur trop élevée peut poser des problèmes de sécurité.
- allow_nested_jwt (bool) : Permet la vérification des JWT contenant un autre JWT (c'est-à-dire des JWT imbriqués ou JWT dans JWT). Cela est opt-in par défaut puisque les revendications à valider sont toujours à l'intérieur du JWT le plus interne et NE SERONT PAS automatiquement validées. Il vous appartient de valider récursivement les JWT internes renvoyés en tant que chaîne dans le champ payload par cette bibliothèque. Un JWT imbriqué DOIT contenir la clé d'en-tête cty définie sur JWT pour être reconnu comme tel.
Valeurs par défaut pour les champs options :
local decrypt_default_options = {
valid_encryption_alg_algorithms = {
["RSA-OAEP"]="RSA-OAEP",
["RSA-OAEP-256"]="RSA-OAEP-256", ["RSA-OAEP-384"]="RSA-OAEP-384", ["RSA-OAEP-512"]="RSA-OAEP-512",
["A128KW"]="A128KW", ["A192KW"]="A192KW", ["A256KW"]="A256KW",
["dir"]="dir",
["ECDH-ES"]="ECDH-ES",
["ECDH-ES+A128KW"]="ECDH-ES+A128KW",
["ECDH-ES+A192KW"]="ECDH-ES+A192KW",
["ECDH-ES+A256KW"]="ECDH-ES+A256KW",
},
valid_encryption_enc_algorithms = {
["A128CBC-HS256"]="A128CBC-HS256",
["A192CBC-HS384"]="A192CBC-HS384",
["A256CBC-HS512"]="A256CBC-HS512",
["A128GCM"]="A128GCM",
["A192GCM"]="A192GCM",
["A256GCM"]="A256GCM",
},
typ = nil,
issuer = nil,
audiences = nil,
subject = nil,
jwtid = nil,
ignore_not_before = false,
ignore_expiration = false,
current_unix_timestamp = nil,
timestamp_skew_seconds = 1,
allow_nested_jwt = false,
}
Exemple minimal avec des clés symétriques :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ"
local decoded_token, err = jwt.decrypt(token, "superSecretKey12")
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar
Exemple minimal avec des clés asymétriques au format PEM :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsieCI6IkFJdkVhSzVKZGl6d1I5ZFMzRUN2Y0dKMGNHWXNFejdpYWJwRUp1bE0tWDAiLCJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AifX0.QFfmPVYjk1PoyhE7elaDgUdUGGeAECLo7jB4ghq_8MIRXV3VKO1yAA.XITF2apHB5roeUsx.08T0gALwkb6Wibr2Og.IJoh3U_tspnMx_mWelRT5g"
local decoded_token, err = jwt.decrypt(token, "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VuBCIEIMCxXl/FEuh3pGo1Z++QRs2vudqkGd63mK0Js0f6y+55\n-----END PRIVATE KEY-----", nil)
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- ECDH-ES+A128KW
print(decoded_token.header.enc) -- A256GCM
print(decoded_token.payload.foo) -- bar
Exemples avec des options personnalisées :
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ"
local decoded_token, err = jwt.decrypt(token, "superSecretKey12", {
valid_encryption_alg_algorithms = {["A128KW"]="A128KW"}, -- n'autoriser que les algorithmes de la famille A128KW (nécessite OpenSSL 3.0+)
valid_encryption_enc_algorithms = {["A128CBC-HS256"]="A128CBC-HS256"}, -- n'autoriser que les algorithmes de la famille A128CBC
audiences = {"user", "admin"}, -- `aud` doit être l'une des valeurs suivantes
ignore_not_before = true -- ignorer la revendication `nbf` (non recommandé)
})
if not decoded_token then
return nil, "jwt invalide : " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar
Utilisation de la vérification JWKS
Le module resty.jwt-verification-jwks implémente la récupération automatique de JWKS à partir d'un point de terminaison HTTP et la validation subséquente de JWT avec les clés récupérées.
Les modules resty.jwt-verification-jwks-cache-* implémentent des stratégies de mise en cache JWKS optionnelles. Une seule stratégie de mise en cache peut être activée à la fois ; si aucune n'est activée, le point de terminaison JWKS sera appelé une fois pour chaque JWT à valider.
jwks.init
syntax: ok, err = jwks.init(cache_strategy?)
Initialiser le module jwks et éventuellement spécifier une stratégie de mise en cache.
Cette fonction doit être appelée une seule fois et de préférence dans la section init_by_lua_file.
local jwks = require("resty.jwt-verification-jwks")
-- initialiser sans cache
local ok, err = jwks.init(nil)
if not ok then
ngx.say("Erreur lors de l'initialisation du module jwks : ", err)
end
-- ou ...
-- initialiser avec un cache local basé sur le dictionnaire de mémoire partagée d'openresty.
-- ajoutez ceci dans la section `http` de votre configuration nginx : `lua_shared_dict resty_jwt_verification_cache_jwks 10m;`
-- voir https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#ngxshareddict
local jwks_cache_local = require("resty.jwt-verification-jwks-cache-local")
local ok, err = jwks.init(jwks_cache_local)
if not ok then
ngx.say("Erreur lors de l'initialisation du module jwks : ", err)
end
Vous pouvez implémenter votre propre cache et le passer dans la méthode d'initialisation à la place. Voici un exemple de comment :
local my_cache = {}
---Obtenir la chaîne d'entrée mise en cache pour la clé.
---@param key string Clé de cache.
---@return string|nil value Retourner le résultat mis en cache sous forme de chaîne s'il est présent, nil sinon.
function my_cache.get(key)
-- TODO
end
---Mettre en cache les données sous la clé jusqu'à expiration.
---@param key string Clé de cache.
---@param value string Valeur de cache.
---@param expiry integer Expiration de l'entrée de cache en secondes.
---@return boolean|nil ok true en cas de succès
---@return string|nil err nil en cas de succès, message d'erreur sinon.
function my_cache.setex(key, value, expiry)
-- TODO
end
local ok, err = jwks.init(my_cache)
if not ok then
ngx.say("Erreur lors de l'initialisation du module jwks : ", err)
end
jwks.set_http_timeouts_ms
syntax: jwks.set_http_timeouts_ms(connect, send, read)
Définir les délais d'attente du client HTTP en millisecondes utilisés pour récupérer JWKS.
local jwks = require("resty.jwt-verification-jwks")
jwks.set_http_timeouts_ms(5000, 5000, 5000)
jwks.set_http_ssl_verify
syntax: jwks.set_http_ssl_verify(enabled)
Activer/désactiver la vérification TLS utilisée par le client HTTP pour récupérer JWKS.
Par défaut, tous les certificats TLS sont vérifiés. Si le point de terminaison JWKS utilise des certificats auto-signés, ajoutez soit la CA racine respective au magasin de certificats de l'OS, soit désactivez la vérification des certificats avec ce point de terminaison (ce qui est dangereux).
local jwks = require("resty.jwt-verification-jwks")
jwks.set_http_ssl_verify(false)
jwks.set_cache_ttl
syntax: jwks.set_http_ssl_verify(enabled)
Changer la durée de vie par défaut du cache. La valeur par défaut est de 12 heures.
Remarque : La durée de vie du cache ne peut être utilisée que lorsque le module jwks a été initialisé avec un cache. Voir comment activer la mise en cache.
local jwks = require("resty.jwt-verification-jwks")
jwks.set_cache_ttl(2 * 3600) -- 2h
jwks.fetch_jwks
syntax: payload, err = jwks.fetch_jwks(endpoint)
Récupérer manuellement JWKS à partir d'un point de terminaison HTTP ; le payload retourné, en cas de succès, est le corps de la réponse HTTP sous forme de chaîne : Aucune vérification n'est effectuée pour savoir si le payload contient JWKS ou autre chose.
Si une stratégie de mise en cache a été activée, le point de terminaison essaiera d'abord de le récupérer à partir du cache. Après un échec de cache et une récupération réussie de JWKS via HTTP, le cache sera mis à jour avec le résultat.
local jwks = require("resty.jwt-verification-jwks")
payload, err = jwks.fetch_jwks("https://www.googleapis.com/oauth2/v3/certs")
if payload == nil then
print("échec de la récupération de JWKS : ", err)
return
end
print(payload) -- '{"keys":[{"alg":"RS256","e":"AQAB","kid":"882503a5fd56e9f734dfba5c50d7bf48db284ae9","kty":"RSA","n":"woRUr445_ODXrFeynz5L208aJkABOKQHEzbfGM_V1ijkYZWZKY0PXKPP_wRKcE4C6OyjDNd5gHh3dF5QsVhVDZCfR9QjTf94o4asngrHzdOcfQ0pZIvzu_vzaVG82VGLM-2rKQp8uz06A6TbUzbIv9wQ8wQpYDIdujNkLqL22Mkb2drPxm9Y9I05PmVdkkvAbu4Q_KRJWxykOigHp-hVBmpYS2P3xuX56gM7ZRcXXJKKUfrGel4nDhSIAAD1wBNcVVgKbb0TYfZmVpRSCji_b6JHjqYhYjUasdotYJzWl7quAFsN_X_4j-cHZ30OS81j--OiIxWpL11y1kcbE0u-Dw","use":"sig"},{"n":"m7GlcF1ExRB4braT7sDnZvlY3wpqX9krkVRqcVA-m43FWFYBtuSpd-lc0EV8R8TO180y0tSgJc7hviI1IBJQlNa7XkjVGhY0ZFUp5rTpC45QbA9Smo4CLa5HQIf-69rkkovjFNMuDQvNiYCgRPLyRjmQbN2uHl4fU3hhf5qFqKTKo7eLCZiEMjrOkTXziA7xJJigUGe-ab8U-AXNH1fnCbejzHEIxL0eUG_4r4xddImOxETDO5T65AQCeqs7vtYos2xq5SLFuaUsithRQ-IMm3OlcVhMjBYt6uvGS6IdMjKon4wThCxEqAEXg0nahiGjnQCW176SNF152__TOjQVwQ","alg":"RS256","kty":"RSA","use":"sig","kid":"8e8fc8e556f7a76d08d35829d6f90ae2e12cfd0d","e":"AQAB"}]}'
jwks.verify_jwt_with_jwks
syntax: jwt, err = jwks.verify_jwt_with_jwks(jwt_token, jwks_endpoint, jws_options?)
Étant donné un jwt_token signé sous forme de chaîne, vérifiez sa signature avec JWKS fournies par le service HTTP trouvé à jwks_endpoint.
En cas de succès, le JWT vérifié est retourné sous forme de table lua, sinon nil et une erreur sont retournés.
Le paramètre optionnel jws_options peut être passé pour configurer le validateur de jetons lors de l'appel à jwt.verify après avoir récupéré avec succès le JWKS. Voir la documentation respective de jwt.verify pour plus d'informations sur les options qui peuvent être passées.
local jwks = require("resty.jwt-verification-jwks")
jwt, err = jwks.verify_jwt_with_jwks("<MON_JWT>", "http://myservice:8888/.well-known/jwks.json", nil)
if jwt == nil then
print("échec de la vérification du jwt : ", err)
return
end
print(jwt.header.alg)
print(tostring(jwt.payload))
jwks.decrypt_jwt_with_jwks
syntax: jwt, err = jwks.decrypt_jwt_with_jwks(jwt_token, jwks_endpoint, jwe_options?)
Étant donné un jwt_token chiffré sous forme de chaîne, déchiffrez-le avec JWKS fournies par le service HTTP trouvé à jwks_endpoint.
En cas de succès, le JWT déchiffré est retourné sous forme de table lua, sinon nil et une erreur sont retournés.
Le paramètre optionnel jwe_options peut être passé pour configurer le validateur de jetons lors de l'appel à jwt.decrypt après avoir récupéré avec succès le JWKS. Voir la documentation respective de jwt.decrypt pour plus d'informations sur les options qui peuvent être passées.
local jwks = require("resty.jwt-verification-jwks")
jwt, err = jwks.decrypt_jwt_with_jwks("<MON_JWT>", "http://myservice:8888/.well-known/jwks.json", nil)
if jwt == nil then
print("échec du déchiffrement du jwt : ", err)
return
end
print(jwt.header.alg)
print(jwt.header.enc)
print(tostring(jwt.payload))
RFCs utilisés comme référence
- RFC 7515 JSON Web Signature (JWS)
- RFC 7516 JSON Web Encryption (JWE)
- RFC 7517 JSON Web Key (JWK)
- RFC 7518 JSON Web Algorithms (JWA)
- RFC 7519 JSON Web Token (JWT)
- RFC 7520 Exemples de protection de contenu utilisant la signature et le chiffrement d'objets JSON (JOSE)
- Attributions JOSE de l'IANA
Exécuter des tests
Configuration
Installer la suite de tests :
sudo cpan Test::Nginx
Installer openresty : voir https://openresty.org/en/linux-packages.html
Exécuter
export PATH=/usr/local/openresty/nginx/sbin:$PATH
prove -r t
Exécuter des benchmarks
La suite de tests Test::Nginx a une intégration de benchmarking intégrée avec weighttp.
Augmenter les limites sysctl
Si vous prévoyez de tester la bibliothèque sous stress, vous devrez peut-être augmenter les limites système.
cat > /etc/sysctl.d/openresty-benchmarks.conf << EOF
net.ipv4.ip_local_port_range=2048 65535
net.ipv4.tcp_tw_reuse=1
net.core.netdev_max_backlog=2000
net.ipv4.tcp_max_syn_backlog=2048
EOF
## appliquer les modifications
sudo sysctl -p /etc/sysctl.d/openresty-benchmarks.conf
Lancer des tests
J'ai créé quelques scénarios pseudo-réalistes dans le dossier benchmarks.
## pour plus d'informations sur la syntaxe : https://openresty.gitbooks.io/programming-openresty/content/testing/test-modes.html
export TEST_NGINX_BENCHMARK='50000 10'
prove -r ./benchmarks
Par défaut, seul 1 travailleur nginx et 1 cœur CPU seront utilisés pour effectuer les benchmarks. Pour augmenter les limites de travailleurs, modifiez la directive workers(1); à l'intérieur des fichiers .t et relancez le benchmark.
GitHub
Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-jwt-verification.