Skip to content

Model user.security.* as typed @ConfigurationProperties (retire scattered @Value; add template-safe access) #355

Description

@devondragon

Problem

user.security.* is the only framework config area still wired as ~40 scattered @Value injections across 14 classes — every other area (MFA, WebAuthn, Captcha, GDPR, Audit, DevLogin, Roles) is already a typed @ConfigurationProperties. Consequences:

  • Defaults are split three ways and already drift: config/dsspringuserconfig.properties, inline @Value(":default") fragments, and 48 hand-maintained entries in additional-spring-configuration-metadata.json. Two live disagreements today: bcryptStrength (inline 10 vs shipped 12) and password.history-count (inline 0 vs shipped 3).
  • Config metadata is hand-maintained instead of generated.
  • Consumers can't read these values in templates without SpEL bean access (${@environment.getProperty(...)}), which Thymeleaf 3.1.5 (Spring Boot 4.1.0) rejects in the restricted expression context used during thymeleaf-layout-dialect decoration. This is what breaks the demo app — see Adopt Spring Boot 4.1.0: migrate ${@environment.getProperty(...)} template pattern for Thymeleaf 3.1.5 restricted expressions SpringUserFrameworkDemoApp#82.

Approach (design approved)

Convert user.security.* to a cohesive family of typed @ConfigurationProperties:

  • UserSecurityConfigProperties(prefix="user.security") — flat URIs/action paths, URI lists, and scalars.
  • PasswordPolicyConfigProperties(prefix="user.security.password") — 11 password-policy fields.
  • RememberMeConfigProperties(prefix="user.security.remember-me") — 7 remember-me fields.

Then: migrate all 14 internal @Value consumers to the beans; replace the 48 hand-maintained metadata entries with generated metadata; and expose a secret-free, immutable view record (URIs + copyrightFirstYear) to templates as @ModelAttribute("userSecurity"), mirroring CaptchaSiteKeyControllerAdvice, so consumers can drop ${@environment...}.

Constraints

  • Additive — zero config-key changes for consumers (relaxed binding maps the existing camelCase keys).
  • Do not trim the shipped dsspringuserconfig.properties — it is @PropertySource-loaded and therefore Environment-visible API; field initializers never reach the Environment.
  • Never expose the raw properties bean to templates (tokenHashSecret leak); @ToString.Exclude the secrets.
  • No @Validated/JSR-380 (would change startup behavior; contradicts defaultAction's deliberate runtime degrade-to-denyAll).

Out of scope

Key reorganization, password/passay redesign, and the demo app changes (a separate follow-up PR adopts Boot 4.1.0 and switches templates to ${userSecurity.*}, closing DemoApp#82).

Design doc

docs/design/2026-08-13-user-security-config-properties-design.md (branch feature/user-security-config-properties).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions