{"record":{"id":"b9a54300aede523c","repo":"spring-projects/spring-security","slug":"can-t-change-password-as-no-authentication-object-b9a543","errorCode":null,"errorMessage":"Can't change password as no Authentication object found in context for current user.","messagePattern":"Can't change password as no Authentication object found in context for current user\\.","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":403,"severity":"error","filePath":"core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java","lineNumber":319,"sourceCode":"\tpublic void deleteUser(String username) {\n\t\tif (getEnableAuthorities()) {\n\t\t\tdeleteUserAuthorities(username);\n\t\t}\n\t\trequireJdbcTemplate().update(this.deleteUserSql, username);\n\t\tthis.userCache.removeUserFromCache(username);\n\t}\n\n\tprivate void deleteUserAuthorities(String username) {\n\t\trequireJdbcTemplate().update(this.deleteUserAuthoritiesSql, username);\n\t}\n\n\t@Override\n\tpublic void changePassword(@Nullable String oldPassword, @Nullable String newPassword)\n\t\t\tthrows AuthenticationException {\n\t\tAuthentication currentUser = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (currentUser == null) {\n\t\t\t// This would indicate bad coding somewhere\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\t\"Can't change password as no Authentication object found in context \" + \"for current user.\");\n\t\t}\n\t\tString username = currentUser.getName();\n\t\t// If an authentication manager has been set, re-authenticate the user with the\n\t\t// supplied password.\n\t\tif (this.authenticationManager != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Reauthenticating user '%s' for password change request.\", username));\n\t\t\tthis.authenticationManager\n\t\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, oldPassword));\n\t\t}\n\t\telse {\n\t\t\tthis.logger.debug(\"No authentication manager set. Password won't be re-checked.\");\n\t\t}\n\t\tthis.logger.debug(\"Changing password for user '\" + username + \"'\");\n\t\trequireJdbcTemplate().update(this.changePasswordSql, newPassword, username);\n\t\tAuthentication authentication = createNewAuthentication(currentUser, newPassword);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java#L301-L337","documentation":"JdbcUserDetailsManager.changePassword requires an Authentication in the SecurityContextHolder to know which user's password to change; if none is present it throws AccessDeniedException. The class then optionally re-authenticates the user against the configured AuthenticationManager before performing the UPDATE. This mirrors InMemoryUserDetailsManager's behavior and is treated as a caller/configuration error.","triggerScenarios":"Calling changePassword when the current thread has no authenticated SecurityContext; running inside a scheduled job or message listener without propagating SecurityContext; a security filter chain that never populates the context for the endpoint; unit tests invoking the method directly without authentication.","commonSituations":"Password reset flows implemented as unauthenticated endpoints; integration tests missing @WithMockUser; Spring remoting/async execution dropping the context between request and password change.","solutions":["Invoke changePassword only within an authenticated context (authenticated request or explicit SecurityContextHolder.getContext().setAuthentication(...))","In tests, use @WithMockUser or set an Authentication in the SecurityContextHolder before the call","Wire an AuthenticationManager into JdbcUserDetailsManager so re-authentication with oldPassword works as intended","For unauthenticated reset flows, look up the user directly and update via JDBC or an admin path instead of changePassword"],"exampleFix":"// before\njdbcManager.changePassword(\"old\", \"new\"); // AccessDeniedException\n// after\nAuthentication auth = new UsernamePasswordAuthenticationToken(\"alice\", null);\nSecurityContextHolder.getContext().setAuthentication(auth);\njdbcManager.changePassword(\"old\", \"new\");","handlingStrategy":"try-catch","validationCode":"Authentication auth = SecurityContextHolder.getContext().getAuthentication();\nif (auth == null || auth instanceof AnonymousAuthenticationToken) {\n    throw new AccessDeniedException(\"Authentication required to change password\");\n}","typeGuard":"static boolean hasAuthentication() {\n    Authentication a = SecurityContextHolder.getContext().getAuthentication();\n    return a != null && a.isAuthenticated();\n}","tryCatchPattern":"try {\n    jdbcManager.changePassword(oldPassword, newPassword);\n} catch (AccessDeniedException e) {\n    if (e.getMessage().contains(\"no Authentication object\")) {\n        // force re-login before password change\n    } else if (e instanceof AuthenticationException) {\n        // oldPassword re-authentication failed\n    } else { throw e; }\n}","preventionTips":["Gate password-change endpoints behind authentication in the security filter chain","Set an Authentication explicitly in tests and background jobs","Configure an AuthenticationManager on JdbcUserDetailsManager for old-password verification","Keep SecurityContext propagation enabled (SecurityContextHolder.MODE_INHERITABLETHREADLOCAL or executors)"],"tags":["authentication","access-denied","jdbc","security-context"],"backgroundTag":"authentication-required","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}