Skip to content

5.1.1 — Post-login redirect hardening

Choose a tag to compare

@devondragon devondragon released this 24 Jul 17:05
· 92 commits to main since this release

This release hardens post-login redirect handling so browser auto-probes (e.g., Safari's /apple-touch-icon.png) can no longer hijack where users land after authentication, and it auto-unprotects common probe paths so they no longer bounce unauthenticated users to the login page. No endpoints, response-code contracts, or user.* properties changed.

SemVer classification: patch — security hardening and small defaults-only behavior adjustments; no new public API and no source/binary-incompatible changes for consuming applications.

Installation

Gradle:

implementation 'com.digitalsanctuary:ds-spring-user-framework:5.1.1'

Maven:

<dependency>
    <groupId>com.digitalsanctuary</groupId>
    <artifactId>ds-spring-user-framework</artifactId>
    <version>5.1.1</version>
</dependency>

Built and verified against Spring Boot 4.1.0 (compatible with 4.0.x), Java 21+.

Security

  • Hardened, consumer-overridable RequestCache to prevent post-login redirect hijacking:
    • A new default RequestCache bean is contributed by UserSecurityBeansAutoConfiguration (@ConditionalOnMissingBean). It uses HttpSessionRequestCache with a stricter matcher that saves only plausible user navigations:
      • GET requests that explicitly accept text/html (Accept: */* is ignored — real navigations list text/html).
      • Not XHR (X-Requested-With: XMLHttpRequest), not HTMX (HX-Request), and not static-asset/auto-probe paths.
      • Excludes well-known probe/static paths: /apple-touch-icon*, /favicon*, /.well-known/, and common static file extensions (png, ico, css, js, fonts, etc.).
    • WebSecurityConfig now wires that RequestCache into http.requestCache(...), and LoginSuccessService reads from the same injected RequestCache, ensuring the save and read sides are consistent — so the user is redirected back to the original protected page they clicked, not to an auto-probed icon URL.
    • Consumer impact:
      • If you want Spring Security's default (save-anything) semantics, define your own RequestCache bean; the framework will back off and use yours in both the filter chain and LoginSuccessService.
      • If you previously relied on saving non-HTML, XHR/HTMX, or asset fetches to the saved request, explicitly override the RequestCache to restore that behavior.

Behavior changes (client impact)

  • getUnprotectedURIsList() now auto-unprotects browser/crawler probe paths for every consumer:
    • /apple-touch-icon*.png, /favicon.*, and /.well-known/** are added to the permitAll list by default (least-privilege patterns, narrowly scoped).
    • Under user.security.defaultAction=deny, unauthenticated requests to these probe URLs will now reach your app (typically 200 if you serve the asset, or 404 if you don't) instead of 302-redirecting to the login page. This reduces noisy, misleading login redirects triggered by the browser while the login page is rendering.
    • If your application intentionally kept any of these paths protected, define your own SecurityFilterChain bean to fully control the permitAll surface.

Fixes

  • Post-login redirect correctness: SavedRequestAwareAuthenticationSuccessHandler (via LoginSuccessService) now reads from the same RequestCache instance the filter chain writes to, so a consumer override is honored end-to-end and the redirect target can't be silently lost or replaced by a browser probe.

Testing

  • Added RequestCacheHardeningTest proving that an HTML navigation survives an /apple-touch-icon.png probe on the same session, and that probes/partials (HTMX/XHR), non-HTML requests, and POSTs are not saved.
  • Added WebSecurityAuthorizationDenyTest coverage for the new auto-unprotected probe paths (asserting 404 rather than a 302-to-login when they aren't listed in configuration).
  • Stabilized parallel tests: introduced a thread-local StatementCountInspector to isolate N+1 query-count assertions from concurrent tests, and de-flaked a warning-count assertion in UserAPIUnitTest.

Other Changes

  • build.gradle: bumped org.hibernate.validator:hibernate-validator to 9.1.2.Final for tests.
  • Release tooling: generate_changelog.py hardened (never blocks a release); no impact on runtime behavior for consumers.

Full Changelog: 5.1.0...5.1.1