{"id":25999592,"url":"https://github.com/anvouk/lua-resty-jwt-verification","last_synced_at":"2025-07-09T06:06:15.210Z","repository":{"id":279082239,"uuid":"805940785","full_name":"anvouk/lua-resty-jwt-verification","owner":"anvouk","description":"JWT verification lib for openresty","archived":false,"fork":false,"pushed_at":"2025-07-06T16:12:15.000Z","size":89,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-09T06:05:00.272Z","etag":null,"topics":["jwe","jwks","jws","jwt","lua","openresty"],"latest_commit_sha":null,"homepage":"","language":"Raku","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anvouk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-05-25T22:51:21.000Z","updated_at":"2025-07-06T16:12:19.000Z","dependencies_parsed_at":"2025-07-05T15:03:03.654Z","dependency_job_id":null,"html_url":"https://github.com/anvouk/lua-resty-jwt-verification","commit_stats":null,"previous_names":["anvouk/lua-resty-jwt-verification"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/anvouk/lua-resty-jwt-verification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anvouk%2Flua-resty-jwt-verification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anvouk%2Flua-resty-jwt-verification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anvouk%2Flua-resty-jwt-verification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anvouk%2Flua-resty-jwt-verification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anvouk","download_url":"https://codeload.github.com/anvouk/lua-resty-jwt-verification/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anvouk%2Flua-resty-jwt-verification/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264403796,"owners_count":23602620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jwe","jwks","jws","jwt","lua","openresty"],"created_at":"2025-03-05T18:40:38.979Z","updated_at":"2025-07-09T06:06:15.202Z","avatar_url":"https://github.com/anvouk.png","language":"Raku","readme":"# JWT verification for openresty\n\nJWT verification library for OpenResty.\n\n## Table of Contents\n\n- [Description](#description)\n- [Status](#status)\n- [Library non-goals](#library-non-goals)\n- [Differences from lua-resty-jwt](#differences-from-lua-resty-jwt)\n- [Supported features](#supported-features)\n  - [JWS Verification](#jws-verification)\n  - [JWE Decryption](#jwe-decryption)\n  - [JWKS retrieval cache strategies](#jwks-retrieval-cache-strategies)\n- [Planned missing features](#planned-missing-features)\n- [Dependencies](#dependencies)\n- [JWT verification usage](#jwt-verification-usage)\n  - [jwt.decode_header_unsafe](#jwtdecode_header_unsafe)\n  - [jwt.verify](#jwtverify)\n  - [jwt.decrypt](#jwtdecrypt)\n- [JWKS verification usage](#jwks-verification-usage)\n  - [jwks.enable_cache_strategy_local](#jwksenable_cache_strategy_local)\n  - [jwks.set_http_timeouts_ms](#jwksset_http_timeouts_ms)\n  - [jwks.set_http_ssl_verify](#jwksset_http_ssl_verify)\n  - [jwks.fetch_jwks](#jwksfetch_jwks)\n  - [jwks.verify_jwt_with_jwks](#jwksverify_jwt_with_jwks)\n- [RFCs used as reference](#rfcs-used-as-reference)\n- [Run tests](#run-tests)\n  - [Setup](#setup)\n  - [Run](#run)\n\n## Description\n\nJWT verification library for OpenResty.\n\nThe project's goal is to be a modern and slimmer replacement for [lua-resty-jwt](https://github.com/cdbattags/lua-resty-jwt/)\nwith built-in support for JWKS.\n\nThis project does not provide JWT manipulation or creation features: you can only verify/decrypt tokens.\n\n## Status\n\nReady for testing: looking for more people to take it for a spin and provide feedback.\n\nThe APIs should be stable; I'll provide a migration document in case breaking changes happen in future releases.\n\n## Library non-goals\n\n- JWT creation/modification\n- Feature complete for the sake of RFCs completeness.\n- Senseless and unsafe RFCs features (e.g. alg none) won't be implemented.\n\n## Differences from lua-resty-jwt\n\nMain differences are:\n- No JWT manipulation of any kind (you can only decrypt/verify them)\n- Simpler internal structure reliant on more recent [lua-resty-openssl](https://github.com/fffonion/lua-resty-openssl) and OpenSSL versions.\n- Supports different JWE algorithms (see tables above).\n- Automatic JWT verification given JWKS HTTP endpoint.\n\nIf any of the points above are a problem, or you need compatibility with older OpenResty versions, I\nrecommend sticking with [lua-resty-jwt](https://github.com/cdbattags/lua-resty-jwt/).\n\n## Supported features\n\n- JWS verification: with symmetric or asymmetric keys.\n- JWE decryption: with symmetric or asymmetric keys.\n- Asymmetric keys format supported:\n  - PEM\n  - DER\n  - JWK\n- JWT claims validation.\n- Automatic JWKS fetching and JWT validation.\n  - optional caching strategies.\n\n### JWS Verification\n\n|  Claims  |    Implemented     |\n|:--------:|:------------------:|\n|   alg    | :white_check_mark: |\n|   jku    |        :x:         |\n|   jwk    |        :x:         |\n|   kid    | :white_check_mark: |\n|   x5u    |        :x:         |\n|   x5c    |        :x:         |\n|   x5t    |        :x:         |\n| x5t#S256 |        :x:         |\n|   typ    | :white_check_mark: |\n|   cty    |        :x:         |\n|   crit   | :white_check_mark: |\n\n|  Alg  |    Implemented     |\n|:-----:|:------------------:|\n| HS256 | :white_check_mark: |\n| HS384 | :white_check_mark: |\n| HS512 | :white_check_mark: |\n| RS256 | :white_check_mark: |\n| RS384 | :white_check_mark: |\n| RS512 | :white_check_mark: |\n| ES256 | :white_check_mark: |\n| ES384 | :white_check_mark: |\n| ES512 | :white_check_mark: |\n| PS256 | :white_check_mark: |\n| PS384 | :white_check_mark: |\n| PS512 | :white_check_mark: |\n| none  |        :x:         |\n\n### JWE Decryption\n\n|  Claims  |    Implemented     |\n|:--------:|:------------------:|\n|   alg    | :white_check_mark: |\n|   enc    | :white_check_mark: |\n|   zip    |        :x:         |\n|   jku    |        :x:         |\n|   jwk    |        :x:         |\n|   kid    | :white_check_mark: |\n|   x5u    |        :x:         |\n|   x5c    |        :x:         |\n|   x5t    |        :x:         |\n| x5t#S256 |        :x:         |\n|   typ    | :white_check_mark: |\n|   cty    |        :x:         |\n|   crit   | :white_check_mark: |\n\n|        Alg         |     Implemented     | Requirements  |\n|:------------------:|:-------------------:|:-------------:|\n|       RSA1_5       |         :x:         |               |\n|      RSA-OAEP      |         :x:         |               |\n|    RSA-OAEP-256    |         :x:         |               |\n|       A128KW       | :white_check_mark:  | *OpenSSL 3.0+ |\n|       A192KW       | :white_check_mark:  | *OpenSSL 3.0+ |\n|       A256KW       | :white_check_mark:  | *OpenSSL 3.0+ |\n|        dir         | :white_check_mark:  |               |\n|      ECDH-ES       |         :x:         |               |\n|     A128GCMKW      |         :x:         |               |\n|     A192GCMKW      |         :x:         |               |\n|     A256GCMKW      |         :x:         |               |\n| PBES2-HS256+A128KW |         :x:         |               |\n| PBES2-HS384+A192KW |         :x:         |               |\n| PBES2-HS512+A256KW |         :x:         |               |\n\n\u003e *The first official release of OpenResty including OpenSSL 3.0+ is [OpenResty 1.27.1.1](https://openresty.org/en/ann-1027001001.html)\n\u003e which shipped with OpenSSL 3.0.15 (Yes, the [godawful slow OpenSSL 3.0 series...](https://github.com/openssl/openssl/issues/17064)).\n\u003e\n\u003e So, please, go with [OpenResty 1.27.1.2](https://openresty.org/en/ann-1027001002.html) as a minimum, which shipped\n\u003e with OpenSSL 3.4.1.\n\n|      Enc      |    Implemented     |\n|:-------------:|:------------------:|\n| A128CBC-HS256 | :white_check_mark: |\n| A192CBC-HS384 | :white_check_mark: |\n| A256CBC-HS512 | :white_check_mark: |\n|    A128GCM    | :white_check_mark: |\n|    A192GCM    | :white_check_mark: |\n|    A256GCM    | :white_check_mark: |\n\n## JWKS retrieval cache strategies\n\n|   Cache Strategy    |    Implemented     |\n|:-------------------:|:------------------:|\n|      no cache       | :white_check_mark: |\n| local (shared_dict) | :white_check_mark: |\n|        redis        |        :x:         |\n\n## Planned missing features\n\nThis is a list of missing features I'd like to implement when given enough time:\n- Implement JWE validation with at least 1 asymmetric `alg`.\n- Nested JWT (i.e. JWT in JWE).\n- JWKS Redis cache strategy.\n- Automatic JWKS validation for JWE.\n\n## Dependencies\n\n```bash\nluarocks install lua-cjson\nluarocks install lua-resty-openssl\nluarocks install lua-resty-http\n```\n\n## JWT verification usage\n\n### jwt.decode_header_unsafe\n\n**syntax**: *header, err = jwt.decode_header_unsafe(token)*\n\nRead a jwt header and convert it to a lua table.\n\n\u003e **Important**: this method does not validate JWT signature! Only use if you need to inspect the token's header\n\u003e without having to perform the full validation.\n\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NDkwNzJ9._MwFdsBPSyci9iARpoAaulReGcn1q7mKiPZjR2JDvdY\"\nlocal header, err = jwt.decode_header_unsafe(token)\nif not header then\n    return nil, \"malformed jwt: \" .. err\nend\nprint(\"alg: \" .. header.alg) -- alg: HS256\n```\n\n### jwt.verify\n\n**syntax**: *decoded_token, err = jwt.verify(token, secret, options?)*\n\nValidate a JWS token and convert it to a lua table.\n\nThe optional parameter `options` can be passed to configure the token validator. Valid fields are:\n- `valid_signing_algorithms` (dict\u003cstring, string\u003e | nil): a dict containing allowed `alg` claims used to validate the JWT.\n- `typ` (string | nil): if non-null, ensure JWT claim `typ` matches the passed value.\n- `issuer` (string | nil): if non-null, ensure JWT claim `iss` matches the passed value.\n- `audiences` (string | table | nil): if non-null, ensure JWT claim `aud` matches one of the supplied values.\n- `subject` (string | nil): if non-null, ensure JWT claim `sub` matches the passed value.\n- `jwtid` (string | nil): if non-null, ensure JWT claim `jti` matches the passed value.\n- `ignore_not_before` (bool): If true, the JWT claim `nbf` will be ignored.\n- `ignore_expiration` (bool): If true, the JWT claim `exp` will be ignored.\n- `current_unix_timestamp` (datetime | nil): the JWT `nbf` and `exp` claims will be validated against this timestamp. If null,\nwill use the current datetime supplied by `ngx.time()`.\n- `timestamp_skew_seconds` (int):\n\nDefault values for `options` fields:\n```lua\nlocal verify_default_options = {\n    valid_signing_algorithms = {\n        [\"HS256\"]=\"HS256\", [\"HS384\"]=\"HS384\", [\"HS512\"]=\"HS512\",\n        [\"RS256\"]=\"RS256\", [\"RS384\"]=\"RS384\", [\"RS512\"]=\"RS512\",\n        [\"ES256\"]=\"ES256\", [\"ES384\"]=\"ES384\", [\"ES512\"]=\"ES512\",\n        [\"PS256\"]=\"PS256\", [\"PS384\"]=\"PS384\", [\"PS512\"]=\"PS512\",\n    },\n    typ = nil,\n    issuer = nil,\n    audiences = nil,\n    subject = nil,\n    jwtid = nil,\n    ignore_not_before = false,\n    ignore_expiration = false,\n    current_unix_timestamp = nil,\n    timestamp_skew_seconds = 1,\n}\n```\n\nMinimal example with symmetric keys:\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0\"\nlocal decoded_token, err = jwt.verify(token, \"superSecretKey\")\nif not decoded_token then\n    return nil, \"invalid jwt: \" .. err\nend\nprint(decoded_token.header.alg) -- HS256\nprint(decoded_token.payload.foo) -- bar\n```\n\nMinimal example with asymmetric keys:\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2Njg2Mzd9.H6PE-zLizMMqefx8DG4X5glVjyxR9UNT225Tq2yufHhu4k9K0IGttpykjMCG8Ck_4Qt2ezEWIgoiWhSn1rv_zwxe7Pv-B09fDs7h1hbASi5MZ0YVAmK9ID1RCKM_NTBEnPLot_iopKZRj2_J5F7lvXwJDZSzEAFJZdrgjKeBS4saDZAv7SIL9Nk75rdhgY-RgRwsjmTYSksj7eioRJJLHifrMnlQDbdrBD5_Qk5tD6VPcssO-vIVBUAYrYYTa7M7A_v47UH84zDtzNYBbk9NrDbyq5-tYs0lZwNhIX8t-0VAxjuCyrrGZvv8_O01pdi90kQmntFIbaiDiD-1WlGcGA\"\nlocal 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-----\")\nif not decoded_token then\n    return nil, \"invalid jwt: \" .. err\nend\nprint(decoded_token.header.alg) -- RS256\nprint(decoded_token.payload.foo) -- bar\n```\n\nExamples with custom `options`:\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0\"\nlocal decoded_token, err = jwt.verify(token, \"superSecretKey\", {\n    valid_signing_algorithms = {[\"HS256\"]=\"HS256\", [\"HS384\"]=\"HS384\", [\"HS512\"]=\"HS512\"}, -- only allow HS family algs\n    audiences = {\"user\", \"admin\"}, -- `aud` must be one of the following\n    ignore_not_before = true -- ignore `nbf` claim (not recommended)\n})\nif not decoded_token then\n    return nil, \"invalid jwt: \" .. err\nend\nprint(decoded_token.header.alg) -- HS256\nprint(decoded_token.payload.foo) -- bar\n```\n\n### jwt.decrypt\n\n**syntax**: *decoded_token, err = jwt.decrypt(token, secret, options?)*\n\nDecrypt and validate a JWE token and convert it to a lua table.\n\nThe optional parameter `options` can be passed to configure the token validator. Valid fields are:\n- `valid_encryption_alg_algorithms` (dict\u003cstring, string\u003e | nil): a dict containing allowed `alg` claims used to decrypt the JWT.\n- `valid_encryption_enc_algorithms` (dict\u003cstring, string\u003e | nil): a dict containing allowed `enc` claims used to decrypt the JWT.\n- `typ` (string | nil): if non-null, ensure JWT claim `typ` matches the passed value.\n- `issuer` (string | nil): if non-null, ensure JWT claim `iss` matches the passed value.\n- `audiences` (string | table | nil): if non-null, ensure JWT claim `aud` matches one of the supplied values.\n- `subject` (string | nil): if non-null, ensure JWT claim `sub` matches the passed value.\n- `jwtid` (string | nil): if non-null, ensure JWT claim `jti` matches the passed value.\n- `ignore_not_before` (bool): If true, the JWT claim `nbf` will be ignored.\n- `ignore_expiration` (bool): If true, the JWT claim `exp` will be ignored.\n- `current_unix_timestamp` (datetime | nil): the JWT `nbf` and `exp` claims will be validated against this timestamp. If null,\n  will use the current datetime supplied by `ngx.time()`.\n- `timestamp_skew_seconds` (int):\n\nDefault values for `options` fields:\n```lua\nlocal decrypt_default_options = {\n    valid_encryption_alg_algorithms = {\n        [\"A128KW\"]=\"A128KW\", [\"A192KW\"]=\"A192KW\", [\"A256KW\"]=\"A256KW\",\n        [\"dir\"]=\"dir\",\n    },\n    valid_encryption_enc_algorithms = {\n        [\"A128CBC-HS256\"]=\"A128CBC-HS256\",\n        [\"A192CBC-HS384\"]=\"A192CBC-HS384\",\n        [\"A256CBC-HS512\"]=\"A256CBC-HS512\",\n        [\"A128GCM\"]=\"A128GCM\",\n        [\"A192GCM\"]=\"A192GCM\",\n        [\"A256GCM\"]=\"A256GCM\",\n    },\n    typ = nil,\n    issuer = nil,\n    audiences = nil,\n    subject = nil,\n    jwtid = nil,\n    ignore_not_before = false,\n    ignore_expiration = false,\n    current_unix_timestamp = nil,\n    timestamp_skew_seconds = 1,\n}\n```\n\nMinimal example with symmetric keys:\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ\"\nlocal decoded_token, err = jwt.decrypt(token, \"superSecretKey12\")\nif not decoded_token then\n    return nil, \"invalid jwt: \" .. err\nend\nprint(decoded_token.header.alg) -- A128KW\nprint(decoded_token.header.enc) -- A128CBC-HS256\nprint(decoded_token.payload.foo) -- bar\n```\n\nMinimal example with asymmetric keys:\n`TODO: not implemented`\n\nExamples with custom `options`:\n```lua\nlocal jwt = require(\"resty.jwt-verification\")\n\nlocal token = \"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ\"\nlocal decoded_token, err = jwt.decrypt(token, \"superSecretKey12\", {\n    valid_encryption_alg_algorithms = {[\"A128KW\"]=\"A128KW\"}, -- only allow A128KW family algs (requires OpenSSL 3.0+)\n    valid_encryption_enc_algorithms = {[\"A128CBC-HS256\"]=\"A128CBC-HS256\"}, -- only allow A128CBC family encs\n    audiences = {\"user\", \"admin\"}, -- `aud` must be one of the following\n    ignore_not_before = true -- ignore `nbf` claim (not recommended)\n})\nif not decoded_token then\n    return nil, \"invalid jwt: \" .. err\nend\nprint(decoded_token.header.alg) -- A128KW\nprint(decoded_token.header.enc) -- A128CBC-HS256\nprint(decoded_token.payload.foo) -- bar\n```\n\n## JWKS verification usage\n\nThe `resty.jwt-verification-jwks` module implements automatic JWKS retrieval from an HTTP endpoint and subsequent JWT\nvalidation with fetched keys.\n\nThe `resty.jwt-verification-jwks-cache-*` modules implement optional JWKS caching strategies. Only one caching strategy\ncan be enabled at a time; if none are enabled, the JWKS endpoint will be called once for every JWT to validate.\n\n### jwks.enable_cache_strategy_local\n\n**syntax**: *ok, err = jwks.enable_cache_strategy_local()*\n\nEnables the JWKS cache strategy using the OpenResty built-in [shared memory dictionaries](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#ngxshareddict).\n\nThis works on a per OpenResty instance and does not perform any network call on cache hit.\n\n```lua\nlocal jwks = require(\"resty.jwt-verification-jwks\")\n\nlocal ok, err = jwks.enable_cache_strategy_local()\nif not ok then\n    ngx.say(\"Error enable cache strategy: \", err)\nend\n```\n\n### jwks.set_http_timeouts_ms\n\n**syntax**: *jwks.set_http_timeouts_ms(connect, send, read)*\n\nSet HTTP client timeouts in milliseconds used for fetching JWKS.\n\n```lua\nlocal jwks = require(\"resty.jwt-verification-jwks\")\n\njwks.enable_cache_strategy_local(5000, 5000, 5000)\n```\n\n### jwks.set_http_ssl_verify\n\n**syntax**: *jwks.set_http_ssl_verify(enabled)*\n\nEnable/disable TLS verification used by HTTP client for fetching JWKS.\n\nBy default, all TLS certificates are verified. If the JWKS endpoint is using self-signed certificates, either add\nthe respective root CA to the OS certs store or disable certificates verification with this endpoint (it's unsafe).\n\n```lua\nlocal jwks = require(\"resty.jwt-verification-jwks\")\n\njwks.set_http_ssl_verify(false)\n```\n\n### jwks.fetch_jwks\n\n**syntax**: *payload, err = jwks.fetch_jwks(endpoint)*\n\nManually fetch JWKS from HTTP endpoint; the returned payload, in case of success, is the HTTP response body as string:\nNo check is performed whatsoever whether the payload contains JWKS or something else.\n\nIf a caching strategy has been enabled, the endpoint will try to fetch it from the cache first. After a cache miss and\nsuccessful JWKS retrieval via HTTP, the cache will be updated with the result.\n\n```lua\nlocal jwks = require(\"resty.jwt-verification-jwks\")\n\npayload, err = jwks.fetch_jwks(\"https://www.googleapis.com/oauth2/v3/certs\")\nif payload == nil then\n    print(\"failed fetching JWKS: \", err)\n    return\nend\nprint(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\"}]}'\n```\n\n### jwks.verify_jwt_with_jwks\n\n**syntax**: *jwt, err = jwks.verify_jwt_with_jwks(jwt_token, jwks_endpoint, jwt_options)*\n\nGiven a jwt_token as a string, verify its signature with JWKS provided by the HTTP service found at jwks_endpoint.\n\nOn success, the decrypted/verified JWT is returned as a lua table, otherwise nil and an error are returned.\n\nThe optional parameter `jwt_options` can be passed to configure the token validator when calling [jwt.verify](#jwtverify)\nafter having successfully fetched the JWKS. See [jwt.verify](#jwtverify) respective docs for more info about which options\ncan be passed.\n\n\u003e **Note**: As of this document, It's possible to verify any JWS using this method but no JWE: It's in the planned\n\u003e features to implement section.\n\n```lua\nlocal jwks = require(\"resty.jwt-verification-jwks\")\n\njwt, err = jwks.verify_jwt_with_jwks(\"\u003cMY_JWT\u003e\", \"http://myservice:8888/.well-known/jwks.json\", nil)\nif jwt == nil then\n    print(\"failed verifying jwt: \", err)\n    return\nend\nprint(jwt.header.alg)\nprint(tostring(jwt.payload))\n```\n\n## RFCs used as reference\n\n- [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) JSON Web Signature (JWS)\n- [RFC 7516](https://datatracker.ietf.org/doc/html/rfc7516) JSON Web Encryption (JWE)\n- [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517) JSON Web Key (JWK)\n- [RFC 7518](https://datatracker.ietf.org/doc/html/rfc7518) JSON Web Algorithms (JWA)\n- [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) JSON Web Token (JWT)\n- [RFC 7520](https://datatracker.ietf.org/doc/html/rfc7520) Examples of Protecting Content Using JSON Object Signing and Encryption (JOSE)\n\n## Run tests\n\n### Setup\n\nInstall test suit:\n```bash\nsudo cpan Test::Nginx\n```\n\nInstall openresty: see https://openresty.org/en/linux-packages.html\n\n### Run\n\n```bash\nexport PATH=/usr/local/openresty/nginx/sbin:$PATH\nprove -r t\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanvouk%2Flua-resty-jwt-verification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanvouk%2Flua-resty-jwt-verification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanvouk%2Flua-resty-jwt-verification/lists"}