feat(openid-connect): update session config to support lua-resty-session, fixes deprecated session.cookie.lifetime#13178
Conversation
4.x Replace the deprecated `session.cookie.lifetime` property with the full set of lua-resty-session 4.x configuration options: cookie settings (cookie_name, cookie_path, cookie_domain, cookie_same_site, etc.), timeout controls (idling_timeout, rolling_timeout, absolute_timeout), remember/persistent session support, and additional options like audience, hash_storage_key, and store_metadata. BREAKING CHANGE: `session.cookie.lifetime` has been removed. Use `idling_timeout`, `rolling_timeout`, and `absolute_timeout` instead.
|
Hi @francescodedomenico, thank you for your contribution, but we need to evaluate whether it’s necessary to expose all the underlying library’s configurations. I’ll discuss this with the other maintainers. I look forward to hearing others’ thoughts. |
idling_timeout, updated plugin configuration in openid-connect.t to reflect new config schema
|
I would like to chip into this discussion. Prior to lua-resty-session 4.x we could use the nginx variables to setup the various features from lua-resty-session which were not exposed directly through openid-connect plugin in Apisix. After 4.x version, due to dependency chain of openid-connect apisix plugin -> lua-resty-openidc -> lua-resty-session it is only possible to configure those features by configuration which is sent from openid-connect plugin. Effectively, the limited schema capabilities are now blocking us from using the features that exist in lua-resty-session. lua-resty-session supports Redis with sentinel configuration -> https://github.com/bungle/lua-resty-session/blob/master/README.md#redis-sentinels-configuration I have tried to play around with it for a bit, and I've managed to create custom docker image from apisix:3.16.0 with lua-resty-redis-connector installed, adjusted the schema for openid-connect to support sentinel properties and were able to successfully connect to Redis sentinel. The setup is still being tested but so far looks good. |
|
Hi @francescodedomenico, another issue with the current PR is that the |
|
hello @Baoyuantop
I did add a migration note in the PR, the closest valid alternative for session.cookie.lifetime is the absolute_timeout parameter (gathered from the lifetime description of the 3.10 resty session). I think it would be more maintainable if we continue to support 1:1 mapping rather than keeping a deprecated parameter and to map it internally. However if you tell me where I am supposed to also add the migration notes I will add them in a docs: commit.
I do agree that many parameters could be added in an session.cookie.additionalProperties field and describe the field as being mapped against the resty session configuration. However I would like to suggest to at least expose the:
These two are a game changer for the user, we can have multiple oidc sessions, avoid "session" cookie conflict and have specific sessions being forwarded only to specific cookie-path. Let me know, thank you. Edit: fixed my answers being inglobated in quotes. |
Looking forward to the code update for further review. |
object Restore session.cookie as a nested object with explicit name, path, and lifetime properties that map to lua-resty-session 4.x cookie_name, cookie_path, and absolute_timeout. Allow additional boolean/number/string properties under session.cookie to be passed through to lua-resty-session as-is, avoiding schema churn when the upstream library adds new options. Restores backward compatibility for the session.cookie.lifetime field that was removed in the previous change.
|
hello @Baoyuantop
thank you |
|
The updated approach aligns well with our earlier discussion — clean schema with explicit One concern about key conflict in Suggestion — add a conflict warning or precedence rule, e.g.: local cookie = session_conf.cookie
if cookie then
for k, v in pairs(cookie) do
local mapped = cookie_key_map[k]
local target_key = mapped or k
if mapped and cookie[target_key] then
core.log.warn("session.cookie: both '", k, "' and '", target_key,
"' are set; using '", k, "' (mapped to '", target_key, "')")
end
opts[target_key] = v
end
endAlternatively, document clearly that users should not set both a mapped alias and its corresponding flat key simultaneously. |
|
@Baoyuantop I would like to point out that this addresses only cookie properties in the session object. Would it make sense to allow additional options to entire session object ( to support the rest of the storage options which are already exposed by lua-resty-session ) and existing session.redis to support redis cluster/sentinels? All configuration options from lua-resty-session -> here |
conflicts When both an explicit alias (name/path/lifetime) and its underlying lua-resty-session key (cookie_name/cookie_path/absolute_timeout) are set under session.cookie, aliases now always take precedence regardless of pairs() iteration order. A warning is logged on conflict.
|
@kovasaurus while I do believe it would be a great addition to support other storage options I think this should be addressed in a whole different PR -> I'd like not to broad the scope of this one. I have noticed that the previous workflow had a couple of jobs failed but they didn't seem related to openid-connect.lua to me. |
The previous merge of master into feat/oidc-session-cookie accepted ours for the EN openid-connect.md attribute table, dropping the re-port improvements from apache#13247 (a3c4e8c) while keeping only the branch session.cookie.* additions. Restore master's table and re-inject the session.cookie.{name,path,lifetime,<other>} rows and the updated session.cookie description. The ZH docs were merged correctly and are unchanged.
|
Since these functionalities would also be quite important for us, i was wondering what the timeline looks like. Is there any plan in place or any indication of when a release can be expected? |
|
Thanks for picking this up — fixing The core idea: expose the lua-resty-session 4.x options flat, under their real names, and enumerate them explicitly instead of using session = {
type = "object",
properties = {
secret = { type = "string", minLength = 16 },
-- lua-resty-session 4.x cookie options, named exactly as the library expects
cookie_name = { type = "string" },
cookie_path = { type = "string" },
cookie_domain = { type = "string" },
cookie_secure = { type = "boolean" },
cookie_http_only = { type = "boolean" },
cookie_same_site = { type = "string", enum = {"Strict", "Lax", "None", "Default"} },
-- session timeouts
idling_timeout = { type = "integer" },
rolling_timeout = { type = "integer" },
absolute_timeout = { type = "integer" },
-- deprecated: kept for backward compatibility, mapped to absolute_timeout at runtime
cookie = {
type = "object",
properties = {
lifetime = { type = "integer" },
},
},
storage = { ... }, -- unchanged
redis = { ... }, -- unchanged
},
required = {"secret"}
}The only thing that needs runtime translation is the legacy On dropping the
The tradeoff is that adding a new option later means a one-line schema addition, but that's cheap and keeps the surface honest about what the bundled library supports. Pick whichever cookie/timeout keys you think cover the common cases — the list above is a starting point, not exhaustive. If you update the PR along these lines I'm happy to review. Does this direction work for you? |
|
@francescodedomenico thanks a lot for taking this on — it's an important fix, the broken |
|
@nic-6443 sounds great! We are currently preparing for a large scale rollout and have chosen APISIX. For security reasons we would very much like to use this feature (it has effectively become a requirement for us at this point :-)). If there is any way i can help or contribute, i would be happy to do so. |
|
I started working on this because I really needed it myself (especially being able to declare different OIDC clients and forward cookies only to specific subpaths). My initial take was to directly expose the resty session configuration in order to take full advantage of the third party library, in my opinion apisix should not have an opinionated approach on how this library is used by mapping/remapping it's configuration. That said, I changed the initial implementation due to the @Baoyuantop suggestion that also makes sense, deprecating completely the session.lifetime and exposing through the schema resty.session configuration would definitely be a breaking change, meaning that this won't reach Apisix until the 4.x release (if apisix uses semantic versioning). In order to avoid a breaking change I suggest to expose the resty.session parameters like you suggested, keep the session.lifetime parameter and marking it as deprecated for the 3.x and to remove it completely when apisix goes 4.x. This PR needs at least 3 reviewer also, before I move on I would love to reach some kind of consensus, I am not sure who else can/should express what kind of approach we want to follow. |
|
@francescodedomenico glad you're keen on this — I think we're closer than it sounds, but let me clear up one thing, because I suspect a version-number mix-up. When I wrote "3.x" and "4.x" I meant the lua-resty-session library versions, not APISIX versions. #12862 bumped the bundled lua-resty-session from And the key point: the approach I proposed is not a breaking change — it's purely additive, so it can ship in APISIX 3.x. Nothing needs to wait for a hypothetical APISIX 4.0:
Concretely, both of these stay valid. Existing config — still works, and {
"session": {
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK",
"cookie": { "lifetime": 7200 }
}
}New config — the flat lua-resty-session 4.x keys: {
"session": {
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK",
"cookie_name": "oidc_session",
"cookie_path": "/app",
"cookie_same_site": "Strict",
"cookie_secure": true,
"absolute_timeout": 7200,
"idling_timeout": 600
}
}On your point that APISIX shouldn't be opinionated about how the library is used — I actually agree, and that's exactly why I suggested the flat names: they're lua-resty-session's own config keys, with no remapping or aliasing in between. The only difference from a full Once we agree on this shape, I'll help bring in the other maintainers to review so the PR doesn't stall on the three-reviewer requirement. Does this clear up the breaking-change concern? If so, I think you're good to update the PR along these lines. |
|
@francescodedomenico one more thing — no pressure either way: if you have the bandwidth, this is your PR and I'm happy to just review and help it land. But if you're stretched thin, I'm glad to pick up the implementation and carry it forward with you so it doesn't stall — your work and authorship stay on it, I'd just help push it over the line. A few users here clearly need this, so let's make sure it lands one way or another. Just let me know which you'd prefer. |
Restructure session config per @nic-6443 review on apache#13178: instead of nested session.cookie.* aliases plus an additionalProperties pass-through, expose lua-resty-session 4.x option names directly under session (cookie_name, cookie_path, cookie_domain, cookie_secure, cookie_http_only, cookie_same_site, idling_timeout, rolling_timeout, absolute_timeout). Keys match what resty.session consumes, so values pass through untouched. Drop the alias map and the two-pass merge/precedence logic in build_session_opts. The only remaining runtime translation is the legacy session.cookie.lifetime: it stays accepted (deprecated), is mapped to absolute_timeout when absolute_timeout is unset, and logs a deprecation warning. When absolute_timeout is set, it wins. session.cookie is restricted to { lifetime } with additionalProperties=false; unknown keys (under cookie or directly under session) are rejected by the schema instead of being silently dropped — the failure mode that caused apache#13178 in the first place. Purely additive vs master: existing { session: { cookie: { lifetime } } } configs keep validating and now actually take effect.
|
@nic-6443 I am on board with this, and luckily I had time to update the PR, like you I am interested in having this fixed for my personal and professional projects. PR and description updated, the code diff from main is very little tbh , I hope review won't be difficult, let me know! |
Co-authored-by: Nic <[email protected]>
- Drop unused `local pairs = pairs` (luacheck warning surfaced by CI
after build_session_opts no longer iterates the config table).
- Drop additionalProperties=false from session.cookie schema: rejecting
previously-allowed extra fields under cookie has no benefit and is a
needless breaking change for existing configs.
- TEST 5: assert cookie.lifetime stays in the returned table now that
build_session_opts mutates session_conf in place instead of building
a new opts table.
- Remove TEST 9 (unknown key under session.cookie rejection) — no
longer enforced after dropping additionalProperties=false.
- Renumber subsequent tests 10–13 -> 9–12.
- TEST 11: fix regex to match the actual jsonschema error
("additional properties forbidden").
- TEST 12: drop leftover `session.cookie: both` line from the earlier
alias implementation, which was never emitted by the current code.
|
thank you for the review @nic-6443 -> I have applied your recomendations and I did bootstrap devcontainers to run the tests locally, I'll let you to mark the open conversation as resolved if you think they are good |
…ion, fixes deprecated session.cookie.lifetime (apache#13178)
Description
APISIX bundles
lua-resty-session 4.1.5, but theopenid-connectplugin's session schema still exposed the legacysession.cookie.lifetimeproperty from the 3.x API. Sincelua-resty-openidcpasses the session configuration as-is toresty.session.start(), only properties recognized by the 4.x library actually take effect — socookie.lifetimewas silently ignored at runtime.This PR fixes
session.cookie.lifetimeby mapping it to lua-resty-session 4.x'sabsolute_timeout, so the value users set actually affects session expiry. The session schema is redesigned to expose lua-resty-session 4.x options flat, under their real names, directly atsession.*, so the values pass straight through toresty.session.start()without any translation layer.Why does it matter
With this update users have full control of the OIDC sticky session, allowing them to:
session.cookie_pathlua-resty-session4.x cookie and timeout options (security flags, SameSite, idling/rolling/absolute timeouts) with explicit, validated keysNew schema design
Session options are flat under
session, using the exact key names thatlua-resty-session4.x consumes — no aliasing, no remapping, no pass-through. The schema is closed (additionalProperties: false), so an unknown or misspelled key fails validation instead of being silently dropped — which is exactly the failure mode that causedcookie.lifetimeto stop working in the first place.session.cookie_namesession.cookie_pathsession.cookie_domainsession.cookie_secureSecurecookie attributesession.cookie_http_onlyHttpOnlycookie attributesession.cookie_same_siteStrict/Lax/None/Default)SameSitecookie attributesession.idling_timeoutsession.rolling_timeoutsession.absolute_timeoutsession.cookie.lifetimesession.absolute_timeoutExample using the new flat keys:
{ "session": { "secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK", "cookie_name": "oidc_session", "cookie_path": "/app", "cookie_secure": true, "cookie_same_site": "Strict", "idling_timeout": 600, "absolute_timeout": 7200 } }Since the keys match what
resty.session.start()already consumes, the plugin simply forwards them — no alias map, nobuild_session_opts()translation table, no precedence rules.Fix for
session.cookie.lifetimePrior to this PR,
session.cookie.lifetimewas accepted by the schema but silently ignored at runtime — lua-resty-session 4.x no longer recognizes the nestedcookie.lifetimekey. This PR keepssession.cookie.lifetimeaccepted (marked deprecated) and maps it toabsolute_timeoutat runtime whenabsolute_timeoutis not set, so existing configurations actually take effect. A one-line deprecation warning is logged so users can migrate. If bothsession.absolute_timeoutandsession.cookie.lifetimeare set,absolute_timeoutwins — no warning, since the flat key is the documented path.Backward compatibility
This change is purely additive and ships in APISIX 3.x:
session.cookie.lifetimestays accepted (deprecated) and now actually takes effect — that's the bug fix.cookie_name,cookie_secure,idling_timeout, …) are new optional properties.Existing configurations such as:
{ "session": { "secret": "...", "cookie": { "lifetime": 7200 } } }continue to validate and now correctly affect session expiry.
Why explicit keys instead of an
additionalPropertiespass-throughlua-resty-sessionis a pinned dependency, not a fast-moving external API. Listing supported options explicitly:cookie.lifetimebug).redisblock in this very plugin,limit-count's redis schema).Adding a new option in the future is a one-line schema addition.
Files changed
apisix/plugins/openid-connect.lua— flat session schema using lua-resty-session 4.x key names;additionalProperties: falseon bothsessionand the deprecatedsession.cookie;build_session_opts()reduced to a shallow copy plus the legacycookie.lifetime → absolute_timeoutmapping with deprecation warningdocs/en/latest/plugins/openid-connect.md— flat-key documentation with deprecation note forsession.cookie.lifetimedocs/zh/latest/plugins/openid-connect.md— Chinese documentation updated to matcht/plugin/openid-connect10.t— 13 schema and unit tests covering flat-key validation,cookie.lifetimedeprecation warning,absolute_timeout-wins precedence, unknown-key rejection (under bothsessionandsession.cookie), and invalidcookie_same_siterejectionAcknowledgements
@nic-6443 and @Baoyuantop for pre-review and planning.
Which issue(s) this PR fixes:
Fixes #13177
Checklist