Skip to content

feat(*): Revocation store for stateless cookie sessions - #209

Open
Hockenba wants to merge 10 commits into
bungle:masterfrom
geico:feat/stateless-session-revocation
Open

feat(*): Revocation store for stateless cookie sessions#209
Hockenba wants to merge 10 commits into
bungle:masterfrom
geico:feat/stateless-session-revocation

Conversation

@Hockenba

@Hockenba Hockenba commented Aug 10, 2026

Copy link
Copy Markdown

Continues #208 (source branch changed; see comment there).

Summary

Adds optional storage-backed session revocation for stateless (cookie) sessions.

  • On session:open, check whether the session ID is in the denylist.
  • On session:destroy, write a lightweight sentinel with TTL matching remaining session lifetime (no session payload stored).

Revocation is only available when storage is nil or "cookie". Select a backend explicitly with revocation = "dshm", "file", "memcached", "mysql", "postgres", "redis", or "shm" (custom storage modules and pre-built set/get tables are also supported). Backends use their normal configuration section. Leave revocation unset or set revocation = false to disable.

revocation_fail_mode defaults to "open" (store unreachable → treat as not revoked; destroy still clears the cookie). "closed" rejects open/destroy when the store is unavailable.

Revocation applies to full destroy / single-audience logout. It does not apply to session:save rotation or partial multi-audience logout.

Consumer: apache/apisix#13651.

Test plan

  • make unit / busted spec/06-revocation-1_spec.lua spec/07-revocation-2_spec.lua
  • spec/06-revocation-1_spec.lua
    • Backends: file, shm, redis, memcached
    • Normal use: open, destroy+revoke, save rotation (no revoke), cookie-only destroy
    • Store: SET/GET, missing key, TTL expiry
    • Configuration: cookie storage with revocation backend, non-cookie storage skip, revocation=false
  • spec/07-revocation-2_spec.lua
    • Backends: mysql, postgres, Redis Sentinel/Cluster, dshm
    • Fail modes: default open, open destroy degrade, closed open reject, closed destroy fail
    • Validation: default revocation_fail_mode, invalid revocation type/value, invalid fail mode
    • Connection failures on SET/GET

Hockenba and others added 10 commits June 26, 2026 15:19
Drop redundant open tests and post-open revocation mutation; exercise
revocation via redis.mode and real Redis integration where possible.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reorder happy-path tests first, dedupe overlap, and group fail-mode coverage.

@bungle bungle left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some early comments.

Comment thread README.md
Comment on lines +331 to +332
| `revocation` | `nil` | Storage used for cookie session revocation records. Use `nil` or `false` to disable, a storage name such as `"shm"`, `"redis"`, `"mysql"`, or `"postgres"`, a custom storage module name, or a storage `table` with `set`/`get` methods. |
| `revocation_fail_mode` | `"open"` | Behavior when the revocation store is unreachable, use `"open"` (treat as not revoked) or `"closed"` (reject the session). |

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking aloud, should we move these inside cookie storage configuration, see:
https://github.com/bungle/lua-resty-session#cookie-storage-configuration

Thus here we should have a new

Option Default Description
cookie nil Configuration for cookie storage, e.g. { revocation_storage = "redis", revocation_fail_mode = "open" } (see below)

Or do you think we may want to use revocation with non-stateless cookies too later, see my comment below about mass revocations?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking aloud, should we move these inside cookie storage configuration, see: https://github.com/bungle/lua-resty-session#cookie-storage-configuration

Thus here we should have a new

Option Default Description
cookie nil Configuration for cookie storage, e.g. { revocation_storage = "redis", revocation_fail_mode = "open" } (see below)
Or do you think we may want to use revocation with non-stateless cookies too later, see my comment below about mass revocations?

I think we should keep revocation and revocation_fail_mode at the top level rather than nesting them under a cookie storage config.

There isn’t much benefit to coupling them to cookie storage. Revocation is more of a session-lifecycle policy than cookie-backend configuration, and nesting it would make it harder later if we want the same mechanism for non-stateless cookies, headers, or other storages — especially with mass revocations in mind. Reversing that later would be awkward for compatibility.

So I’d leave the options top-level, with the current behavior that they only apply when using cookie storage. But it is up to you ultimately of course.

Comment thread README.md
Comment on lines +364 to +365
On every `session:open`, the library checks whether the session identifier is
revoked. On `session:destroy`, the identifier is written to the selected

@bungle bungle Aug 11, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering that should we also support mass revocations. E.g. revoke all sessions that are created before this timestamp, revoke all sessions belonging to this subject created before this timestamp etc. Revoke all sessions created for this audience before this timestamp, etc. Not that you need to implement all that, but just that we don't paint us in a corner with this. Non-stateless version of this is store_metadata option that keeps track of sid, aud, subject (making it possible to selective delete sessions, e.g. all sessions by subject).

@Hockenba Hockenba Aug 11, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering that should we also support mass revocations. E.g. revoke all sessions that are created before this timestamp, revoke all sessions belonging to this subject created before this timestamp etc. Revoke all sessions created for this audience before this timestamp, etc. Not that you need to implement all that, but just that we don't paint us in a corner with this. Non-stateless version of this is store_metadata option that keeps track of sid, aud, subject (making it possible to selective delete sessions, e.g. all sessions by subject).

Good idea — I’d treat mass revocations (by time, subject, audience, etc.) as a possible future feature rather than something to design into this PR.

For cookie/stateless sessions, per-sid revocation is enough for now. Bulk filters are a separate and more complex than just a simple 1 or 0 concern, and would likely lean on something like store_metadata (or richer revocation records) when/if someone wants them.

As long as we keep revocation keyed by session id and don’t hard-code assumptions against richer filters later, we’re not painting ourselves into a corner. No need to over-engineer for that here and now when it could be done in the future, so we don't balloon the scope of this PR too big.

@Hockenba Hockenba changed the title feat(*): Redis-backed revocation for stateless cookie sessions feat(*): Revocation for stateless cookie sessions Aug 11, 2026
@Hockenba Hockenba changed the title feat(*): Revocation for stateless cookie sessions feat(*): Revocation store for stateless cookie sessions Aug 11, 2026
@Hockenba
Hockenba requested a review from bungle August 11, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants