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 503e9f5da fix(authz-casdoor): scope session cookie per Casdoor client
(#13387)
503e9f5da is described below
commit 503e9f5da472295fb5ee357d6777b67709434650
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Thu May 28 12:50:50 2026 +0800
fix(authz-casdoor): scope session cookie per Casdoor client (#13387)
---
apisix/plugins/authz-casdoor.lua | 29 ++++++-
t/plugin/authz-casdoor.t | 171 +++++++++++++++++++++++++++++++++++++++
2 files changed, 197 insertions(+), 3 deletions(-)
diff --git a/apisix/plugins/authz-casdoor.lua b/apisix/plugins/authz-casdoor.lua
index cbaa7b707..e5fd6f192 100644
--- a/apisix/plugins/authz-casdoor.lua
+++ b/apisix/plugins/authz-casdoor.lua
@@ -17,11 +17,16 @@
local core = require("apisix.core")
local http = require("resty.http")
local session = require("resty.session")
+local resty_sha256 = require("resty.sha256")
+local str = require("resty.string")
local ngx = ngx
local rand = math.random
local tostring = tostring
+local cookie_name_cache = {}
+
+
local plugin_name = "authz-casdoor"
local schema = {
type = "object",
@@ -45,6 +50,19 @@ local _M = {
schema = schema
}
+
+local function session_opts(conf)
+ local name = cookie_name_cache[conf.client_id]
+ if not name then
+ local sha256 = resty_sha256:new()
+ sha256:update(conf.client_id)
+ name = "authz_casdoor_session_" .. str.to_hex(sha256:final())
+ cookie_name_cache[conf.client_id] = name
+ end
+ return { cookie_name = name }
+end
+
+
local function fetch_access_token(code, conf)
local client = http.new()
local url = conf.endpoint_addr .. "/api/login/oauth/access_token"
@@ -93,7 +111,8 @@ end
function _M.access(conf, ctx)
local current_uri = ctx.var.uri
- local session_obj, sess_err, session_present = session.open()
+ local opts = session_opts(conf)
+ local session_obj, sess_err, session_present = session.open(opts)
-- step 1: check whether hits the callback
local m, err = ngx.re.match(conf.callback_url, ".+//[^/]+(/.*)", "jo")
if err or not m then
@@ -142,20 +161,24 @@ function _M.access(conf, ctx)
return 503
end
local session_obj_write = session.new {
+ cookie_name = opts.cookie_name,
cookie = {lifetime = lifetime}
}
session_obj_write:open()
session_obj_write:set("access_token", access_token)
+ session_obj_write:set("client_id", conf.client_id)
session_obj_write:save()
core.response.set_header("Location", original_url)
return 302
end
-- step 2: check whether session exists
- if not (session_present and session_obj:get("access_token")) then
+ if not (session_present
+ and session_obj:get("access_token")
+ and session_obj:get("client_id") == conf.client_id) then
-- session not exists, redirect to login page
local state = rand(0x7fffffff)
- local session_obj_write = session.start()
+ local session_obj_write = session.start(opts)
session_obj_write:set("original_uri", current_uri)
session_obj_write:set("state", state)
session_obj_write:save()
diff --git a/t/plugin/authz-casdoor.t b/t/plugin/authz-casdoor.t
index aef07facc..9a25d76d0 100644
--- a/t/plugin/authz-casdoor.t
+++ b/t/plugin/authz-casdoor.t
@@ -512,3 +512,174 @@ apisix:
--- response_body
3416238e1edf915eac08b8fe345b2b95cdba7e04
YUfqAO0kPXjZIoAbPSuryCkUDksEmwSq08UDTIUWolN6KQwEUrh72TazePueo4/S
+
+
+
+=== TEST 11: configure two routes with different client_id values
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+
+ local fake_uri = "http://127.0.0.1:10420"
+ local low_callback = "http://127.0.0.1:" .. ngx.var.server_port ..
+ "/low/callback"
+ local high_callback = "http://127.0.0.1:" .. ngx.var.server_port ..
+ "/high/callback"
+
+ local code, body = t('/apisix/admin/routes/11',
+ ngx.HTTP_PUT,
+ [[{
+ "methods": ["GET"],
+ "uri": "/low/*",
+ "plugins": {
+ "authz-casdoor": {
+ "callback_url":"]] .. low_callback .. [[",
+ "endpoint_addr":"]] .. fake_uri .. [[",
+ "client_id":"low-client",
+ "client_secret":"low-secret"
+ },
+ "proxy-rewrite": {
+ "uri": "/echo"
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "test.com:1980": 1
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.say("failed to set up low route")
+ return
+ end
+
+ local code, body = t('/apisix/admin/routes/12',
+ ngx.HTTP_PUT,
+ [[{
+ "methods": ["GET"],
+ "uri": "/high/*",
+ "plugins": {
+ "authz-casdoor": {
+ "callback_url":"]] .. high_callback .. [[",
+ "endpoint_addr":"]] .. fake_uri .. [[",
+ "client_id":"high-client",
+ "client_secret":"high-secret"
+ },
+ "proxy-rewrite": {
+ "uri": "/echo"
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "test.com:1980": 1
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.say("failed to set up high route")
+ return
+ end
+ ngx.say("done")
+ }
+ }
+--- response_body
+done
+
+
+
+=== TEST 12: session cookie scoped per client_id
+--- config
+ location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local log = core.log
+ local httpc = require("resty.http").new()
+
+ local base = "http://127.0.0.1:" .. ngx.var.server_port
+ local low_url = base .. "/low/data"
+ local high_url = base .. "/high/data"
+
+ -- step 1: unauthenticated GET on /low/data -> 302 to Casdoor for
low-client
+ local res1, err1 = httpc:request_uri(low_url, {method = "GET"})
+ if not res1 then
+ log.error(err1)
+ return
+ end
+ ngx.say("step1_status=", res1.status)
+ local loc1 = res1.headers["Location"] or ""
+ if ngx.re.find(loc1, "client_id=low-client", "jo") then
+ ngx.say("step1_to_low_client=yes")
+ else
+ ngx.say("step1_to_low_client=no loc=", loc1)
+ end
+
+ local pre_cookie = res1.headers["Set-Cookie"]
+ local m, err = ngx.re.match(loc1, "state=([0-9]*)", "jo")
+ if err or not m then
+ log.error(err or "no state")
+ return
+ end
+ local state = m[1]
+
+ -- step 2: complete the callback to establish a session for
low-client
+ local callback_url = base .. "/low/callback?code=aaa&state=" ..
state
+ local res2, err2 = httpc:request_uri(callback_url, {
+ method = "GET",
+ headers = {Cookie = pre_cookie}
+ })
+ if not res2 then
+ log.error(err2)
+ return
+ end
+ ngx.say("step2_status=", res2.status)
+ local post_cookie = res2.headers["Set-Cookie"]
+
+ -- step 3: reuse the post-login cookie against /high/data
+ -- (different client_id) -> must redirect for high-client
+ local res3, err3 = httpc:request_uri(high_url, {
+ method = "GET",
+ headers = {Cookie = post_cookie}
+ })
+ if not res3 then
+ log.error(err3)
+ return
+ end
+ ngx.say("step3_status=", res3.status)
+ local loc3 = res3.headers["Location"] or ""
+ if ngx.re.find(loc3, "client_id=high-client", "jo") then
+ ngx.say("step3_to_high_client=yes")
+ else
+ ngx.say("step3_to_high_client=no loc=", loc3)
+ end
+
+ -- Confirm the Set-Cookie names differ between routes, proving the
+ -- cookie-name scoping layer is active independent of the
in-session
+ -- client_id check.
+ local function cookie_name(set_cookie)
+ if type(set_cookie) == "table" then
+ set_cookie = set_cookie[1]
+ end
+ return set_cookie and set_cookie:match("^([^=]+)=")
+ end
+ local low_name = cookie_name(pre_cookie)
+ local high_name = cookie_name(res3.headers["Set-Cookie"])
+ if low_name and high_name and low_name ~= high_name then
+ ngx.say("cookie_names_differ=yes")
+ else
+ ngx.say("cookie_names_differ=no low=", tostring(low_name),
+ " high=", tostring(high_name))
+ end
+ }
+ }
+--- response_body
+step1_status=302
+step1_to_low_client=yes
+step2_status=302
+step3_status=302
+step3_to_high_client=yes
+cookie_names_differ=yes