diff --git a/docs/modules/ROOT/pages/features/authentication/password-storage.adoc b/docs/modules/ROOT/pages/features/authentication/password-storage.adoc index 94e2cf21e03..1a34069fd77 100644 --- a/docs/modules/ROOT/pages/features/authentication/password-storage.adoc +++ b/docs/modules/ROOT/pages/features/authentication/password-storage.adoc @@ -689,3 +689,54 @@ class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHan } ---- ====== + +[[authentication-upgrading-password-encoding]] +== Upgrading Password Encoding + +Spring Security can automatically upgrade existing password encodings without asking users to reset their passwords. It uses the `PasswordEncoder.upgradeEncoding(String)` method to determine whether a stored password should be re-encoded for improved security. + +If `PasswordEncoder.upgradeEncoding(String)` indicates an update is needed and a `UserDetailsPasswordService` bean is available, the framework will rehash the password immediately after a successful login. The raw password submitted at login is passed to `PasswordEncoder` to produce a stronger encoding. The `UserDetailsPasswordService` saves the newly encoded password in the user store (e.g., database or LDAP) and returns the updated `UserDetails`. + +To enable this behavior, simply define a `UserDetailsPasswordService` bean. You can always provide your own `UserDetailsPasswordService` implementation tailored to your persistent user store. + +For example, if you are using a `UserDetailsManager`, you can write: + +.Using UserDetailsPasswordService with UserDetailsManager +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Bean +UserDetailsPasswordService userDetailsPasswordService(UserDetailsManager userDetailsManager) { + return (user, newPassword) -> { + UserDetails updated = User.withUserDetails(user) + .password(newPassword) + .build(); + userDetailsManager.updateUser(updated); + return updated; + }; +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Bean +open fun userDetailsPasswordService(userDetailsManager: UserDetailsManager): UserDetailsPasswordService { + return UserDetailsPasswordService { user, newPassword -> + val updated = User.withUserDetails(user) + .password(newPassword) + .build() + userDetailsManager.updateUser(updated) + updated + } +} +---- +====== + +When using xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc-bean[JdbcUserDetailsManager], you do not need to register a separate `UserDetailsPasswordService` bean, since `JdbcUserDetailsManager` already implements that interface. You simply enable the upgrade mechanism by calling `JdbcUserDetailsManager.setEnableUpdatePassword(true)`. + +This approach lets you migrate from weaker hashes to stronger algorithms (like <>) without impacting your users. Passwords are re-encoded only after successful authentication, enabling a gradual, transparent migration for your application.