Pular para conteúdo

jwt-verification: Biblioteca de verificação de JWT para nginx-module-lua com integração JWKS

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Depois, você pode prosseguir com os seguintes passos.

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

Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua esteja instalado.

Este documento descreve lua-resty-jwt-verification v0.7.0 lançado em 30 de outubro de 2025.


Biblioteca de verificação de JWT para OpenResty.

OPM LuaRocks Tests

Descrição

Biblioteca de verificação de JWT para OpenResty.

O objetivo do projeto é ser um substituto moderno e mais leve para lua-resty-jwt com suporte embutido para JWKS.

Este projeto não fornece recursos de manipulação ou criação de JWT: você pode apenas verificar/descriptografar tokens.

Objetivos não da biblioteca

  • Criação/modificação de JWT
  • Completo em recursos apenas para a completude dos RFCs.
  • Recursos de RFCs sem sentido e inseguros (por exemplo, alg none) não serão implementados.

Diferenças em relação ao lua-resty-jwt

As principais diferenças são: - Nenhuma manipulação de JWT de qualquer tipo (você pode apenas descriptografá-los/verificá-los) - Estrutura interna mais simples, dependente de versões mais recentes do lua-resty-openssl e OpenSSL. - Suporta diferentes algoritmos JWE (veja as tabelas acima). - Verificação automática de JWT dada uma URL de endpoint HTTP JWKS.

Se algum dos pontos acima for um problema, ou se você precisar de compatibilidade com versões mais antigas do OpenResty, recomendo continuar com lua-resty-jwt.

Tipos

Tipos e verificações nulas são fornecidos com uso extensivo de EmmyLua annotations.

Integrações de plugins de IDE para EmmyLua: - Idea - VSCode

O arquivo ngx.d.lua na raiz do projeto fornece alguns stubs de ngx.

Recursos suportados

  • Verificação de JWS: com chaves simétricas ou assimétricas.
  • Descriptografia de JWE: com chaves simétricas ou assimétricas.
  • Formato de chaves assimétricas suportadas:
  • PEM
  • DER
  • JWK
  • Validação de claims de JWT.
  • Busca automática de JWKS e validação de JWT.
  • estratégias de cache opcionais.
  • JWTs aninhados (JWS em JWE)

Verificação de JWS

Claims Implementado
alg ✅
jku ❌
jwk ❌
kid ✅
x5u ❌
x5c ❌
x5t ❌
x5t#S256 ❌
typ ✅
cty ❌
crit ✅
alg Implementado Requisitos de Implementação JOSE Requisitos
HS256 ✅ Requerido
HS384 ✅ Opcional
HS512 ✅ Opcional
RS256 ✅ Recomendado
RS384 ✅ Opcional
RS512 ✅ Opcional
ES256 ✅ Recomendado+
ES384 ✅ Opcional
ES512 ✅ Opcional
PS256 ✅ Opcional
PS384 ✅ Opcional
PS512 ✅ Opcional
none ❌ Opcional
EdDSA ❌ Obsoleto
ES256K ✅ Opcional
Ed25519 ✅ Opcional *OpenSSL 3.0+
Ed448 ✅ Opcional

Descriptografia de JWE

Claims Implementado
alg ✅
enc ✅
zip ❌
jku ❌
jwk ❌
kid ✅
x5u ❌
x5c ❌
x5t ❌
x5t#S256 ❌
typ ✅
cty ✅
crit ✅
kty Implementado Requisitos de Implementação JOSE
EC ✅ Recomendado+
RSA ✅ Requerido
oct ✅ Requerido
OKP ✅ Opcional
alg Implementado Requisitos de Implementação JOSE Requisitos
RSA1_5 ❌ Recomendado-
RSA-OAEP ✅ Recomendado+
RSA-OAEP-256 ✅ Opcional
RSA-OAEP-384 ✅ Opcional
RSA-OAEP-512 ✅ Opcional
A128KW ✅ Recomendado *OpenSSL 3.0+
A192KW ✅ Opcional *OpenSSL 3.0+
A256KW ✅ Recomendado *OpenSSL 3.0+
dir ✅ Recomendado
ECDH-ES ✅ Recomendado+
ECDH-ES+A128KW ✅ Recomendado *OpenSSL 3.0+
ECDH-ES+A192KW ✅ Opcional *OpenSSL 3.0+
ECDH-ES+A256KW ✅ Recomendado *OpenSSL 3.0+
A128GCMKW ❌ Opcional
A192GCMKW ❌ Opcional
A256GCMKW ❌ Opcional
PBES2-HS256+A128KW ❌ Opcional
PBES2-HS384+A192KW ❌ Opcional
PBES2-HS512+A256KW ❌ Opcional

