How does DaoAuthenticationProvider work, and how does it collaborate with UserDetailsService and PasswordEncoder?
answer
- retrieveUser -> checks -> additionalAuthenticationChecks
- PasswordEncoder.matches does the comparison
- hideUserNotFoundExceptions default true -> BadCredentials
- dummy encode blocks timing attacks
- UserDetailsService bean + PasswordEncoder bean = auto-wired
basics
~20 sDaoAuthenticationProvider is the AuthenticationProvider for username/password login. It calls UserDetailsService to load the user, then uses a PasswordEncoder to compare the submitted password with the stored hash. If they match and the account is enabled, it returns an authenticated token.
solid answer
~40 sDaoAuthenticationProvider is an AuthenticationProvider that authenticates UsernamePasswordAuthenticationToken. In its retrieveUser step it delegates to UserDetailsService.loadUserByUsername to get the UserDetails; in additionalAuthenticationChecks it uses a PasswordEncoder to compare the submitted raw password with the stored encoded password. Between those, pre/post checks validate the account-status flags (enabled, non-locked, non-expired). On success it builds a fully authenticated UsernamePasswordAuthenticationToken carrying the authorities. It also protects against username enumeration: hideUserNotFoundExceptions defaults to true, converting UsernameNotFoundException into BadCredentialsException, and to blunt timing attacks it runs a dummy password encode even when the user is not found. With Spring Boot, defining a UserDetailsService bean plus a PasswordEncoder bean auto-wires this provider; you rarely instantiate it directly.
code
java · 23 lines@Configuration
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService(AccountRepository repo) {
return new MyUserDetailsService(repo);
}
@Bean
public PasswordEncoder passwordEncoder() {
// DelegatingPasswordEncoder: reads {bcrypt}, {argon2}, {noop} ... prefixes
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// Optional: explicit provider when you need to customize it
@Bean
public AuthenticationManager authManager(UserDetailsService uds, PasswordEncoder pe) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
provider.setPasswordEncoder(pe);
// provider.setHideUserNotFoundExceptions(true); // default
return new ProviderManager(provider);
}
}go deeper
Know it's the username/password provider that loads the user then compares passwords.
Explain the retrieveUser/checks/additionalAuthenticationChecks steps and the PasswordEncoder.matches call.
Discuss enumeration protection, timing mitigation, and DelegatingPasswordEncoder prefixes.
Reason about UserDetailsPasswordService hash upgrades, multi-provider ProviderManager wiring, and security trade-offs of hideUserNotFoundExceptions.
**DaoAuthenticationProvider** (package `org.springframework.security.authentication.dao`) is the standard `AuthenticationProvider` implementation for **username + password** authentication. It extends `AbstractUserDetailsAuthenticationProvider`. An `AuthenticationProvider` has two methods: `supports(Class)` — here it returns true for `UsernamePasswordAuthenticationToken` — and `authenticate(Authentication)`. **The authentication sequence:** 1. The `ProviderManager` (the default `AuthenticationManager`) receives an *unauthenticated* `UsernamePasswordAuthenticationToken` (built by the filter from the submitted username/password) and finds the provider whose `supports(...)` matches. 2. **retrieveUser** — the provider calls `userDetailsService.loadUserByUsername(username)` to obtain a `UserDetails`. If that throws `UsernameNotFoundException` and `hideUserNotFoundExceptions` is true (the default), it is converted to `BadCredentialsException` (see enumeration protection below). If it returns null, an `InternalAuthenticationServiceException` is raised. 3. **preAuthenticationChecks** — a `UserDetailsChecker` verifies status flags: not locked (`LockedException`), enabled (`DisabledException`), account not expired (`AccountExpiredException`). 4. **additionalAuthenticationChecks** — this is where the password is actually verified: `passwordEncoder.matches(presentedPassword, userDetails.getPassword())`. On mismatch it throws `BadCredentialsException`. 5. **postAuthenticationChecks** — verifies credentials are not expired (`CredentialsExpiredException`). 6. On success it returns a **new, authenticated** `UsernamePasswordAuthenticationToken` populated with the principal (usually the UserDetails) and its authorities; `isAuthenticated()` is true. This is stored in the `SecurityContext`. **PasswordEncoder is mandatory.** Spring Security requires a `PasswordEncoder`; the modern default is `PasswordEncoderFactories.createDelegatingPasswordEncoder()` (a `DelegatingPasswordEncoder`), which reads a `{id}` prefix on the stored hash (e.g. `{bcrypt}$2a$...`) to pick the algorithm. That's why storing `{noop}password` means plaintext. `matches(raw, encoded)` does the comparison — never compare strings yourself. **Username enumeration protection — `hideUserNotFoundExceptions` (default true):** if a user doesn't exist, converting `UsernameNotFoundException` to a generic `BadCredentialsException` means the client cannot distinguish 'no such user' from 'wrong password'. Setting it false re-exposes the distinction (occasionally wanted for internal apps, but a security trade-off). **Timing-attack mitigation:** even when the user is not found, `DaoAuthenticationProvider` runs a password encode against a dummy hash (`userNotFoundEncodedPassword`) so the response time for 'no such user' resembles 'wrong password', preventing attackers from probing which usernames exist by measuring latency. **Automatic password upgrades — `UserDetailsPasswordService`:** if your `UserDetailsService` also implements `UserDetailsPasswordService`, and `PasswordEncoder.upgradeEncoding(...)` returns true (e.g. an old hash with a lower cost factor), the provider calls `updatePassword(...)` to re-hash and persist the password transparently on successful login. **Wiring in Spring Boot:** you rarely `new` this provider. If you expose a `UserDetailsService` bean and a `PasswordEncoder` bean, Boot's auto-config assembles a `DaoAuthenticationProvider` inside the default `AuthenticationManager`. To customize (custom provider, multiple providers), you configure an `AuthenticationManager`/`ProviderManager` explicitly. Since Spring Security 6 you can construct it via `new DaoAuthenticationProvider(userDetailsService)` and `setPasswordEncoder(...)`. **Gotchas:** - Forgetting the `PasswordEncoder` bean → startup or runtime error, or falling back to no-op behavior in older setups. - Mismatched encoder: hashes stored with `{bcrypt}` but a raw `BCryptPasswordEncoder` bean (not delegating) will fail to parse the `{id}` prefix. - Defining a `UserDetailsService` bean can *replace* Boot's default user and silently change auth — expected, but surprising.
- Why does DaoAuthenticationProvider still encode a password when the username is unknown?To defeat timing attacks. If it returned instantly for unknown users but took ~100ms (a BCrypt compare) for known ones, an attacker could enumerate valid usernames by measuring latency. The dummy encode equalizes the response time.
- What does hideUserNotFoundExceptions do, and what's the trade-off of disabling it?When true (default) it converts UsernameNotFoundException to BadCredentialsException so clients can't tell 'no such user' from 'wrong password' — preventing username enumeration. Disabling it exposes that distinction, which leaks account existence; only acceptable for internal/low-risk apps.
saying these in an interview costs you the question
- Claiming DaoAuthenticationProvider compares raw string passwords directly instead of using PasswordEncoder.matches
- Saying UserDetailsService verifies the password (the provider does, via the encoder)
- Not knowing a PasswordEncoder is required
- Believing account-status flags (enabled/locked/expired) are ignored during password login