apereo/cas · error · AccountDisabledException
Could not authenticate forbidden account for
Error message
Could not authenticate forbidden account for
What it means
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.
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
Example fix
// before (Syncope console) user.status = suspended
// after (Syncope REST)
PATCH /syncope/rest/users/{key} {"type":"USER","operation":"DEPROVISION","suspended":false} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check via Syncope REST before CAS login
GET /syncope/rest/users?FIQLString=username=={u}
if (user.suspended) throw new AccountDisabledException("suspended in Syncope"); Try / catch
try {
return authenticationManager.authenticate(transaction);
} catch (AccountDisabledException e) {
model.put("errorCode", "account.disabled");
return "casAccountDisabledView";
} Prevention
- 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
When it happens
Trigger: Calling the authentication handler (standard CAS username/password flow) where authenticateSyncopeUser returns a user object with field suspended == true.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- AccountDisabledException
- Account password must change for
- Could not authenticate account for
- Radius authentication failed for user
- Radius authentication failed for user
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/842068e0aadbb1ea.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-syncope-authentication/src/main/java/org/apereo/cas/syncope/SyncopeAuthenticationHandler.java:62
private final String syncopeDomain;
public SyncopeAuthenticationHandler(final SyncopeAuthenticationProperties properties,
final PrincipalFactory principalFactory,
final String syncopeDomain) {
super(properties.getName(), principalFactory, properties.getOrder());
this.properties = properties;
this.syncopeDomain = syncopeDomain;
}
@Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
final UsernamePasswordCredential credential, @Nullable final String originalPassword) throws Throwable {
val result = authenticateSyncopeUser(credential);
if (result.isPresent()) {
val user = result.get();
LOGGER.debug("Received Syncope user object as [{}]", user);
if (user.has("suspended") && user.get("suspended").asBoolean()) {
throw new AccountDisabledException(
"Could not authenticate forbidden account for " + credential.getUsername());
}
if (user.has("mustChangePassword") && user.get("mustChangePassword").asBoolean()) {
throw new AccountPasswordMustChangeException(
"Account password must change for " + credential.getUsername());
}
val principalAttributes = SyncopeUtils.convertFromUserEntity(user, properties.getAttributeMappings());
val name = properties.getAttributeMappings().getOrDefault("domain", "syncopeDomain");
principalAttributes.put(name, CollectionUtils.wrapList(syncopeDomain));
val principal = principalFactory.createPrincipal(user.get("username").asString(), principalAttributes);
return createHandlerResult(credential, principal, new ArrayList<>());
}
throw new FailedLoginException("Could not authenticate account for " + credential.getUsername());
}
protected Optional<JsonNode> authenticateSyncopeUser(final UsernamePasswordCredential credential) {
HttpResponse response = null;
try {View on GitHub (pinned to e7288fc434)