From 26e3a3791fe6c80a1613f22cb512763c4f08195a Mon Sep 17 00:00:00 2001 From: Michele Righi Date: Wed, 23 Oct 2024 14:17:34 +0200 Subject: [PATCH 1/3] fix(jwt-auth): store token in request ctx Fixes #11281 Signed-off-by: Michele Righi --- apisix/plugins/jwt-auth.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 740efcdc6b4e..5b3df40b82ec 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -48,6 +48,10 @@ local schema = { hide_credentials = { type = "boolean", default = false + }, + store_in_ctx = { + type = "boolean", + default = false } }, } @@ -274,6 +278,10 @@ function _M.rewrite(conf, ctx) return 401, {message = "failed to verify jwt"} end + if conf.store_in_ctx then + ctx.jwt_obj = jwt_obj + end + consumer_mod.attach_consumer(ctx, consumer, consumer_conf) core.log.info("hit jwt-auth rewrite") end From 817c784db2afde1ecf4849fdf0bb9fd36de04a47 Mon Sep 17 00:00:00 2001 From: Michele Righi Date: Wed, 23 Oct 2024 16:26:14 +0200 Subject: [PATCH 2/3] test(jwt-auth): add test case for new feature Add new test case (jwt-auth4.t) to test the correct behaviour of `store_in_ctx` config parameter Signed-off-by: Michele Righi --- t/plugin/jwt-auth4.t | 186 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 t/plugin/jwt-auth4.t diff --git a/t/plugin/jwt-auth4.t b/t/plugin/jwt-auth4.t new file mode 100644 index 000000000000..99fb402fffcc --- /dev/null +++ b/t/plugin/jwt-auth4.t @@ -0,0 +1,186 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +BEGIN { + $ENV{VAULT_TOKEN} = "root"; +} + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + if (!$block->response_body) { + $block->set_value("response_body", "passed\n"); + } + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: add consumer with username and plugins +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "jack", + "plugins": { + "jwt-auth": { + "key": "user-key", + "secret": "my-secret-key" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } + + + +=== TEST 2: enable jwt auth plugin using admin api with default config +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": {}, + "serverless-post-function": { + "phase": "rewrite", + "functions": [ + "return function(conf, ctx) + if ctx.jwt_obj then + ngx.status = 200 + ngx.say(\"JWT found in ctx. Payload key: \" .. ctx.jwt_obj.payload.key) + return ngx.exit(200) + else + ngx.status = 500 + ngx.say(\"JWT not found in ctx.\") + return ngx.exit(500) + end + end" + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/jwt-auth-no-ctx" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } + + + +=== TEST 3: enable jwt auth plugin using admin api with custom parameter +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": { + "store_in_ctx": true + }, + "serverless-post-function": { + "phase": "rewrite", + "functions": [ + "return function(conf, ctx) + if ctx.jwt_obj then + ngx.status = 200 + ngx.say(\"JWT found in ctx. Payload key: \" .. ctx.jwt_obj.payload.key) + return ngx.exit(200) + else + ngx.status = 500 + ngx.say(\"JWT not found in ctx.\") + return ngx.exit(500) + end + end" + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/jwt-auth-ctx" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } + + + +=== TEST 4: verify (header with bearer) +--- request +GET /jwt-auth-no-ctx +--- more_headers +Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsIm5iZiI6MTcyNzI3NDk4M30.N6ebc4U5ms976pwKZ_iQ88w_uJKqUVNtTYZ_nXhRpWo +--- error_code: 500 +--- response_body +JWT not found in ctx. + + + +=== TEST 5: verify (header with bearer) +--- request +GET /jwt-auth-ctx +--- more_headers +Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsIm5iZiI6MTcyNzI3NDk4M30.N6ebc4U5ms976pwKZ_iQ88w_uJKqUVNtTYZ_nXhRpWo +--- error_code: 200 +--- response_body +JWT found in ctx. Payload key: user-key From d994762cbae4b1d772dc84e1ecc1414d06af6720 Mon Sep 17 00:00:00 2001 From: Michele Righi Date: Wed, 23 Oct 2024 16:34:49 +0200 Subject: [PATCH 3/3] docs(jwt-auth): update en/zh docs Added the new config parameter in jwt-auth docs Signed-off-by: Michele Righi --- docs/en/latest/plugins/jwt-auth.md | 1 + docs/zh/latest/plugins/jwt-auth.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index a3522efe730a..bf9dc906d7fc 100644 --- a/docs/en/latest/plugins/jwt-auth.md +++ b/docs/en/latest/plugins/jwt-auth.md @@ -58,6 +58,7 @@ For Route: | query | string | False | jwt | The query string to get the token from. Lower priority than header. | | cookie | string | False | jwt | The cookie to get the token from. Lower priority than query. | | hide_credentials | boolean | False | false | Set to true will not pass the authorization request of header\query\cookie to the Upstream.| +| store_in_ctx | boolean | False | false | Set to true will store the JWT in the request context. This allows lower-priority plugins that run afterwards on the same request to retrieve and use the JWT token. | You can implement `jwt-auth` with [HashiCorp Vault](https://www.vaultproject.io/) to store and fetch secrets and RSA keys pairs from its [encrypted KV engine](https://developer.hashicorp.com/vault/docs/secrets/kv) using the [APISIX Secret](../terminology/secret.md) resource. diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md index 88065cb50cd9..38d98087c55e 100644 --- a/docs/zh/latest/plugins/jwt-auth.md +++ b/docs/zh/latest/plugins/jwt-auth.md @@ -58,6 +58,7 @@ Route 端: | query | string | 否 | jwt | 设置我们从哪个 query string 获取 token,优先级低于 header。 | | cookie | string | 否 | jwt | 设置我们从哪个 cookie 获取 token,优先级低于 query。 | | hide_credentials | boolean | 否 | false | 该参数设置为 `true` 时,则不会将含有认证信息的 header\query\cookie 传递给 Upstream。| +| store_in_ctx | boolean | 否 | false | 设置为 `true` 会将 JWT 存储在请求上下文中。这允许在同一请求上随后运行的低优先级插件检索和使用 JWT 令牌。 | 您可以使用 [HashiCorp Vault](https://www.vaultproject.io/) 实施 `jwt-auth`,以从其[加密的 KV 引擎](https://developer.hashicorp.com/vault/docs/secrets/kv) 使用 [APISIX Secret](../terminology/secret.md) 资源。