*O primeiro lançamento oficial do OpenResty incluindo OpenSSL 3.0+ é OpenResty 1.27.1.1 que foi lançado com OpenSSL 3.0.15 (Sim, a horrível e lenta série OpenSSL 3.0...).

Portanto, por favor, use OpenResty 1.27.1.2 como mínimo, que foi lançado com OpenSSL 3.4.1.

enc Implementado Requisitos de Implementação JOSE
A128CBC-HS256 ✅ Requerido
A192CBC-HS384 ✅ Opcional
A256CBC-HS512 ✅ Requerido
A128GCM ✅ Recomendado
A192GCM ✅ Opcional
A256GCM ✅ Recomendado

Estratégias de cache para recuperação de JWKS

Estratégia de Cache Implementado
sem cache ✅
local (shared_dict) ✅

Uso da verificação de JWT

jwt.decode_header_unsafe

sintaxe: header, err = jwt.decode_header_unsafe(token)

Lê um cabeçalho jwt e o converte em uma tabela lua.

Importante: este método não valida a assinatura do JWT! Use apenas se você precisar inspecionar o cabeçalho do token sem realizar a validação completa.

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 malformado: " .. err
end
print("alg: " .. header.alg) -- alg: HS256

jwt.verify

sintaxe: decoded_token, err = jwt.verify(token, secret, options?)

Valida um token JWS e o converte em uma tabela lua.

O parâmetro opcional options pode ser passado para configurar o validador de token. Os campos válidos são: - valid_signing_algorithms (dict | nil): um dicionário contendo os claims alg permitidos usados para validar o JWT. - typ (string | nil): se não nulo, garante que o claim JWT typ corresponda ao valor passado. - issuer (string | nil): se não nulo, garante que o claim JWT iss corresponda ao valor passado. - audiences (string | tabela | nil): se não nulo, garante que o claim JWT aud corresponda a um dos valores fornecidos. - subject (string | nil): se não nulo, garante que o claim JWT sub corresponda ao valor passado. - jwtid (string | nil): se não nulo, garante que o claim JWT jti corresponda ao valor passado. - ignore_not_before (bool): Se verdadeiro, o claim JWT nbf será ignorado. - ignore_expiration (bool): Se verdadeiro, o claim JWT exp será ignorado. - current_unix_timestamp (datetime | nil): os claims JWT nbf e exp serão validados em relação a este timestamp. Se nulo, usará a data e hora atuais fornecidas por ngx.time(). - timestamp_skew_seconds (int): Quantos segundos de margem a biblioteca pode usar para verificar a expiração do token em relação ao tempo atual. Útil quando os relógios não estão sempre exatamente sincronizados. Definir este valor muito alto pode representar problemas de segurança.

Valores padrão para os campos 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,
}

Exemplo mínimo com chaves simétricas:

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 inválido: " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar

Exemplo mínimo com chaves assimétricas:

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 inválido: " .. err
end
print(decoded_token.header.alg) -- RS256
print(decoded_token.payload.foo) -- bar

Exemplos com options personalizadas:

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"}, -- permite apenas algoritmos da família HS
    audiences = {"user", "admin"}, -- `aud` deve ser um dos seguintes
    ignore_not_before = true -- ignora o claim `nbf` (não recomendado)
})
if not decoded_token then
    return nil, "jwt inválido: " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar

jwt.decrypt

sintaxe: decoded_token, err = jwt.decrypt(token, secret, options?)

Descriptografa e valida um token JWE e o converte em uma tabela lua.

O parâmetro opcional options pode ser passado para configurar o validador de token. Os campos válidos são: - valid_encryption_alg_algorithms (dict | nil): um dicionário contendo os claims alg permitidos usados para descriptografar o JWT. - valid_encryption_enc_algorithms (dict | nil): um dicionário contendo os claims enc permitidos usados para descriptografar o JWT. - typ (string | nil): se não nulo, garante que o claim JWT typ corresponda ao valor passado. - issuer (string | nil): se não nulo, garante que o claim JWT iss corresponda ao valor passado. - audiences (string | tabela | nil): se não nulo, garante que o claim JWT aud corresponda a um dos valores fornecidos. - subject (string | nil): se não nulo, garante que o claim JWT sub corresponda ao valor passado. - jwtid (string | nil): se não nulo, garante que o claim JWT jti corresponda ao valor passado. - ignore_not_before (bool): Se verdadeiro, o claim JWT nbf será ignorado. - ignore_expiration (bool): Se verdadeiro, o claim JWT exp será ignorado. - current_unix_timestamp (datetime | nil): os claims JWT nbf e exp serão validados em relação a este timestamp. Se nulo, usará a data e hora atuais fornecidas por ngx.time(). - timestamp_skew_seconds (int): Quantos segundos de margem a biblioteca pode usar para verificar a expiração do token em relação ao tempo atual. Útil quando os relógios não estão sempre exatamente sincronizados. Definir este valor muito alto pode representar problemas de segurança. - allow_nested_jwt (bool): Permite a verificação de JWTs que contêm outro JWT (ou seja, JWTs aninhados ou jwt-in-jwt). Isso é opcional, pois os claims a serem validados estão sempre dentro do JWT mais interno e NÃO serão validados automaticamente. Cabe a você validar recursivamente os JWTs internos retornados como uma string no campo payload por esta biblioteca. Um JWT aninhado DEVE conter a chave de cabeçalho cty configurada como JWT para ser reconhecido como tal.

