This is an automated email from the ASF dual-hosted git repository.
shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new e2671c49b fix(dingtalk-auth): clear client-supplied X-Userinfo before
authentication (#13491)
e2671c49b is described below
commit e2671c49b615de8c7e1505228261285ba90f66aa
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Wed Jun 10 16:11:15 2026 +0800
fix(dingtalk-auth): clear client-supplied X-Userinfo before authentication
(#13491)
---
apisix/plugins/dingtalk-auth.lua | 3 +
t/plugin/dingtalk-auth.t | 139 +++++++++++++++++++++++++++++++++++++++
2 files changed, 142 insertions(+)
diff --git a/apisix/plugins/dingtalk-auth.lua b/apisix/plugins/dingtalk-auth.lua
index d4c4b07b0..56152d2df 100644
--- a/apisix/plugins/dingtalk-auth.lua
+++ b/apisix/plugins/dingtalk-auth.lua
@@ -215,6 +215,9 @@ end
function _M.rewrite(conf, ctx)
local userinfo, err
+ -- clear any client-supplied X-Userinfo before authentication
+ core.request.set_header(ctx, "X-Userinfo", nil)
+
local sess, sess_err = session.open(
{
secret = conf.secret,
diff --git a/t/plugin/dingtalk-auth.t b/t/plugin/dingtalk-auth.t
index 50c5ddcab..9908caf2f 100644
--- a/t/plugin/dingtalk-auth.t
+++ b/t/plugin/dingtalk-auth.t
@@ -67,6 +67,13 @@ add_block_preprocessor(sub {
}))
}
}
+
+ location /dt-echo {
+ content_by_lua_block {
+ -- echo back the received X-Userinfo header so tests can
assert it
+ ngx.say(ngx.req.get_headers()["x-userinfo"] or "none")
+ }
+ }
}
_EOC_
@@ -371,3 +378,135 @@ passed
]
--- error_code eval
[302, 200]
+
+
+
+=== TEST 14: client-supplied X-Userinfo is not forwarded to upstream
+--- config
+ location /t {
+ content_by_lua_block {
+ local http = require("resty.http")
+ local httpc = http.new()
+ local t = require("lib.test_admin").test
+ local forged =
ngx.encode_base64('{"userid":"admin","name":"forged"}')
+
+ -- restore route 1 to a clean config and obtain a legitimate cookie
+ local code = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "methods": ["GET"],
+ "upstream": {
+ "nodes": {"127.0.0.1:1980": 1},
+ "type": "roundrobin"
+ },
+ "plugins": {
+ "dingtalk-auth": {
+ "app_key": "testappkey",
+ "app_secret": "testappsecret",
+ "secret": "my-session-secret",
+ "access_token_url":
"http://127.0.0.1:10421/v1.0/oauth2/accessToken",
+ "userinfo_url":
"http://127.0.0.1:10421/topapi/v2/user/getuserinfo",
+ "redirect_uri": "/login"
+ }
+ },
+ "uri": "/hello"
+ }]]
+ )
+ assert(code <= 201, "setup route 1 failed: " .. tostring(code))
+
+ local base = "http://127.0.0.1:" .. ngx.var.server_port
+ local res, err = httpc:request_uri(base .. "/hello", {
+ method = "GET",
+ query = {code = "valid_code"},
+ })
+ assert(res, err)
+ assert(res.status == 200, "expected 200 on auth, got " ..
res.status)
+ local cookie = res.headers["Set-Cookie"]
+ assert(cookie, "expected Set-Cookie after auth")
+
+ -- forged X-Userinfo without a cookie must not bypass
authentication
+ local res1, err1 = httpc:request_uri(base .. "/hello", {
+ method = "GET",
+ headers = {["X-Userinfo"] = forged},
+ })
+ assert(res1, err1)
+ assert(res1.status == 302,
+ "forged X-Userinfo without cookie should redirect, got " ..
res1.status)
+
+ -- route with set_userinfo_header=false: upstream must receive no
X-Userinfo,
+ -- even when the client supplies a forged one alongside a valid
cookie
+ local code2 = t('/apisix/admin/routes/2',
+ ngx.HTTP_PUT,
+ [[{
+ "methods": ["GET"],
+ "upstream": {
+ "nodes": {"127.0.0.1:10421": 1},
+ "type": "roundrobin"
+ },
+ "plugins": {
+ "dingtalk-auth": {
+ "app_key": "testappkey",
+ "app_secret": "testappsecret",
+ "secret": "my-session-secret",
+ "access_token_url":
"http://127.0.0.1:10421/v1.0/oauth2/accessToken",
+ "userinfo_url":
"http://127.0.0.1:10421/topapi/v2/user/getuserinfo",
+ "set_userinfo_header": false,
+ "redirect_uri": "/login"
+ }
+ },
+ "uri": "/dt-echo-off"
+ }]]
+ )
+ assert(code2 <= 201, "setup route 2 failed: " .. tostring(code2))
+
+ local res2, err2 = httpc:request_uri(base .. "/dt-echo-off", {
+ method = "GET",
+ headers = {["Cookie"] = cookie, ["X-Userinfo"] = forged},
+ })
+ assert(res2, err2)
+ assert(res2.status == 200, "expected 200 on echo route, got " ..
res2.status)
+ assert(res2.body == "none\n",
+ "forged X-Userinfo must not reach upstream, got: " ..
(res2.body or "nil"))
+
+ -- route with set_userinfo_header=true: the forged value is
overwritten
+ -- with the verified user info, never forwarded as-is
+ local code3 = t('/apisix/admin/routes/3',
+ ngx.HTTP_PUT,
+ [[{
+ "methods": ["GET"],
+ "upstream": {
+ "nodes": {"127.0.0.1:10421": 1},
+ "type": "roundrobin"
+ },
+ "plugins": {
+ "dingtalk-auth": {
+ "app_key": "testappkey",
+ "app_secret": "testappsecret",
+ "secret": "my-session-secret",
+ "access_token_url":
"http://127.0.0.1:10421/v1.0/oauth2/accessToken",
+ "userinfo_url":
"http://127.0.0.1:10421/topapi/v2/user/getuserinfo",
+ "set_userinfo_header": true,
+ "redirect_uri": "/login"
+ }
+ },
+ "uri": "/dt-echo-on"
+ }]]
+ )
+ assert(code3 <= 201, "setup route 3 failed: " .. tostring(code3))
+
+ local res3, err3 = httpc:request_uri(base .. "/dt-echo-on", {
+ method = "GET",
+ headers = {["Cookie"] = cookie, ["X-Userinfo"] = forged},
+ })
+ assert(res3, err3)
+ assert(res3.status == 200, "expected 200 on echo route, got " ..
res3.status)
+ assert(res3.body ~= forged .. "\n",
+ "forged X-Userinfo must be overwritten, got: " .. (res3.body
or "nil"))
+ assert(res3.body ~= "none\n",
+ "verified X-Userinfo should be set, got: " .. (res3.body or
"nil"))
+
+ ngx.say("passed")
+ }
+ }
+--- response_body
+passed