{"record":{"id":"842068e0aadbb1ea","repo":"apereo/cas","slug":"could-not-authenticate-forbidden-account-for","errorCode":null,"errorMessage":"Could not authenticate forbidden account for ","messagePattern":"Could not authenticate forbidden account for ","errorType":"exception","errorClass":"AccountDisabledException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-syncope-authentication/src/main/java/org/apereo/cas/syncope/SyncopeAuthenticationHandler.java","lineNumber":62,"sourceCode":"    private final String syncopeDomain;\n\n    public SyncopeAuthenticationHandler(final SyncopeAuthenticationProperties properties,\n                                        final PrincipalFactory principalFactory,\n                                        final String syncopeDomain) {\n        super(properties.getName(), principalFactory, properties.getOrder());\n        this.properties = properties;\n        this.syncopeDomain = syncopeDomain;\n    }\n\n    @Override\n    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(\n        final UsernamePasswordCredential credential, @Nullable final String originalPassword) throws Throwable {\n        val result = authenticateSyncopeUser(credential);\n        if (result.isPresent()) {\n            val user = result.get();\n            LOGGER.debug(\"Received Syncope user object as [{}]\", user);\n            if (user.has(\"suspended\") && user.get(\"suspended\").asBoolean()) {\n                throw new AccountDisabledException(\n                    \"Could not authenticate forbidden account for \" + credential.getUsername());\n            }\n            if (user.has(\"mustChangePassword\") && user.get(\"mustChangePassword\").asBoolean()) {\n                throw new AccountPasswordMustChangeException(\n                    \"Account password must change for \" + credential.getUsername());\n            }\n            val principalAttributes = SyncopeUtils.convertFromUserEntity(user, properties.getAttributeMappings());\n            val name = properties.getAttributeMappings().getOrDefault(\"domain\", \"syncopeDomain\");\n            principalAttributes.put(name, CollectionUtils.wrapList(syncopeDomain));\n            val principal = principalFactory.createPrincipal(user.get(\"username\").asString(), principalAttributes);\n            return createHandlerResult(credential, principal, new ArrayList<>());\n        }\n        throw new FailedLoginException(\"Could not authenticate account for \" + credential.getUsername());\n    }\n\n    protected Optional<JsonNode> authenticateSyncopeUser(final UsernamePasswordCredential credential) {\n        HttpResponse response = null;\n        try {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-syncope-authentication/src/main/java/org/apereo/cas/syncope/SyncopeAuthenticationHandler.java#L44-L80","documentation":"SyncopeAuthenticationHandler.authenticateUsernamePasswordInternal throws AccountDisabledException when the Syncope user JSON response contains \"suspended\": true. The account exists and the password was accepted (or at least the user was found), but Syncope marks the account suspended so CAS refuses authentication as a disabled account.","triggerScenarios":"Calling the authentication handler (standard CAS username/password flow) where authenticateSyncopeUser returns a user object with field suspended == true.","commonSituations":"Syncope admin suspended the user for policy or inactivity; user status 'suspended' in Syncope console; password resets not processed so account remains suspended; testing with a seeded demo user that is suspended.","solutions":["Unsuspend the account in the Apache Syncope admin console (or via Syncope REST: PATCH /users/{key} setting suspended=false)","Check Syncope workflow/policies that auto-suspend users and adjust them","Verify you are querying the right Syncope domain/realm so the unsuspended user is the one returned","As a last resort, deactivate CAS-based login for that user in Syncope rather than repeatedly hitting the disabled state"],"exampleFix":"// before (Syncope console) user.status = suspended\n// after (Syncope REST)\nPATCH /syncope/rest/users/{key} {\"type\":\"USER\",\"operation\":\"DEPROVISION\",\"suspended\":false}","handlingStrategy":"try-catch","validationCode":"// pre-check via Syncope REST before CAS login\nGET /syncope/rest/users?FIQLString=username=={u}\nif (user.suspended) throw new AccountDisabledException(\"suspended in Syncope\");","typeGuard":null,"tryCatchPattern":"try {\n    return authenticationManager.authenticate(transaction);\n} catch (AccountDisabledException e) {\n    model.put(\"errorCode\", \"account.disabled\");\n    return \"casAccountDisabledView\";\n}","preventionTips":["Monitor Syncope for suspended accounts and notify users proactively","Automate unsuspension workflows where appropriate","Verify the correct Syncope domain is queried","Exclude obviously suspended accounts from login attempts to avoid lockout noise"],"tags":["syncope","account-disabled","authentication","cas"],"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"}