spring-projects/spring-security · error · AccessDeniedException
Can't change password as no Authentication object found in c
Error message
Can't change password as no Authentication object found in context for current user.
What it means
InMemoryUserDetailsManager.changePassword requires an authenticated user in the SecurityContextHolder: it reads the current Authentication to identify whose password to change. When the context holds no Authentication (null), it throws AccessDeniedException, since there is no user whose password could be changed. The manager treats the absence as a programming/configuration error, not a normal user error.
Source
Thrown at core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java:138
if (user instanceof MutableUserDetails mutable) {
this.users.put(user.getUsername().toLowerCase(Locale.ROOT), mutable);
}
else {
this.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user));
}
}
@Override
public boolean userExists(String username) {
return this.users.containsKey(username.toLowerCase(Locale.ROOT));
}
@Override
public void changePassword(@Nullable String oldPassword, @Nullable String newPassword) {
Authentication currentUser = this.securityContextHolderStrategy.getContext().getAuthentication();
if (currentUser == null) {
// This would indicate bad coding somewhere
throw new AccessDeniedException(
"Can't change password as no Authentication object found in context " + "for current user.");
}
String username = currentUser.getName();
this.logger.debug(LogMessage.format("Changing password for user '%s'", username));
// If an authentication manager has been set, re-authenticate the user with the
// supplied password.
if (this.authenticationManager != null) {
this.logger.debug(LogMessage.format("Reauthenticating user '%s' for password change request.", username));
this.authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, oldPassword));
}
else {
this.logger.debug("No authentication manager set. Password won't be re-checked.");
}
MutableUserDetails user = this.users.get(username.toLowerCase(Locale.ROOT));
Assert.state(user != null, "Current user doesn't exist in database.");
user.setPassword(newPassword);
}View on GitHub (pinned to 96852e8860)
Solutions
- Ensure the call runs inside an authenticated security context (e.g., within an authenticated request or with SecurityContextHolder.getContext().setAuthentication(...) set beforehand)
- In tests, authenticate first with SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(...)) or @WithMockUser
- If using a custom strategy, set it on the manager via setSecurityContextHolderStrategy and populate it consistently
- Propagate the SecurityContext to async threads (DelegatingSecurityContextExecutor) before calling changePassword
Example fix
// before
manager.changePassword("old", "new"); // no auth in context -> AccessDeniedException
// after
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("user", "old",
AuthorityUtils.createAuthorityList("ROLE_USER")));
manager.changePassword("old", "new"); Defensive patterns
Strategy: validation
Validate before calling
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !auth.isAuthenticated()) {
throw new IllegalStateException("changePassword requires an authenticated user");
} Type guard
static boolean isAuthenticated() {
Authentication a = SecurityContextHolder.getContext().getAuthentication();
return a != null && a.isAuthenticated() && !(a instanceof AnonymousAuthenticationToken);
} Try / catch
try {
manager.changePassword(oldPassword, newPassword);
} catch (AccessDeniedException e) {
if (e.getMessage().contains("no Authentication object")) {
// redirect to login / abort password change
} else { throw e; }
} Prevention
- Only expose changePassword through authenticated endpoints
- Use @WithMockUser in tests that touch changePassword
- Propagate SecurityContext to async threads (DelegatingSecurityContextRunnable)
- Configure SecurityContextHolderStrategy consistently if customizing context storage
When it happens
Trigger: Calling changePassword(oldPassword, newPassword) outside an authenticated request thread; clearing SecurityContextHolder programmatically before the call; using a custom SecurityContextHolderStrategy whose context was never populated; invoking it from a background/scheduled task or test without authenticating first.
Common situations: Password-change endpoints missing authentication configuration (anonymous access paths); tests calling changePassword directly without setUp authentication; async threads losing the SecurityContext because it is not propagated.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Authenticated principal required to operate with ACLs
- Can't change password as no Authentication object found in c
- An Authentication object was not found in the SecurityContex
- Access is denied
- RunAsImplAuthenticationProvider.incorrectKey
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/efe15b59765c9206.
Report an issue: GitHub.