Skip to content
Flag of Europe
Made in the European Union · Independently built · Released under EUPL 1.2

00.81.00

Available on Maven Central. A focused hardening release: session records finally age out (the SweepingSessionStore decorator persists the policy-true state and enforces retention), the OAuth2 login-CSRF surface is closed (CallbackStateBinding, CWE-352), empty security annotations now fail the build (CWE-863), token-propagation audience selection is pinned (CWE-522), and the wire-codec delegator announced in 00.80.00 is removed. No new module. The current release is 00.83.00.

Release date: 2026-08-31 Previous release: 00.80.00 Maven coordinates (parent): com.svenruppert.jsentinel:jSentinel-parent:00.81.00 Full changelog: GitHub release v00.81.00

A focused hardening release: session records finally age, the highest-priority open audit findings (Tier 1 + Tier 2) are closed, and the wire-codec delegator announced in 00.80.00 is executed. No new module, no new feature surface.

Five themes

  1. Session-lifecycle integrity — the reported bug “admin views show weeks-old sessions as ACTIVE” is closed: the new SweepingSessionStore decorator persists the policy-true state on every read and implements the retention promise of the SessionStore contract; a STRICT/PRODUCTION boot now flags store-backed sessions configured without any lifetime enforcement.
  2. OAuth2 login-CSRF (CWE-352) — the REST callback handler binds the single-use state to the user-agent fail-closed via the new CallbackStateBinding hook (__Host- cookie, constant-time compare).
  3. Processor compile guard (CWE-863) — empty security annotations (@RequiresPermission({}), …) now fail the build with processing/empty-security-annotation instead of only failing every call at request time.
  4. Propagation selection pinned (CWE-522) — the audience/strategy token-selection chain is verified and pinned by regression tests: no token ever reaches the wrong outbound audience, caches never cross-serve.
  5. Announced removal — the deprecated events.rest.EnvelopeWireCodec delegator is gone, one release after forRemoval = true, since = "00.80.00".

Headline change

Records used to stay ACTIVE forever unless an admin clicked Revoke. Now you wrap the store once, and every read shows and persists the policy-true state:

SessionStore store = new SweepingSessionStore(
    new InMemorySessionStore(),
    new TimeoutSessionPolicy<>(config, clock, audit));
// stale ACTIVE → persisted EXPIRED + one SessionExpired audit event
// terminal records past retention (default 30 days) → purged

And the callback side of the OAuth2 login flow now binds state to the browser before the flow runs:

// start side: bind the state to this browser
response.header("Set-Cookie", CallbackStateBinding.hostCookieHeader(state));
// callback side: fail-closed binding BEFORE the flow runs
new OAuth2CallbackHandler(flow, sink, CallbackStateBinding.hostCookie());

What’s new in detail

AreaChange
jSentinel-core (session)SweepingSessionStore decorator: per-record SessionPolicy.evaluate(...) on every read path; stale ACTIVE → persisted EXPIRED + exactly one SessionExpired; terminal records past lastActivityAt + retention (default 30 d) deleted. No background thread. Audit-reason literals got a single home (SessionExpired.REASON_IDLE_TIMEOUT / REASON_ABSOLUTE_LIFETIME).
jSentinel-dxNew bootstrap finding sessions/no-timeout-policy: ERROR in STRICT/PRODUCTION, INFO in dev modes.
jSentinel-oauth2-restCallbackStateBinding hook (hostCookie(), hostCookieHeader(...) with header-injection guards on value and name, unbound()); OAuth2CallbackHandler evaluates the binding fail-closed before the flow — a rejected callback does not consume the single-use state.
jSentinel-processorprocessing/empty-security-annotation compile ERROR for empty value arrays, blank entries and blank @RequiresPolicy names.
jSentinel-propagation-oidcRegression tests: audience travels into the RFC 8693 exchange and scopes the mint; the cache serves the same (subject, audience) only; subjects never share entries; a blank audience omits the parameter.
jSentinel-events-restDeprecated EnvelopeWireCodec delegator and its byte-identity pin test removed; zero references remain reactor-wide.

Additivity

Additive except three deliberate changes, all on misconfigured or opted-out setups: the announced events.rest.EnvelopeWireCodec removal (the authoritative events.wire.EnvelopeWireCodec and the wire format are unchanged); store-backed sessions without a lifetime policy now raise sessions/no-timeout-policy (ERROR in STRICT, INFO in dev); and empty security annotations that previously compiled and failed every call at runtime now fail the build.

Migrations

EnvelopeWireCodec: replace …events.rest.EnvelopeWireCodec / …events.rest.EventWireException imports with …events.wire.*; encode/decode and the wire format are byte-identical. Session stores: wrap the concrete store once in SweepingSessionStore; admin views need no change. OAuth2 REST callback: switch to the three-argument OAuth2CallbackHandler with CallbackStateBinding.hostCookie() and emit hostCookieHeader(state) on the start side; integrations that bind state themselves keep the two-argument constructor (now logged once).

Quality

No module fell more than 3 pp under its previous baseline. jSentinel-core held flat at 84 % (2253/2680); new PIT baselines recorded for jSentinel-oauth2-rest 86 %, jSentinel-processor 82 %, jSentinel-rest 94 % and jSentinel-vaadin 80 %. Every fix ships a no-mock regression test against the real implementation (real flow + HttpServer stubs, real stores, real policies, fixed clocks, compile-testing for the processor). Exit review: SHIP. Full reactor green. No mocks.

Roadmap

Next: 00.81.10 — the full rebranding jSentinel → jCustos (eu.jsentinel, jCustos-*), a feature-free cycle, prerequisite being the Sonatype Central namespace verification for eu.jsentinel. Then 00.82.00 as the first feature release under the new name.