|
| 1 | +## [4.2.2] - 2026-03-11 |
| 2 | +### Features |
| 3 | +- Opt-in Multi-Factor Authentication (MFA) via simple user.mfa.* properties |
| 4 | + - New configuration properties (MfaConfigProperties) under user.mfa.*: |
| 5 | + - user.mfa.enabled: toggle MFA on/off (default false) |
| 6 | + - user.mfa.factors: comma-separated list of factors, supported values: PASSWORD, WEBAUTHN |
| 7 | + - user.mfa.passwordEntryPointUri: redirect for missing PASSWORD (default /user/login.html) |
| 8 | + - user.mfa.webauthnEntryPointUri: redirect for missing WEBAUTHN (default /user/webauthn/login.html) |
| 9 | + - Enforcement built on Spring Security 7’s MFA: |
| 10 | + - Conditional bean mfaAuthorizationManagerFactory that augments .authenticated() with AllRequiredFactorsAuthorizationManager |
| 11 | + - Each configured factor is translated to its required FactorGrantedAuthority (PASSWORD_AUTHORITY, WEBAUTHN_AUTHORITY) |
| 12 | + - Startup validation (MfaConfiguration): |
| 13 | + - Fails fast on empty/blank/unknown factors |
| 14 | + - Ensures WEBAUTHN factor is only allowed when user.webauthn.enabled=true |
| 15 | + - Warns if PASSWORD factor is required in passkey-only environments |
| 16 | + - New REST endpoint for UI flow control: |
| 17 | + - GET /user/mfa/status (exposed only when MFA is enabled) |
| 18 | + - Accessible to partially-authenticated users; reports: |
| 19 | + - mfaEnabled, requiredFactors, satisfiedFactors, missingFactors, fullyAuthenticated |
| 20 | + - Backed by immutable DTO MfaStatusResponse |
| 21 | + - Security behavior and UX: |
| 22 | + - WebSecurityConfig.setupMfa() configures DelegatingMissingAuthorityAccessDeniedHandler |
| 23 | + - Missing factor → redirected to configured entry-point URIs (via LoginUrlAuthenticationEntryPoint) |
| 24 | + - /user/mfa/status explicitly added to unprotected URIs (tightened from wildcard to avoid exposing future endpoints) |
| 25 | + - Defaults added to dsspringuserconfig.properties to make enabling MFA straightforward |
| 26 | + |
| 27 | +### Fixes |
| 28 | +- Portable and size-correct WebAuthn binary columns |
| 29 | + - Replaced @Lob and hardcoded columnDefinition="BLOB" in WebAuthnCredential with plain byte[] and explicit @Column(length=...) |
| 30 | + - Prevents bypassing Hibernate dialect translation that caused DDL failures on PostgreSQL (no blob type) |
| 31 | + - Verified to generate bytea on PostgreSQL; appropriate binary types on MySQL |
| 32 | + - Corrected column lengths to avoid unintended MEDIUMBLOB on MySQL: |
| 33 | + - attestation_object and attestation_client_data_json set to length=65535 (was 65536, 1 byte over BLOB threshold) |
| 34 | + - public_key set to length=2048 (typical COSE sizes, accommodates larger keys) |
| 35 | + - Result: consistent schema generation across PostgreSQL, MySQL, and H2; no functional regressions reported |
| 36 | + |
| 37 | +### Breaking Changes |
| 38 | +- No public API or default behavior changes. |
| 39 | +- Migration note for existing databases: |
| 40 | + - Schema types for WebAuthn binary columns may differ from previous DDL (e.g., from explicit BLOB to dialect-native types like bytea on PostgreSQL or varbinary/blob on MySQL). |
| 41 | + - If you manage schema via migrations (Liquibase/Flyway), review and align your migration scripts with the new lengths and types: |
| 42 | + - public_key length 2048 |
| 43 | + - attestation_object length 65535 |
| 44 | + - attestation_client_data_json length 65535 |
| 45 | + |
| 46 | +### Refactoring |
| 47 | +- MFA implementation refinements for stability and clarity: |
| 48 | + - MfaStatusResponse switched from Lombok @Data to @Value for immutability |
| 49 | + - MfaAPI now uses method-injected Authentication instead of SecurityContextHolder |
| 50 | + - Single source of truth for factor→authority mapping (MfaConfiguration.mapFactorToAuthority is public and reused) |
| 51 | + - WebSecurityConfig.setupMfa() simplified with immutable Map.of(...) for factor-to-URI routing |
| 52 | + - Removed redundant default AccessDeniedHandler config and unused imports |
| 53 | + - Normalized requiredFactors casing to uppercase; eliminated redundant toUpperCase calls |
| 54 | + - Clarified JavaDoc and comments; dropped redundant @PropertySource (already covered elsewhere) |
| 55 | + |
| 56 | +### Documentation |
| 57 | +- README version updates: |
| 58 | + - 4.2.0 → 4.2.1 |
| 59 | + - Then 4.2.1 → 4.2.2 to reflect latest release instructions |
| 60 | + |
| 61 | +### Testing |
| 62 | +- Comprehensive tests for the new MFA feature: |
| 63 | + - MfaConfigurationTest covering property binding, factor resolution, and validation paths |
| 64 | + - MfaFeatureEnabledIntegrationTest and MfaFeatureDisabledIntegrationTest verifying endpoint exposure, partial auth access, and status payload shape |
| 65 | + - Enforcement test confirming DelegatingMissingAuthorityAccessDeniedHandler redirects to password entry-point when PASSWORD factor is missing |
| 66 | + - MfaMultiFactorIntegrationTest exercising PASSWORD+WEBAUTHN scenarios end-to-end (setupMfa, factor resolution, MFA status) |
| 67 | + - Positive-path cases confirming fullyAuthenticated=true when all required FactorGrantedAuthority entries are present |
| 68 | + |
| 69 | +### Other Changes |
| 70 | +- Dependency updates |
| 71 | + - thymeleaf-layout-dialect: 3.4.0 → 4.0.0 (compileOnly in this project) |
| 72 | + - webauthn4j-core: 0.31.0.RELEASE → 0.31.1.RELEASE |
| 73 | + - Gradle Wrapper: 9.3.1 → 9.4.0 |
| 74 | +- Build/versioning |
| 75 | + - gradle.properties bumped to 4.2.2-SNAPSHOT |
| 76 | +- Merges of Dependabot PRs for the above updates |
| 77 | + |
| 78 | +Notes for adopters: |
| 79 | +- Enabling MFA is opt-in. Minimal config example: |
| 80 | + - user.mfa.enabled=true |
| 81 | + - user.mfa.factors=PASSWORD,WEBAUTHN |
| 82 | + - Optionally override the entry-point URIs to match your UI |
| 83 | +- If including WEBAUTHN as a factor, ensure user.webauthn.enabled=true and the WebAuthn flow is configured. |
| 84 | + |
1 | 85 | ## [4.2.1] - 2026-02-27 |
2 | 86 | ### Features |
3 | 87 | - Passwordless passkey-only accounts |
|
0 commit comments