{"record":{"id":"efe15b59765c9206","repo":"spring-projects/spring-security","slug":"can-t-change-password-as-no-authentication-object","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":null,"severity":"error","filePath":"core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java","lineNumber":138,"sourceCode":"\t\tif (user instanceof MutableUserDetails mutable) {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), mutable);\n\t\t}\n\t\telse {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user));\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean userExists(String username) {\n\t\treturn this.users.containsKey(username.toLowerCase(Locale.ROOT));\n\t}\n\n\t@Override\n\tpublic void changePassword(@Nullable String oldPassword, @Nullable String newPassword) {\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\tthis.logger.debug(LogMessage.format(\"Changing password for user '%s'\", username));\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\tMutableUserDetails user = this.users.get(username.toLowerCase(Locale.ROOT));\n\t\tAssert.state(user != null, \"Current user doesn't exist in database.\");\n\t\tuser.setPassword(newPassword);\n\t}","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nmanager.changePassword(\"old\", \"new\"); // no auth in context -> AccessDeniedException\n// after\nSecurityContextHolder.getContext().setAuthentication(\n    new UsernamePasswordAuthenticationToken(\"user\", \"old\",\n        AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\nmanager.changePassword(\"old\", \"new\");","handlingStrategy":"validation","validationCode":"Authentication auth = SecurityContextHolder.getContext().getAuthentication();\nif (auth == null || !auth.isAuthenticated()) {\n    throw new IllegalStateException(\"changePassword requires an authenticated user\");\n}","typeGuard":"static boolean isAuthenticated() {\n    Authentication a = SecurityContextHolder.getContext().getAuthentication();\n    return a != null && a.isAuthenticated() && !(a instanceof AnonymousAuthenticationToken);\n}","tryCatchPattern":"try {\n    manager.changePassword(oldPassword, newPassword);\n} catch (AccessDeniedException e) {\n    if (e.getMessage().contains(\"no Authentication object\")) {\n        // redirect to login / abort password change\n    } else { throw e; }\n}","preventionTips":["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"],"tags":["authentication","access-denied","in-memory-users","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"}