apereo/cas · error · AuthenticationException
Principal is unauthorized to authenticate as
Error message
Principal is unauthorized to authenticate as
What it means
After confirming the surrogate username is present, SurrogateAuthenticationPostProcessor checks eligibility via the surrogate eligibility auditable execution. When the primary principal is NOT allowed to authenticate as the surrogate user, it executes an ineligible-access audit and throws AuthenticationException whose message is 'Principal <p> is unauthorized to authenticate as <s>'.
Solutions
- Grant eligibility: add the primary principal to the target account's surrogate list in the configured surrogate source.
- Verify the surrogate eligibility source (cas.authn.surrogate.* json/ldap/groovy) is reachable and returns data for this pair.
- Check registered-service surrogate policy/attributes aren't filtering out eligibility.
- Review the audit trail (surrogateEligibilityAuditableExecution) to see exactly which check denied access.
Example fix
// before: LDAP surrogate filter finds nothing
cas.authn.surrogate.ldap.search-filter=(&(uid={principal})(surrogateMember={surrogate}))
// after: also allow members of a surrogate group
cas.authn.surrogate.ldap.search-filter=(&(objectClass=person)(|(uid={principal})(member={principal}))) Defensive patterns
Strategy: validation
Validate before calling
// check eligibility before running the full surrogate flow
boolean eligible = surrogateAuthenticationService.canImpersonate(surrogateUsername, principal, Optional.of(registeredService));
if (!eligible) { throw new AccessDeniedException("Not eligible to authenticate as " + surrogateUsername); } Try / catch
try {
processor.process(transaction, result);
} catch (AuthenticationException e) {
// present 'not permitted to impersonate' message and audit reference
LOGGER.warn("Surrogate eligibility denied: {}", e.getMessage());
} Prevention
- Review the audit records from surrogateEligibilityAuditableExecution when denials occur
- Keep the eligibility source (LDAP/JSON) reachable and monitored
- Document the surrogate approval workflow so admins know how to grant access
- Test service-specific surrogate policies after registered-service changes
When it happens
Trigger: Post-processing a surrogate authentication where canImpersonate (or the eligibility source consulted in process()) denies the primary->surrogate mapping, even though the primary credential itself authenticated fine.
Common situations: Primary user missing from the surrogate allow-list for the target; LDAP/JSON eligibility source unreachable so lookup yields nothing; service-specific surrogate policy blocking this registered service; attribute release filtering out the evidence attributes.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unable to authorize surrogate authentication request for
- [ ] is not eligible to authenticate as [ ]
- Impersonation is not allowed for
- Missing surrogate username in credential
- LDAP response is not found or does not contain a result…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/43597cb01de07f43.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/authentication/SurrogateAuthenticationPostProcessor.java:100
.build();
surrogateEligibilityAuditableExecution.execute(surrogateEligibleAudit);
return;
}
LOGGER.error("Principal [{}] is unable/unauthorized to authenticate as [{}]", primaryPrincipal, surrogateUsername);
throw new FailedLoginException();
} catch (final Exception e) {
publishFailureEvent(primaryPrincipal, surrogateUsername);
final Map<String, Throwable> map = CollectionUtils.wrap(getClass().getSimpleName(),
new SurrogateAuthenticationException("Principal " + primaryPrincipal + " is unauthorized to authenticate as " + surrogateUsername));
val surrogateIneligibleAudit = AuditableContext.builder()
.service(transaction.getService())
.authentication(authentication)
.build();
surrogateEligibilityAuditableExecution.execute(surrogateIneligibleAudit);
throw new AuthenticationException(map);
}
}
@Override
public boolean supports(final Credential credential) {
return credential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class)
.stream()
.anyMatch(trait -> StringUtils.isNotBlank(trait.getSurrogateUsername()));
}
private void publishFailureEvent(final Principal principal, final String surrogate) {
val clientInfo = ClientInfoHolder.getClientInfo();
val event = new CasSurrogateAuthenticationFailureEvent(this, principal, surrogate, clientInfo);
publishEvent(event);
}
private void publishSuccessEvent(final Principal principal, final String surrogate) {
val clientInfo = ClientInfoHolder.getClientInfo();View on GitHub (pinned to e7288fc434)