Skip to content

feat(security): Spring Security 6 parity — Tier 1 authentication spine (26.6.30)#38

Merged
ancongui merged 7 commits into
mainfrom
feat/spring-security-tier1-auth-spine
Jun 19, 2026
Merged

feat(security): Spring Security 6 parity — Tier 1 authentication spine (26.6.30)#38
ancongui merged 7 commits into
mainfrom
feat/spring-security-tier1-auth-spine

Conversation

@ancongui

Copy link
Copy Markdown
Contributor

Spring Security 6 parity — Tier 1: the authentication spine

The core of Spring Security's authentication architecture — the foundation later
tiers build on. Additive: no behaviour change to existing code (the default
build and all existing suites are untouched). Release 26.6.30.

Follows Tier 0 (#37). See the updated Spring Security Parity book appendix.

Added

  • Authentication manager spineAuthenticationManager / ProviderManager
    / AuthenticationProvider; AuthenticationRequest (UsernamePassword /
    BearerToken, #[non_exhaustive]); BearerTokenAuthenticationProvider adapts
    the existing Verifier into the spine.
  • UserDetails + DAO authenticationUserDetails (four account-status
    flags), UserDetailsService, UserDetailsChecker /
    AccountStatusUserDetailsChecker, InMemoryUserDetailsService, and an
    enumeration-safe DaoAuthenticationProvider.
  • DelegatingPasswordEncoder — Spring's {id}-prefixed storage
    ({bcrypt}/{argon2}/{noop}) with upgrade_encoding re-hash-on-login and
    legacy bare-hash migration; NoOpPasswordEncoder.
  • SecurityContextRepository — pluggable between-request context store
    (HttpSessionSecurityContextRepository default, NullSecurityContextRepository
    stateless); SessionAuthenticationLayer now loads through a swappable repo.
  • AuthenticationEventPublisher — success/failure events from ProviderManager.
  • Pluggable AuthenticationEntryPoint / AccessDeniedHandler — the
    ExceptionTranslationFilter seam on FilterChain (defaults to problem+json).

Verification

  • Per-batch TDD; firefly-security 94 → 113 lib tests + all integration
    suites green; clippy + cargo fmt --check clean; reactive-banking sample
    e2e gate green (no regression from the FilterChain / session_auth changes).
  • A 4-dimension adversarial review of the diff surfaced 15 findings; the
    substantive one (a not-found timing-mitigation degradation under a
    misconfigured encoder) is fixed, test gaps are closed, and the by-design /
    refinement findings are documented as Known limitations in the CHANGELOG.

🤖 Generated with Claude Code

Andres Contreras added 7 commits June 19, 2026 12:27
AuthenticationManager / ProviderManager / AuthenticationProvider — the Rust
analog of Spring's authentication architecture. AuthenticationRequest is a
#[non_exhaustive] enum (UsernamePassword / BearerToken) standing in for Spring's
pre-auth token; ProviderManager tries each supporting provider in order, first
success wins, provider-not-found otherwise. BearerTokenAuthenticationProvider
adapts the existing Verifier so JWT/JWKS verifiers slot into the spine.

+5 tests; security lib 99 green; clippy + fmt clean.
Spring's recommended {id}-prefixed password storage: DelegatingPasswordEncoder
hashes with a default encoder and prefixes {id} ({bcrypt}$2b$…), verify()
reads the {id} and delegates, and upgrade_encoding() flags a stored hash for
re-encoding on next login when its {id} differs from the default or it is a
legacy unprefixed hash. with_defaults() = {bcrypt} default + {argon2} + {noop},
with bare/legacy hashes verified as bcrypt for seamless migration.
NoOpPasswordEncoder ({noop}) for tests/dev.

+5 tests; security lib 104 green; clippy + fmt clean.
UserDetails (stored credential + the four Spring account-status flags),
UserDetailsService (load_user_by_username -> Option), UserDetailsChecker +
AccountStatusUserDetailsChecker (locked/disabled/expired), InMemoryUserDetails-
Service, and DaoAuthenticationProvider — an AuthenticationProvider for the
UsernamePassword request that loads the user, runs the status checks, verifies
via a PasswordEncoder, and checks credentials expiry. Enumeration-safe: an
unknown user and a wrong password both fail as 'Bad credentials' with comparable
encoder work. Plugs into the ProviderManager spine alongside the bearer provider.

+4 tests; security lib 108 green; clippy + fmt clean.
SecurityContextRepository (load/save/contains) — Spring's pluggable
between-request context store — with HttpSessionSecurityContextRepository
(default; configurable session key, wire-compatible with the OAuth2/OTT/WebAuthn
handlers) and NullSecurityContextRepository (stateless). SessionAuthenticationLayer
now loads the context through a (swappable) repository instead of a hardcoded
session key; added Authentication::is_authenticated(). Restore-semantics tests
moved to the repository module.

+4 repo tests; full security suite (107 lib + integration) green; clippy + fmt clean.
…T1.5)

AuthenticationEventPublisher (AuthenticationEvent::Success/Failure) wired into
ProviderManager — publishes every authentication outcome (with the attempted
username on failure); LoggingAuthenticationEventPublisher default. Completes the
authentication-manager spine.

ExceptionTranslationFilter seam: AuthenticationEntryPoint (401) + AccessDenied-
Handler (403) traits with the canonical problem+json defaults; FilterChain gains
with_authentication_entry_point / with_access_denied_handler so a deployment can
customise the rejection (login redirect, WWW-Authenticate, etc.).

+2 tests; full security suite (109 lib + integration) green; clippy + fmt clean.
… version 26.6.30

CHANGELOG 26.6.30 (Tier 1: AuthenticationManager spine, UserDetails+DAO,
DelegatingPasswordEncoder, SecurityContextRepository, auth events, pluggable
entry-point/access-denied). Spring Security Parity appendix (EN+ES) marks the
authentication-spine row + roadmap tier as done; designed book rebuilt (EN+ES).
Workspace bumped to 26.6.30.
- [MED] DaoAuthenticationProvider: the not-found timing-mitigation dummy hash
  now falls back to a valid bcrypt hash if the configured encoder's hash()
  fails, so the user-enumeration timing equalization can't silently collapse to
  an empty (instant-fail) hash under a misconfigured encoder.
- Test gaps closed: SecurityContextRepository seam on SessionAuthenticationLayer
  (NullSecurityContextRepository swap), Authentication::is_authenticated, DAO
  backend-error propagation, HttpSession typed-object load fallback, and the
  bearer (username:None) failure event.
- Documented the remaining by-design/refinement findings (ProviderManager
  account-status short-circuit, upgrade_encoding {id}-only, {noop}/unprefixed
  defaults) as Known limitations in the 26.6.30 CHANGELOG.

+4 tests; full security suite (113 lib + integration) green; clippy + fmt clean.
@ancongui ancongui merged commit d9394ed into main Jun 19, 2026
4 checks passed
@ancongui ancongui deleted the feat/spring-security-tier1-auth-spine branch June 19, 2026 11:29
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.

1 participant