{"record":{"id":"79252d812e2d5002","repo":"apereo/cas","slug":"could-not-authenticate-expired-account-for-usern","errorCode":null,"errorMessage":"Could not authenticate expired account for ${username}","messagePattern":"Could not authenticate expired account for (.+?)","errorType":"exception","errorClass":"AccountExpiredException","httpStatus":412,"severity":"error","filePath":"support/cas-server-support-rest-authentication/src/main/java/org/apereo/cas/adaptors/rest/RestAuthenticationHandler.java","lineNumber":97,"sourceCode":"        try {\n            val exec = HttpExecutionRequest\n                .builder()\n                .basicAuthUsername(credential.getUsername())\n                .basicAuthPassword(credential.toPassword())\n                .method(HttpMethod.valueOf(properties.getMethod().toUpperCase(Locale.ENGLISH)))\n                .url(SpringExpressionLanguageValueResolver.getInstance().resolve(properties.getUri()))\n                .httpClient(httpClient)\n                .build()\n                .withoutRetry();\n            response = HttpUtils.execute(exec);\n            val status = HttpStatus.resolve(Objects.requireNonNull(response).getCode());\n            return switch (Objects.requireNonNull(status)) {\n                case OK -> buildPrincipalFromResponse(credential, response);\n                case FORBIDDEN -> throw new AccountDisabledException(\"Could not authenticate forbidden account for \" + credential.getUsername());\n                case UNAUTHORIZED -> throw new FailedLoginException(\"Could not authenticate account for \" + credential.getUsername());\n                case NOT_FOUND -> throw new AccountNotFoundException(\"Could not locate account for \" + credential.getUsername());\n                case LOCKED -> throw new AccountLockedException(\"Could not authenticate locked account for \" + credential.getUsername());\n                case PRECONDITION_FAILED -> throw new AccountExpiredException(\"Could not authenticate expired account for \" + credential.getUsername());\n                case PRECONDITION_REQUIRED -> throw new AccountPasswordMustChangeException(\"Account password must change for \" + credential.getUsername());\n                default -> throw new FailedLoginException(\"Rest endpoint returned an unknown status code \" + status + \" for \" + credential.getUsername());\n            };\n        } finally {\n            HttpUtils.close(response);\n        }\n    }\n\n    protected AuthenticationHandlerExecutionResult buildPrincipalFromResponse(\n        final UsernamePasswordCredential credential,\n        final HttpResponse response) throws Throwable {\n        try {\n            try (val content = ((HttpEntityContainer) response).getEntity().getContent()) {\n                val result = IOUtils.toString(content, StandardCharsets.UTF_8);\n                LOGGER.debug(\"REST authentication response received: [{}]\", result);\n                val principalFromRest = MAPPER.readValue(result, Principal.class);\n                val principal = principalFactory.createPrincipal(principalFromRest.getId(), principalFromRest.getAttributes());\n                return createHandlerResult(credential, principal, getWarnings(response));","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-rest-authentication/src/main/java/org/apereo/cas/adaptors/rest/RestAuthenticationHandler.java#L79-L115","documentation":"RestAuthenticationHandler throws AccountExpiredException when the remote REST endpoint replies HTTP 412 PRECONDITION_FAILED. CAS maps this status to an expired account, meaning the account (or its password) is no longer valid by time-based policy.","triggerScenarios":"authenticateUsernamePasswordInternal receives HTTP 412 from the endpoint; the switch maps PRECONDITION_FAILED -> AccountExpiredException.","commonSituations":"Password aged out on the remote system; account validity/expiry date passed; the endpoint uses 412 for conditional-request failures unrelated to expiry, which CAS then misinterprets.","solutions":["Renew/extend the account or password validity in the remote system","Have the user reset an expired password","If the endpoint uses 412 for other preconditions, change the endpoint semantics or write a custom handler mapping","Synchronize password-expiry policies between CAS and the remote store"],"exampleFix":"// before: remote account\n{\"user\":\"jdoe\",\"expires\":\"2024-01-01\"}\n// after extension\n{\"user\":\"jdoe\",\"expires\":\"2027-01-01\"}","handlingStrategy":"try-catch","validationCode":"val acct = remoteUserStore.lookup(username);\nif (acct != null && acct.getExpiresOn().isBefore(LocalDate.now())) {\n    throw new AccountExpiredException(\"Account expired\");\n}","typeGuard":"boolean isPreconditionFailed(HttpResponse r) { return r != null && r.getCode() == 412; }","tryCatchPattern":"try {\n    return restHandler.authenticate(credential);\n} catch (AccountExpiredException e) {\n    LOGGER.info(\"Expired account: {}\", e.getMessage());\n    throw e; // route to renewal/password-reset flow\n}","preventionTips":["Run expiry-sync jobs between CAS and the remote store","Alert on accounts nearing expiry so renewals happen proactively","Keep 412 semantics reserved for expiry in the endpoint contract","Test expiry behavior after password-policy changes"],"tags":["authentication","rest","http-412","account-expired"],"backgroundTag":"http-error-response","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"}