You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
help request: how to debug weird user info returned/altered in the OpenID connect plugin? #13440
Works, a base64 encoded user info arrives at the client. However, something has changed inside. The original response from the user info endpoint was (according to their support):
{"sub": "a UID", "name": "Testuser One", "roles": []}
but after base64 decoding the data from the header on the client I see this:
{"sub": "a UID", "name": "Testuser One", "roles": {}}
Note the empty array roles has changed to an object.
In order to debug this I would need to follow the entire chain of moving parts, starting with the OpenID connect plugin. Is there any way to print out to the log the data received from the user info endpoint?
Environment
APISIX version (run apisix version): 3.16.0
Operating system (run uname -a): official docker image
OpenResty / Nginx version (run openresty -V or nginx -V): 1.27.1.2
etcd version, if relevant (run curl http://127.0.0.1:9090/v1/server_info):
APISIX Dashboard version, if relevant:
Plugin runner version, for issues related to plugin runners:
LuaRocks version, for installation issues (run luarocks --version):
From the code path, response-rewrite should not be the place where the JSON structure is changed. It only resolves $http_x_userinfo and copies that request header value to the response header.
The X-Userinfo header is set earlier by the openid-connect plugin. The plugin gets userinfo / response.user from lua-resty-openidc, JSON-encodes it, base64-encodes it, and then sets it as the X-Userinfo request header. To verify what APISIX receives directly from the IDP userinfo endpoint, please temporarily set the APISIX error log level to debug in a test environment, reproduce the request once, and check error.log for the userinfo response: ... line. That log is emitted after lua-resty-openidc calls the userinfo endpoint and before APISIX re-encodes the value into X-Userinfo.
Please be careful because userinfo may contain sensitive user data. I would not recommend enabling debug logging for a long time in production, and please redact sensitive fields before sharing logs.
Could you please provide the following details?
The full openid-connect plugin configuration, with secrets/tokens redacted.
Whether this request uses authorization code flow, bearer token introspection, or JWKS validation.
The userinfo response: ... line from debug logs.
The base64-decoded X-Userinfo value for the same request.
If the debug log already shows roles as {}, APISIX is receiving it in that form from the IDP. If the debug log shows roles as [] but the decoded X-Userinfo shows {}, then we can continue investigating JSON decode/encode preservation in APISIX / lua-resty-openidc.
The route is in front of the portal web server, the IDP is configured for authorization code workflow. Token will be stored in the session cookie (I know it makes the cookie large, so I might change that to Redis in the future).
Thanks for the debug log @ronnybremer, that pinned it down. Confirmed: this is a real bug. It is not in response-rewrite or in the plugin logic. The array gets lost during the session cookie round trip.
How the bug occurs, step by step
Your IDP returns "roles": [].
lua-resty-openidc decodes this JSON into a Lua table. APISIX configures the JSON library to put a special "this is an array" tag on decoded arrays (core/json.lua:35). So at this point roles is an empty table tagged as an array. This is why your debug log still shows [].
The user object is saved into your session cookie. The tag survives, the cookie still contains "roles":[].
On the next request, lua-resty-session decodes the cookie back into a Lua table. Problem: it uses its own private copy of the JSON library (utils.lua:248), and that copy does not apply the array tag. So roles comes back as a plain empty table.
Here is the core issue: in Lua, an empty array and an empty object are the exact same thing, an empty table {}. The only way to tell them apart is that tag, and step 4 just dropped it.
The plugin now encodes this untagged empty table into the X-Userinfo header (openid-connect.lua:804). The JSON encoder has to guess, and its default for an empty table is object. So you get "roles":{}.
Impact
Only empty arrays break. Non-empty arrays like ["admin"] have contents that make them recognizable as arrays, so they are fine.
Happens with any session storage (cookie, Redis, shm), they all use the same serializer.
X-ID-Token claims are affected the same way.
Fix and workarounds
The proper fix is in lua-resty-session: its JSON decoder should tag arrays too. We will raise this upstream and bump the dependency once fixed.
Until then:
Treat {} and [] both as "empty" in your client/upstream code.
Thank you @shreemaan-abhishek for the detailed analysis and for finding the bug :) The IDP is outside of my control, but I will configure Rust serde in the meantime to handle both an array and an object.
Activity
Baoyuantop commentedon May 27, 2026
From the code path,
response-rewriteshould not be the place where the JSON structure is changed. It only resolves$http_x_userinfoand copies that request header value to the response header.The
X-Userinfoheader is set earlier by theopenid-connectplugin. The plugin getsuserinfo/response.userfromlua-resty-openidc, JSON-encodes it, base64-encodes it, and then sets it as theX-Userinforequest header. To verify what APISIX receives directly from the IDP userinfo endpoint, please temporarily set the APISIX error log level todebugin a test environment, reproduce the request once, and checkerror.logfor theuserinfo response: ...line. That log is emitted afterlua-resty-openidccalls the userinfo endpoint and before APISIX re-encodes the value intoX-Userinfo.Please be careful because userinfo may contain sensitive user data. I would not recommend enabling debug logging for a long time in production, and please redact sensitive fields before sharing logs.
Could you please provide the following details?
openid-connectplugin configuration, with secrets/tokens redacted.userinfo response: ...line from debug logs.X-Userinfovalue for the same request.If the debug log already shows
rolesas{}, APISIX is receiving it in that form from the IDP. If the debug log showsrolesas[]but the decodedX-Userinfoshows{}, then we can continue investigating JSON decode/encode preservation in APISIX / lua-resty-openidc.ronnybremer commentedon May 27, 2026
Done that, here seems to be the reply received from the user_info endpoint:
So the roles is still an array at that point. However, it arrives at the web browser as:
My environment:
Running APISIX as an ingress controller in a Kubernetes cluster.
OpenID Connect plugin config:
The route is in front of the portal web server, the IDP is configured for authorization code workflow. Token will be stored in the session cookie (I know it makes the cookie large, so I might change that to Redis in the future).
Thank you for your help!
shreemaan-abhishek commentedon Jun 2, 2026
Thanks for the debug log @ronnybremer, that pinned it down. Confirmed: this is a real bug. It is not in
response-rewriteor in the plugin logic. The array gets lost during the session cookie round trip.How the bug occurs, step by step
"roles": [].lua-resty-openidcdecodes this JSON into a Lua table. APISIX configures the JSON library to put a special "this is an array" tag on decoded arrays (core/json.lua:35). So at this pointrolesis an empty table tagged as an array. This is why your debug log still shows[]."roles":[].lua-resty-sessiondecodes the cookie back into a Lua table. Problem: it uses its own private copy of the JSON library (utils.lua:248), and that copy does not apply the array tag. Sorolescomes back as a plain empty table.{}. The only way to tell them apart is that tag, and step 4 just dropped it.X-Userinfoheader (openid-connect.lua:804). The JSON encoder has to guess, and its default for an empty table is object. So you get"roles":{}.Impact
["admin"]have contents that make them recognizable as arrays, so they are fine.X-ID-Tokenclaims are affected the same way.Fix and workarounds
The proper fix is in
lua-resty-session: its JSON decoder should tag arrays too. We will raise this upstream and bump the dependency once fixed.Until then:
{}and[]both as "empty" in your client/upstream code.ronnybremer commentedon Jun 2, 2026
Thank you @shreemaan-abhishek for the detailed analysis and for finding the bug :) The IDP is outside of my control, but I will configure Rust serde in the meantime to handle both an array and an object.
test(openid-connect): guard empty userinfo claims against session rou…
fix(patch): make cjson instances inherit APISIX's cjson options
fix(patch): make cjson instances inherit APISIX's cjson options