{"record":{"id":"9b20ebd2c6a5b0bd","repo":"apereo/cas","slug":"accountpasswordmustchangeexception","errorCode":null,"errorMessage":"AccountPasswordMustChangeException","messagePattern":"AccountPasswordMustChangeException","errorType":"exception","errorClass":"AccountPasswordMustChangeException","httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-redis-authentication/src/main/java/org/apereo/cas/redis/RedisAuthenticationHandler.java","lineNumber":47,"sourceCode":"    }\n\n    @Override\n    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(\n        final UsernamePasswordCredential credential,\n        final String originalPassword) throws Throwable {\n        val account = (RedisUserAccount) redisTemplate.opsForValue().get(credential.getUsername());\n        if (account == null) {\n            throw new AccountNotFoundException();\n        }\n        if (!getPasswordEncoder().matches(originalPassword, account.getPassword())) {\n            LOGGER.warn(\"Account password on record for [{}] does not match the given/encoded password\", credential.getId());\n            throw new FailedLoginException();\n        }\n        switch (account.getStatus()) {\n            case DISABLED -> throw new AccountDisabledException();\n            case EXPIRED -> throw new AccountExpiredException();\n            case LOCKED -> throw new AccountLockedException();\n            case MUST_CHANGE_PASSWORD -> throw new AccountPasswordMustChangeException();\n            case OK -> LOGGER.debug(\"Account status is OK\");\n        }\n        val principal = principalFactory.createPrincipal(account.getUsername(), account.getAttributes());\n        return createHandlerResult(credential, principal, new ArrayList<>());\n    }\n}\n","sourceCodeStart":29,"sourceCodeEnd":54,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-redis-authentication/src/main/java/org/apereo/cas/redis/RedisAuthenticationHandler.java#L29-L54","documentation":"RedisAuthenticationHandler throws AccountPasswordMustChangeException when the Redis account record has status MUST_CHANGE_PASSWORD. CAS signals that authentication may proceed administratively but the user must change their password before being fully admitted, typically routing the flow to a password-change screen.","triggerScenarios":"authenticateUsernamePasswordInternal loads the account from Redis, the password matches, but account.getStatus() is MUST_CHANGE_PASSWORD in the status switch.","commonSituations":"A password expired or an admin forced a reset flag in the Redis user store; an external provisioning system marked the account must-change; user was migrated with a forced-reset flag set.","solutions":["Have the user complete the CAS password-change flow to clear the must-change flag","Reset/clear the MUST_CHANGE_PASSWORD status in the Redis account record once the password is updated","Check the provisioning source that sets this status if it was set unintentionally"],"exampleFix":"// before (stored in Redis)\n{\"username\":\"jdoe\",\"status\":\"MUST_CHANGE_PASSWORD\"}\n// after password change\n{\"username\":\"jdoe\",\"status\":\"OK\"}","handlingStrategy":"try-catch","validationCode":"val acct = redisAccountService.findAccount(username);\nif (acct != null && acct.getStatus() == AccountStatus.MUST_CHANGE_PASSWORD) {\n    // redirect to password change flow instead of authenticating\n}","typeGuard":"boolean needsPasswordChange(AccountState s) {\n    return s != null && s.getStatus() == AccountStatus.MUST_CHANGE_PASSWORD;\n}","tryCatchPattern":"try {\n    return authenticationHandler.authenticate(credential);\n} catch (AccountPasswordMustChangeException e) {\n    // send user to the password-change webflow\n    throw e;\n}","preventionTips":["Ensure CAS password management is configured so users can clear the flag","Provision users with OK status unless a reset is truly required","Sync must-change flags from your IAM system deliberately","Test the full password-change round trip in staging"],"tags":["authentication","redis","password-expired"],"backgroundTag":"authentication-required","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}