apereo/cas · error · SurrogateAuthenticationException
Impersonation is not allowed for
Error message
Impersonation is not allowed for [{}] What it means
SurrogateServiceTicketGeneratorAuthority.shouldGenerate validates, before issuing a service ticket, that any surrogate credential trait present is legitimately allowed: it asks surrogateAuthenticationService.canImpersonate for the resolved principal and surrogate username. If impersonation is denied, it logs this warning and throws SurrogateAuthenticationException, refusing ticket generation. This is a hard authorization failure, unlike the earlier warn-and-return paths.
Solutions
- Re-authenticate so eligibility is re-evaluated with current settings/LDAP state.
- Restore the principal's eligibility (eligible-accounts map or LDAP member attribute) if denial is unintended.
- Ensure the same surrogate authentication service implementation is used for selection and ticket generation.
- Log principal.id and surrogateUser at debug to find normalization/casing mismatches.
Example fix
// before // principal 'jdoe' not in eligible-accounts, ticket generation proceeds and throws // after cas.authn.surrogate.simple.eligible-accounts.jdoe=user1 # then re-login as jdoe
Defensive patterns
Strategy: try-catch
Validate before calling
if (!surrogateAuthenticationService.canImpersonate(surrogateUser, principal, service)) {
throw new SurrogateAuthenticationException("Not allowed"); // fail fast, don't request ticket
} Type guard
static boolean isSurrogateCredential(Credential c) {
return c.getCredentialMetadata() != null
&& c.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class).isPresent();
} Try / catch
try {
ticket = ticketRegistryGrantor.grantServiceTicket(...);
} catch (SurrogateAuthenticationException e) {
// re-authenticate or deny the impersonation request
} Prevention
- Re-authenticate after eligibility changes
- Use one surrogate service implementation everywhere
- Monitor SurrogateAuthenticationException occurrences
When it happens
Trigger: A credential carrying SurrogateCredentialTrait(surrogateUser) reaches shouldGenerate, and surrogateAuthenticationService.canImpersonate(surrogateUser, principal, service) returns false (e.g. eligibility removed after authentication, or simple map lacks the principal).
Common situations: User authenticated before their eligibility was revoked, then requests a service ticket; mismatch between the surrogate service backing the webflow selection and the one validating ticket generation; principal id normalization differences causing canImpersonate to see a different id.
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.
Related errors
- Unable to authorize surrogate authentication request for
- Principal is unauthorized to authenticate as
- [ ] is not eligible to authenticate as [ ]
- Missing surrogate username in credential
- screen.service.error.message
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/ceec383d828bf774.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/ticket/SurrogateServiceTicketGeneratorAuthority.java:61
val authentication = authenticationResult.getAuthentication();
LOGGER.debug("Checking if service ticket generation is allowed for [{}] and [{}]", authentication, service);
return findSurrogateCredentialTrait(authentication).isPresent();
}
@Override
public boolean shouldGenerate(final AuthenticationResult authenticationResult, final Service service) throws Throwable {
val authentication = authenticationResult.getAuthentication();
val result = findSurrogateCredentialTrait(authentication);
if (result.isPresent()) {
val pair = result.get();
val givenService = serviceSelectionPlan.resolveService(service);
val principal = resolvedPrincipal(pair.getKey().getId());
val surrogateUser = pair.getRight().getSurrogateUsername();
LOGGER.debug("Checking if [{}] can impersonate [{}] for service [{}]", principal, surrogateUser, givenService);
if (surrogateAuthenticationService.canImpersonate(surrogateUser, principal, Optional.ofNullable(givenService))) {
return true;
}
LOGGER.warn("Impersonation is not allowed for [{}]", surrogateUser);
throw new SurrogateAuthenticationException("Impersonating %s is not allowed".formatted(surrogateUser));
}
return true;
}
protected Optional<Pair<Credential, SurrogateCredentialTrait>> findSurrogateCredentialTrait(
final Authentication authentication) {
return authentication.getCredentials()
.stream()
.filter(Objects::nonNull)
.filter(credential -> Objects.nonNull(credential.getCredentialMetadata()))
.filter(credential -> credential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class).isPresent())
.map(credential -> {
val credentialTrait = credential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class).orElseThrow();
return Pair.of(credential, credentialTrait);
})
.findFirst();
}View on GitHub (pinned to e7288fc434)