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