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 b95627061c fix(openid-connect): validate introspection issuer (#13916)
b95627061c is described below

commit b95627061c0554267e7e037f3ff88b7a0f4300c3
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Fri Sep 11 18:36:15 2026 +0800

    fix(openid-connect): validate introspection issuer (#13916)
---
 apisix/plugins/openid-connect.lua          |  11 ++
 t/plugin/openid-connect-claim-validation.t | 159 +++++++++++++++++++++++++++++
 2 files changed, 170 insertions(+)

diff --git a/apisix/plugins/openid-connect.lua 
b/apisix/plugins/openid-connect.lua
index b4913a741c..e195b6244f 100644
--- a/apisix/plugins/openid-connect.lua
+++ b/apisix/plugins/openid-connect.lua
@@ -1084,6 +1084,17 @@ local function introspect(ctx, conf)
             return ngx.HTTP_UNAUTHORIZED, err, nil, nil
         end
 
+        local valid_issuers = core.table.try_read_attr(conf, "claim_validator",
+                                                        "issuer", 
"valid_issuers")
+        if valid_issuers and
+            (type(res.iss) ~= "string" or
+             not core.table.array_find(valid_issuers, res.iss)) then
+            local issuer_err = "issuer validation failed"
+            ngx.header["WWW-Authenticate"] = 'Bearer realm="' .. conf.realm ..
+                '", error="invalid_token", error_description="' .. issuer_err 
.. '"'
+            return ngx.HTTP_UNAUTHORIZED, issuer_err, nil, nil
+        end
+
         -- Token successfully validated and response from the introspection
         -- endpoint contains the userinfo.
         core.log.debug("token validate successfully by introspection")
diff --git a/t/plugin/openid-connect-claim-validation.t 
b/t/plugin/openid-connect-claim-validation.t
index 23c930cf91..63ccee040d 100644
--- a/t/plugin/openid-connect-claim-validation.t
+++ b/t/plugin/openid-connect-claim-validation.t
@@ -200,3 +200,162 @@ passed
 Bearer realm="apisix", error="invalid_token", error_description="issuer 
validation unavailable"$
 --- error_log
 OIDC access discovery url failed
+
+
+
+=== TEST 5: set up a route validating the introspection response issuer
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local json = require("toolkit.json")
+
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, 
json.encode({
+                uri = "/hello",
+                plugins = {
+                    ["openid-connect"] = {
+                        client_id = "apisix",
+                        client_secret = "secret",
+                        discovery = 
"http://127.0.0.1:16969/.well-known/openid-configuration";,
+                        introspection_endpoint = 
"http://127.0.0.1:16969/introspect";,
+                        introspection_endpoint_auth_method = 
"client_secret_post",
+                        bearer_only = true,
+                        claim_validator = {
+                            issuer = { valid_issuers = 
{"https://example.com/issuer"} },
+                        },
+                    },
+                },
+                upstream = {
+                    type = "roundrobin",
+                    nodes = { ["127.0.0.1:1980"] = 1 },
+                },
+            }))
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 6: an introspection response with an allowed issuer is accepted
+--- http_config
+    server {
+        listen 16969;
+        server_name localhost;
+
+        location /introspect {
+            default_type application/json;
+            return 200 '{"active":true,"iss":"https://example.com/issuer"}';
+        }
+    }
+--- request
+GET /hello HTTP/1.1
+--- more_headers
+Authorization: Bearer allowed-issuer
+--- error_code: 200
+
+
+
+=== TEST 7: an introspection response with another issuer is rejected
+--- http_config
+    server {
+        listen 16969;
+        server_name localhost;
+
+        location /introspect {
+            default_type application/json;
+            return 200 
'{"active":true,"iss":"https://other.example.com/issuer"}';
+        }
+    }
+--- request
+GET /hello HTTP/1.1
+--- more_headers
+Authorization: Bearer other-issuer
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Bearer realm="apisix", error="invalid_token", 
error_description="issuer validation failed"
+--- error_log
+OIDC introspection failed: issuer validation failed
+
+
+
+=== TEST 8: an introspection response without an issuer is rejected
+--- http_config
+    server {
+        listen 16969;
+        server_name localhost;
+
+        location /introspect {
+            default_type application/json;
+            return 200 '{"active":true}';
+        }
+    }
+--- request
+GET /hello HTTP/1.1
+--- more_headers
+Authorization: Bearer missing-issuer
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Bearer realm="apisix", error="invalid_token", 
error_description="issuer validation failed"
+--- error_log
+OIDC introspection failed: issuer validation failed
+
+
+
+=== TEST 9: set up an introspection route without an issuer allowlist
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local json = require("toolkit.json")
+
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, 
json.encode({
+                uri = "/hello",
+                plugins = {
+                    ["openid-connect"] = {
+                        client_id = "apisix",
+                        client_secret = "secret",
+                        discovery = 
"http://127.0.0.1:16969/.well-known/openid-configuration";,
+                        introspection_endpoint = 
"http://127.0.0.1:16969/introspect";,
+                        introspection_endpoint_auth_method = 
"client_secret_post",
+                        bearer_only = true,
+                    },
+                },
+                upstream = {
+                    type = "roundrobin",
+                    nodes = { ["127.0.0.1:1980"] = 1 },
+                },
+            }))
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 10: an issuer remains optional without an explicit allowlist
+--- http_config
+    server {
+        listen 16969;
+        server_name localhost;
+
+        location /introspect {
+            default_type application/json;
+            return 200 '{"active":true}';
+        }
+    }
+--- request
+GET /hello HTTP/1.1
+--- more_headers
+Authorization: Bearer missing-issuer
+--- error_code: 200

Reply via email to