Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jwt-auth): store JWT in the request context #11675

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apisix/plugins/jwt-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ local schema = {
hide_credentials = {
type = "boolean",
default = false
},
store_in_ctx = {
type = "boolean",
default = false
}
},
}
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/en/latest/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions docs/zh/latest/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) 资源。

Expand Down
186 changes: 186 additions & 0 deletions t/plugin/jwt-auth4.t
Original file line number Diff line number Diff line change
@@ -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
Loading