Skip to content

chore(deps): use api7-lua-resty-session to fix empty arrays in OIDC userinfo#13680

Open
AlinsRan wants to merge 3 commits into
apache:masterfrom
AlinsRan:chore/bump-lua-resty-session-api7
Open

chore(deps): use api7-lua-resty-session to fix empty arrays in OIDC userinfo#13680
AlinsRan wants to merge 3 commits into
apache:masterfrom
AlinsRan:chore/bump-lua-resty-session-api7

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #13440

The shared cjson instance in `lua-resty-session` (`lib/resty/session/utils.lua`) decodes JSON without the array metatable. An empty array therefore loses its array identity across the session save/load cycle and is re-encoded as an object.

With `openid-connect`, userinfo is stored in the session and read back on subsequent requests, so an empty claim such as `roles` comes back as a plain empty table and `core.json.encode` renders it as an object:

IdP userinfo : {"sub":"a UID","name":"Testuser One","roles":[]}
X-Userinfo   : {"sub":"a UID","name":"Testuser One","roles":{}}   <- roles became an object

Note the corruption is not in openid-connect or in openidc's own decode: core/json.lua enables decode_array_with_array_mt(true) on the shared cjson.safe singleton, which openidc reuses, so the array survives into the session. It is lost on the way out, in lua-resty-session's own cjson.safe.new() instance, which does not have that option set.

Fix

Switch the dependency to api7-lua-resty-session = 4.1.6-0, which enables decode_array_with_array_mt on that instance (upstream PR: bungle/lua-resty-session#207).

lua-resty-openidc (>= 4.0.3) and lua-resty-saml (= 4.1.5-1) still declare the upstream lua-resty-session, so it is still installed transitively. That is fine: LuaRocks gives the canonical module path to the lexicographically smaller rock name (luarocks/repos.lua, name < cur_name), so api7-lua-resty-session owns resty.session.* and the upstream copy is relegated to a versioned directory that nothing requires. This is exactly the arrangement already used by api7-lua-resty-http and api7-lua-resty-jwt, which coexist with the upstream rocks pulled in by openidc.

Verification

Resolving the dependency set (api7-lua-resty-session + lua-resty-openidc + lua-resty-saml) into a clean tree:

installed    : api7-lua-resty-session, lua-resty-openidc, lua-resty-saml, lua-resty-session
canonical    : share/lua/5.1/resty/session/utils.lua  -> has decode_array_with_array_mt
relegated    : share/lua/5.1/lua_resty_session_4_1_5_1-resty/   (upstream, unreferenced)

Replaying the userinfo round-trip through that tree:

require resty.openidc : ok
session stored        : {"roles":[],"sub":"a UID","name":"Testuser One"}
X-Userinfo            : {"roles":[],"sub":"a UID","name":"Testuser One"}

The result is independent of install order, since the winner is decided by rock name rather than by installation sequence.

…serinfo

The shared cjson instance in lua-resty-session decodes JSON without the
array metatable, so an empty array loses its array identity across the
session save/load cycle and is re-encoded as an object. With the
openid-connect plugin this turns empty userinfo claims such as `roles`
into `{}`, and `X-Userinfo` carries `"roles":{}` instead of `"roles":[]`.

Switch to api7-lua-resty-session 4.1.6-0, which enables
decode_array_with_array_mt on that instance.

lua-resty-openidc and lua-resty-saml still depend on the upstream
lua-resty-session, so it is still pulled in transitively, but
api7-lua-resty-session sorts before it and therefore owns the
resty.session.* module paths -- the same arrangement already used for
api7-lua-resty-http and api7-lua-resty-jwt.

Fixes apache#13440
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. dependencies Pull requests that update a dependency file labels Jul 9, 2026
…nd-trip

Reproduces the apache#13440 chain without an IdP: core.json.decode (as openidc
parses userinfo) -> session encode/decode -> core.json.encode (as the plugin
builds X-Userinfo). With the old lua-resty-session the last step yields
`{}`; the test therefore fails if the dependency is reverted.
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jul 9, 2026
…ycle

Replace the direct session_utils check with an end-to-end test: a request
is routed through the openid-connect plugin to an upstream that echoes the
headers it received, and the test asserts the empty `roles` claim arrives
in X-Userinfo as `[]`.

The session is forged with the plugin's own secret so the plugin takes the
already-authenticated path and restores the userinfo from the session --
the same path every request takes after the callback, and the one where
the empty array used to be lost. A non-expired access token plus
renew_access_token_on_expiry=false keep openidc from reaching for the
discovery document, so no IdP is needed.

Against the old lua-resty-session the upstream receives `"roles":{}`.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

help request: how to debug weird user info returned/altered in the OpenID connect plugin?

1 participant