This is an automated email from the ASF dual-hosted git repository.

nic-6443 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 993ca77d7 fix(authz-casdoor): expire the session when the Casdoor 
token expires (#13500)
993ca77d7 is described below

commit 993ca77d7df9f968b8565e4c5a152c00cbc45e2c
Author: Nic <[email protected]>
AuthorDate: Thu Jun 11 09:58:07 2026 +0800

    fix(authz-casdoor): expire the session when the Casdoor token expires 
(#13500)
---
 apisix/plugins/authz-casdoor.lua |  17 ++++--
 t/plugin/authz-casdoor.t         | 108 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 5 deletions(-)

diff --git a/apisix/plugins/authz-casdoor.lua b/apisix/plugins/authz-casdoor.lua
index e5fd6f192..a9c100dac 100644
--- a/apisix/plugins/authz-casdoor.lua
+++ b/apisix/plugins/authz-casdoor.lua
@@ -22,6 +22,7 @@ local str = require("resty.string")
 local ngx = ngx
 local rand = math.random
 local tostring = tostring
+local tonumber = tonumber
 
 
 local cookie_name_cache = {}
@@ -94,11 +95,12 @@ local function fetch_access_token(code, conf)
                "failed when accessing token: no access_token contained"
     end
     -- In the reply of casdoor, setting expires_in to 0 indicates that the 
access_token is invalid.
-    if not data.expires_in or data.expires_in == 0 then
+    local expires_in = tonumber(data.expires_in)
+    if not expires_in or expires_in <= 0 then
         return nil, nil, "failed when accessing token: invalid access_token"
     end
 
-    return data.access_token, data.expires_in, nil
+    return data.access_token, expires_in, nil
 end
 
 
@@ -162,20 +164,25 @@ function _M.access(conf, ctx)
         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)
+        -- lua-resty-session 4.x no longer honors the old cookie.lifetime 
option,
+        -- so bind the session to the access token's expiry explicitly and 
enforce
+        -- it when the session is reused (see step 2 below).
+        session_obj_write:set("access_token_expires_at", ngx.time() + lifetime)
         session_obj_write:save()
         core.response.set_header("Location", original_url)
         return 302
     end
 
-    -- step 2: check whether session exists
+    -- step 2: check whether a valid, unexpired session exists
+    local token_expires_at = session_present and 
session_obj:get("access_token_expires_at")
     if not (session_present
             and session_obj:get("access_token")
-            and session_obj:get("client_id") == conf.client_id) then
+            and session_obj:get("client_id") == conf.client_id
+            and (not token_expires_at or token_expires_at > ngx.time())) then
         -- session not exists, redirect to login page
         local state = rand(0x7fffffff)
         local session_obj_write = session.start(opts)
diff --git a/t/plugin/authz-casdoor.t b/t/plugin/authz-casdoor.t
index 9a25d76d0..f73edd748 100644
--- a/t/plugin/authz-casdoor.t
+++ b/t/plugin/authz-casdoor.t
@@ -45,6 +45,12 @@ add_block_preprocessor(sub {
                     return
                 end
 
+                if arg == "shortexp" then
+                    ngx.status = 200
+                    ngx.say(json_encode({ access_token = "cccccccccccccccc", 
expires_in = 2 }))
+                    return
+                end
+
                 ngx.status = 200
                 ngx.say(json_encode({ access_token = "aaaaaaaaaaaaaaaa", 
expires_in = 1000000 }))
             }
@@ -683,3 +689,105 @@ step2_status=302
 step3_status=302
 step3_to_high_client=yes
 cookie_names_differ=yes
+
+
+
+=== TEST 13: route whose Casdoor token has a short expires_in
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local fake_uri = "http://127.0.0.1:10420";
+            local callback_url = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                    "/shortexp/callback"
+            local code, body = t('/apisix/admin/routes/13',
+                ngx.HTTP_PUT,
+                [[{
+                    "methods": ["GET"],
+                    "uri": "/shortexp/*",
+                    "plugins": {
+                        "authz-casdoor": {
+                            "callback_url":"]] .. callback_url .. [[",
+                            "endpoint_addr":"]] .. fake_uri .. [[",
+                            "client_id":"7ceb9b7fda4a9061ec1c",
+                            
"client_secret":"3416238e1edf915eac08b8fe345b2b95cdba7e04"
+                        },
+                        "proxy-rewrite": {
+                            "uri": "/echo"
+                        }
+                    },
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "test.com:1980": 1
+                        }
+                    }
+                }]]
+            )
+            if code >= 300 then
+                ngx.say("failed to set up routing rule")
+            end
+            ngx.say("done")
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 14: session expires when the Casdoor token's expires_in elapses
+--- config
+    location /t {
+        content_by_lua_block {
+            local log = require("apisix.core").log
+            local httpc = require("resty.http").new()
+            local base = "http://127.0.0.1:"; .. ngx.var.server_port
+
+            -- step 1: unauthenticated request -> redirect to Casdoor, get 
state cookie
+            local res1 = httpc:request_uri(base .. "/shortexp/d", {method = 
"GET"})
+            if not res1 or res1.status ~= 302 then
+                ngx.say("step1 expected 302, got: ", res1 and res1.status or 
"nil")
+                return
+            end
+            local pre_cookie = res1.headers["Set-Cookie"]
+            local m = ngx.re.match(res1.headers["Location"] or "", 
"state=([0-9]*)", "jo")
+            if not m then
+                ngx.say("no state in redirect")
+                return
+            end
+
+            -- step 2: complete the callback; Casdoor returns expires_in=2,
+            -- establishing the access-token session
+            local res2 = httpc:request_uri(
+                base .. "/shortexp/callback?code=shortexp&state=" .. m[1],
+                {method = "GET", headers = {Cookie = pre_cookie}})
+            if not res2 or res2.status ~= 302 then
+                ngx.say("step2 expected 302, got: ", res2 and res2.status or 
"nil")
+                return
+            end
+            local post_cookie = res2.headers["Set-Cookie"]
+
+            -- step 3: the fresh session is valid -> request is proxied (200)
+            local res3 = httpc:request_uri(base .. "/shortexp/d",
+                {method = "GET", headers = {Cookie = post_cookie}})
+            if not res3 or res3.status ~= 200 then
+                ngx.say("step3 expected 200, got: ", res3 and res3.status or 
"nil")
+                return
+            end
+
+            -- step 4: after expires_in (2s) the session must be rejected and 
the
+            -- request redirected back to Casdoor for re-authentication
+            ngx.sleep(3)
+            local res4 = httpc:request_uri(base .. "/shortexp/d",
+                {method = "GET", headers = {Cookie = post_cookie}})
+            if not res4 or res4.status ~= 302 then
+                ngx.say("step4 expected 302 after expiry, got: ", res4 and 
res4.status or "nil")
+                return
+            end
+
+            ngx.say("passed")
+        }
+    }
+--- timeout: 15
+--- response_body
+passed

Reply via email to