Valores padrão para os campos 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,
}

Exemplo mínimo com chaves simétricas:

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 inválido: " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar

Exemplo mínimo com chaves assimétricas em formato 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 inválido: " .. err
end
print(decoded_token.header.alg) -- ECDH-ES+A128KW
print(decoded_token.header.enc) -- A256GCM
print(decoded_token.payload.foo) -- bar

Exemplos com options personalizadas:

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"}, -- permite apenas algoritmos da família A128KW (requer OpenSSL 3.0+)
    valid_encryption_enc_algorithms = {["A128CBC-HS256"]="A128CBC-HS256"}, -- permite apenas codificações da família A128CBC
    audiences = {"user", "admin"}, -- `aud` deve ser um dos seguintes
    ignore_not_before = true -- ignora o claim `nbf` (não recomendado)
})
if not decoded_token then
    return nil, "jwt inválido: " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar

Uso da verificação de JWKS

O módulo resty.jwt-verification-jwks implementa a recuperação automática de JWKS de um endpoint HTTP e a subsequente validação de JWT com as chaves obtidas.

Os módulos resty.jwt-verification-jwks-cache-* implementam estratégias de cache JWKS opcionais. Apenas uma estratégia de cache pode ser habilitada por vez; se nenhuma estiver habilitada, o endpoint JWKS será chamado uma vez para cada JWT a ser validado.

jwks.init

sintaxe: ok, err = jwks.init(cache_strategy?)

Inicializa o módulo jwks e opcionalmente especifica uma estratégia de cache.

Esta função deve ser chamada apenas uma vez e preferencialmente na seção init_by_lua_file.

local jwks = require("resty.jwt-verification-jwks")

-- inicializa sem cache
local ok, err = jwks.init(nil)
if not ok then
    ngx.say("Erro ao inicializar o módulo jwks: ", err)
end

-- ou ...

-- inicializa com cache local baseado no dicionário de memória compartilhada do OpenResty.
-- adicione isso na seção `http` da sua configuração do nginx: `lua_shared_dict resty_jwt_verification_cache_jwks 10m;`
-- veja 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("Erro ao inicializar o módulo jwks: ", err)
end

Você pode implementar seu próprio cache e passá-lo no método de inicialização. Aqui está um exemplo de como:

local my_cache = {}

---Obtém a string de entrada em cache para a chave.
---@param key string Chave do cache.
---@return string|nil value Retorna o resultado em cache como string se presente, nil caso contrário.
function my_cache.get(key)
    -- TODO
end

---Cache de dados sob a chave até a expiração.
---@param key string Chave do cache.
---@param value string Valor do cache.
---@param expiry integer Expiração da entrada do cache em segundos.
---@return boolean|nil ok verdadeiro em caso de sucesso
---@return string|nil err nil em caso de sucesso, mensagem de erro caso contrário.
function my_cache.setex(key, value, expiry)
    -- TODO
end

local ok, err = jwks.init(my_cache)
if not ok then
    ngx.say("Erro ao inicializar o módulo jwks: ", err)
end

jwks.set_http_timeouts_ms

sintaxe: jwks.set_http_timeouts_ms(connect, send, read)

Define os timeouts do cliente HTTP em milissegundos usados para buscar JWKS.

local jwks = require("resty.jwt-verification-jwks")

jwks.set_http_timeouts_ms(5000, 5000, 5000)

jwks.set_http_ssl_verify

sintaxe: jwks.set_http_ssl_verify(enabled)

Habilita/desabilita a verificação TLS usada pelo cliente HTTP para buscar JWKS.

Por padrão, todos os certificados TLS são verificados. Se o endpoint JWKS estiver usando certificados autoassinados, adicione o respectivo CA raiz ao armazenamento de certificados do OS ou desative a verificação de certificados com este endpoint (isso é inseguro).

