Explain the parent AuthenticationManager mechanism in ProviderManager and the credential-erasure behavior. Why do they exist?
answer
- parent = shared global manager, local list falls back to it
- per-SecurityFilterChain manager + AuthenticationConfiguration parent
- eraseCredentialsAfterAuthentication = true default
- CredentialsContainer.eraseCredentials nulls the password
- child doesn't re-erase parent's result
basics
~20 sA ProviderManager can have a parent AuthenticationManager it falls back to when none of its own providers authenticate. This lets multiple filter chains share a common global manager. After a successful authentication, ProviderManager erases the raw credentials from the returned token by default to reduce in-memory exposure.
solid answer
~40 sProviderManager supports a hierarchy: each instance holds an ordered list of local AuthenticationProviders plus an optional parent AuthenticationManager. If no local provider produces a result (and none throws a fatal exception), it delegates to the parent. This is how Spring Security shares a single 'global' AuthenticationManager (built from AuthenticationConfiguration) across many per-SecurityFilterChain managers — common providers live in the parent, chain-specific ones locally. Separately, eraseCredentialsAfterAuthentication defaults to true: after success, if the returned Authentication implements CredentialsContainer, ProviderManager calls eraseCredentials() to null the raw password/secret so it isn't retained in the SecurityContext (and thus not in the session). A subtlety: a ProviderManager will NOT erase credentials on results returned by its parent (the parent handles its own erasure), avoiding double handling. Disable erasure with setEraseCredentialsAfterAuthentication(false) if a downstream needs the raw credential.
code
java · 15 lines@Configuration
class SecurityConfig {
// A local ProviderManager with a chain-specific provider, falling back to a shared parent
@Bean
AuthenticationManager chainManager(
ApiKeyAuthenticationProvider apiKeyProvider,
AuthenticationConfiguration authConfig) throws Exception {
AuthenticationManager global = authConfig.getAuthenticationManager(); // the parent
ProviderManager local = new ProviderManager(List.of(apiKeyProvider), global);
// Keep raw credentials on the returned token only if truly needed:
// local.setEraseCredentialsAfterAuthentication(false);
return local;
}
}go deeper
Know a ProviderManager can fall back to a parent manager and that passwords get cleared after login.
Explain that local providers run first, parent is the fallback, and erasure is on by default via CredentialsContainer.
Articulate the per-filter-chain-plus-global-parent topology and the parent-not-re-erased subtlety.
Design authentication topologies across chains, decide erasure policy against session-persistence risk, and avoid manager cycles/mis-scoped providers.
## The parent manager: composing a hierarchy `ProviderManager` has two collaborators: a `List<AuthenticationProvider>` (its own delegates) and an optional **parent `AuthenticationManager`**. `authenticate()` flow with a parent: 1. Try each local provider (supports → authenticate), as described in the standard algorithm. 2. If no local provider returned a result **and** no fatal exception was thrown, and a parent exists, call `parent.authenticate(authentication)`. 3. The parent's result or exception is then folded into the final resolution (success returns it; a parent `AuthenticationException` becomes the `lastException` candidate; `ProviderNotFoundException` from the parent is treated specially so it doesn't mask a more meaningful local exception). ### Why a parent exists Spring Security builds an `AuthenticationManager` **per `SecurityFilterChain`** through `HttpSecurity`/`AuthenticationManagerBuilder`. To avoid re-registering the same providers everywhere, there is typically a **global (parent) `AuthenticationManager`** exposed by `AuthenticationConfiguration`. Each local `ProviderManager` can add chain-specific providers while falling back to the shared parent for common ones (e.g. the DAO/username-password provider). This yields a clean *hierarchy* where shared authentication logic is defined once. It also isolates chains: a provider added to one chain doesn't automatically leak into another, but the common parent is reused. ### Gotcha: don't create accidental cycles Because managers can reference a parent, misconfiguration can create loops or a chain that never reaches the intended provider. When manually wiring `ProviderManager`s, be deliberate about which providers are local vs in the parent. ## Credential erasure A freshly authenticated `Authentication` (e.g. `UsernamePasswordAuthenticationToken`) initially still carries the **raw credential** (the password) in `getCredentials()`. Leaving it there means the password lingers in the `SecurityContext` — and if the context is persisted (HTTP session, `SecurityContextRepository`), potentially in session storage. `ProviderManager` mitigates this with **`eraseCredentialsAfterAuthentication` (default `true`)**: - After a successful `authenticate()`, if the result implements `CredentialsContainer`, `ProviderManager` calls `result.eraseCredentials()`, which nulls the sensitive fields (credentials, and often the password inside a `UserDetails` principal if that also implements `CredentialsContainer`). - Built-in tokens like `UsernamePasswordAuthenticationToken` and `User` (the default `UserDetails`) implement `CredentialsContainer`, so this works out of the box. ### Subtleties - **Parent results are not re-erased** by the child `ProviderManager`; the manager that actually produced the result performs erasure, avoiding redundant work and respecting the producer's policy. - **Custom tokens**: if your token doesn't implement `CredentialsContainer`, erasure does nothing — implement it (or extend a built-in) to benefit. - **When to disable**: if a later step genuinely needs the raw credential (e.g. re-authenticating downstream, or a test), call `setEraseCredentialsAfterAuthentication(false)` — but understand you're keeping secrets in memory/session longer. ## When to use / reason about - Use the **parent** when structuring multiple filter chains that should share common authentication logic. - Keep **erasure on** in production for defense-in-depth; only disable with a concrete, justified need. - Remember erasure runs **only on success** — a failed authentication never reaches the erasure step.
- Why does a child ProviderManager avoid erasing credentials on a result returned by its parent?The manager that actually produced the result is responsible for its own erasure policy. Re-erasing would be redundant and could conflict with the parent's configuration, so the child leaves parent-produced results untouched.
- What must a token implement for erasure to actually null its credentials?CredentialsContainer. Built-in tokens like UsernamePasswordAuthenticationToken and the default User principal implement it; a custom token must implement eraseCredentials() (or extend a built-in) for erasure to have any effect.
saying these in an interview costs you the question
- Thinking the parent is tried before local providers
- Believing erasure happens on failed authentication too
- Assuming any custom token gets credential erasure automatically
- Confusing 'parent' with a superclass rather than a delegated manager