Session Policy
A session that lives forever is a security risk; a session that gets
killed mid-request is a UX disaster. SessionPolicy lets the
application define both kinds of limits in one place, and it lets the
adapters enforce them consistently.
Konzept-V00.60.00.md.
Delivered 2026-05-10; demos opt in via SPI; session-id rotation on
login landed 2026-05-11.The contract
public interface SessionPolicy<U> {
SessionDecision evaluate(SessionMetadata metadata);
default boolean rotateSessionAfterLogin() { return true; }
}SessionDecision is sealed:
public sealed interface SessionDecision {
record Continue() implements SessionDecision {}
record Invalidate(String reason) implements SessionDecision {}
}SessionMetadata carries:
subjectIdcreatedAt,lastActiveAtidleTimeout,absoluteTimeoutremoteAddress(when the adapter has it)
The policy is a pure query — it doesn’t mutate, it doesn’t reach into transport-specific state. Adapters call it before honouring a session and act on the result.
Defaults
NoopSessionPolicy— alwaysContinue. Useful for tests.TimeoutSessionPolicy— checks both idle and absolute limits, returnsInvalidate(reason)when either is exceeded. Reason strings are stable so audit consumers can rely on them ("idle-timeout","absolute-timeout").
Session-id rotation after login
After a successful login, the policy’s rotateSessionAfterLogin()
hook (default true) triggers
VaadinService.reinitializeSession(...) — a fresh session id, new
servlet session, but the same Vaadin UI state preserved through the
re-initialization. This is the session-fixation defence: an
attacker who got hold of the pre-login session id can no longer use it
after login.
Implementation lives in the Vaadin adapter (commit 0f53cd5, B3 —
“honour SessionDecision.Invalidate from onLogin via session-id
rotation”).
Vaadin enforcement
The SessionLifetimeListener in security-vaadin consults the policy
on every Vaadin event that touches a session. On Invalidate(reason):
- The reason is published as
SessionExpiredorSessionInvalidatedon the audit pipeline. - The Vaadin session is closed; the HTTP session is invalidated.
- The user is redirected to the login route via the existing logout listener chain — same code path as a manual logout.
REST enforcement
RestAuthorizationFilter consults SessionPolicy.evaluate(...) once
the subject is resolved from the request. On Invalidate(reason) the
filter returns 401 Unauthorized and (where applicable) revokes the
bearer token through the logout listener chain.
A REST client receiving 401 mid-conversation knows to re-authenticate.
Configuration
Like the brute-force policy, TimeoutSessionPolicy reads its limits
through a …ConfigurationLoader with the sysprop → env → default
chain:
| System property | Env var | Default |
|---|---|---|
security.session.idle-timeout | SECURITY_SESSION_IDLE_TIMEOUT | PT30M |
security.session.absolute-timeout | SECURITY_SESSION_ABSOLUTE_TIMEOUT | PT8H |
security.session.rotate-after-login | SECURITY_SESSION_ROTATE_AFTER_LOGIN | true |
Plugging in your own
public final class MyTenantAwareSessionPolicy implements SessionPolicy<MyUser> {
@Override
public SessionDecision evaluate(SessionMetadata m) {
if (tenants.isSuspended(m.subjectId().tenantId())) {
return new SessionDecision.Invalidate("tenant-suspended");
}
return defaultPolicy.evaluate(m);
}
}Register in META-INF/services/com.svenruppert.vaadin.security.session.SessionPolicy.
Both adapters pick it up via SecurityServiceResolver.