local jwks = require("resty.jwt-verification-jwks")

jwks.set_http_ssl_verify(false)

jwks.set_cache_ttl

sintaxe: jwks.set_http_ssl_verify(enabled)

Altera o TTL de cache padrão. O valor padrão é 12 horas.

Nota: O TTL de cache só pode ser usado quando o módulo jwks foi inicializado com um cache. Veja como habilitar o cache.

local jwks = require("resty.jwt-verification-jwks")

jwks.set_cache_ttl(2 * 3600) -- 2h

jwks.fetch_jwks

sintaxe: payload, err = jwks.fetch_jwks(endpoint)

Busca manualmente JWKS do endpoint HTTP; o payload retornado, em caso de sucesso, é o corpo da resposta HTTP como string: Nenhuma verificação é realizada para saber se o payload contém JWKS ou algo mais.

Se uma estratégia de cache foi habilitada, o endpoint tentará buscá-lo primeiro no cache. Após uma falta de cache e a recuperação bem-sucedida de JWKS via HTTP, o cache será atualizado com o resultado.

local jwks = require("resty.jwt-verification-jwks")

payload, err = jwks.fetch_jwks("https://www.googleapis.com/oauth2/v3/certs")
if payload == nil then
    print("falha ao buscar 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

sintaxe: jwt, err = jwks.verify_jwt_with_jwks(jwt_token, jwks_endpoint, jws_options?)

Dado um jwt_token assinado como string, verifica sua assinatura com JWKS fornecido pelo serviço HTTP encontrado em jwks_endpoint.

Em caso de sucesso, o JWT verificado é retornado como uma tabela lua, caso contrário, nil e um erro são retornados.

O parâmetro opcional jws_options pode ser passado para configurar o validador de token ao chamar jwt.verify após ter buscado com sucesso o JWKS. Veja a documentação respectiva de jwt.verify para mais informações sobre quais opções podem ser passadas.

local jwks = require("resty.jwt-verification-jwks")

jwt, err = jwks.verify_jwt_with_jwks("<MEU_JWT>", "http://meuservico:8888/.well-known/jwks.json", nil)
if jwt == nil then
    print("falha ao verificar jwt: ", err)
    return
end
print(jwt.header.alg)
print(tostring(jwt.payload))

jwks.decrypt_jwt_with_jwks

sintaxe: jwt, err = jwks.decrypt_jwt_with_jwks(jwt_token, jwks_endpoint, jwe_options?)

Dado um jwt_token criptografado como string, descriptografa-o com JWKS fornecido pelo serviço HTTP encontrado em jwks_endpoint.

Em caso de sucesso, o JWT descriptografado é retornado como uma tabela lua, caso contrário, nil e um erro são retornados.

O parâmetro opcional jwe_options pode ser passado para configurar o validador de token ao chamar jwt.decrypt após ter buscado com sucesso o JWKS. Veja a documentação respectiva de jwt.decrypt para mais informações sobre quais opções podem ser passadas.

local jwks = require("resty.jwt-verification-jwks")

jwt, err = jwks.decrypt_jwt_with_jwks("<MEU_JWT>", "http://meuservico:8888/.well-known/jwks.json", nil)
if jwt == nil then
    print("falha ao descriptografar jwt: ", err)
    return
end
print(jwt.header.alg)
print(jwt.header.enc)
print(tostring(jwt.payload))

RFCs usadas como referência

Executar testes

Configuração

Instale o conjunto de testes:

sudo cpan Test::Nginx

Instale o openresty: veja https://openresty.org/en/linux-packages.html

Executar

export PATH=/usr/local/openresty/nginx/sbin:$PATH
prove -r t

Executar benchmarks

O conjunto de testes Test::Nginx tem integração de benchmarking embutida com weighttp.

Aumentar limites do sysctl

Se você planeja estressar a biblioteca, pode ser necessário aumentar os limites do sistema.

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

## aplicar mudanças
sudo sysctl -p /etc/sysctl.d/openresty-benchmarks.conf

Lançar testes

Eu criei alguns cenários pseudo-reais dentro da pasta benchmarks.

## para mais informações sobre a sintaxe: https://openresty.gitbooks.io/programming-openresty/content/testing/test-modes.html
export TEST_NGINX_BENCHMARK='50000 10'
prove -r ./benchmarks

Por padrão, apenas 1 trabalhador do nginx e 1 núcleo de CPU serão usados para realizar os benchmarks. Para aumentar os limites de trabalhadores, altere a diretiva workers(1); dentro dos arquivos .t e execute novamente o benchmark.

GitHub

Você pode encontrar dicas de configuração adicionais e documentação para este módulo no repositório GitHub para nginx-module-jwt-verification.