apereo/cas · error · GeneralSecurityException
<policy status exception>
Error message
<policy status exception>
What it means
RestfulAuthenticationPolicy delegates authentication-policy decisions to a remote REST endpoint. After executing the HTTP call, any status code other than 200 OK is converted via handleResponseStatusCode and rethrown as a GeneralSecurityException carrying the resulting AuthenticationHandlerExecutionResult, so the policy is reported as unsatisfied/failed rather than silently passing.
Solutions
- Check the REST endpoint is reachable and returns 200 for a valid principal: curl -i the configured URL with equivalent payload.
- Verify cas.authn.policy.rest.url points at the correct service and context path.
- Inspect remote service logs for why it returned 401/403/500 for this principal.
- Confirm network/proxy/TLS configuration between CAS and the endpoint (status 404/502/503 usually means infrastructure, not policy).
Example fix
// before
management: # wrong path
cas:
authn:
policy:
rest:
url: https://auth.example.com/api/wrong
// after
cas:
authn:
policy:
rest:
url: https://auth.example.com/api/policy Defensive patterns
Strategy: try-catch
Validate before calling
val probe = HttpUtils.execute(HttpUtils.httpBuilder(properties.getUrl()).GET().build())
if (probe.getCode() != 200) throw new IllegalStateException("Policy REST endpoint not healthy: " + probe.getCode()) Try / catch
try { return policy.isSatisfiedBy(authentication, service, context); }
catch (GeneralSecurityException e) {
LOGGER.warn("RESTful authentication policy failed", e);
return AuthenticationPolicyExecutionResult.failure();
} Prevention
- Monitor the remote policy endpoint for availability
- Validate the URL with a curl smoke test after config changes
- Ensure the remote service returns 200 (not 204/201) for satisfied policy
When it happens
Trigger: Calling isSatisfiedBy on a policy backed by cas.authn.policy.rest[...] when the configured REST endpoint returns a non-200 status (401/403 for rejected credentials, 404 for a wrong URL, 500 for a backend crash) for the given principal.
Common situations: REST endpoint URL misconfigured or pointing at the wrong context path; the remote service rejects the principal (blocked/locked account); the remote service is down and a gateway returns 502/503; TLS or proxy issues producing error responses; API version change so the endpoint no longer returns 200 for success.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Unable to accept response status
- No credentials can be extracted to authenticate the REST…
- Failed to save google authenticator account
- Unable to successfully fetch JWKS resource from
- Failed to load configuration metadata
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/3f98b3316500697f.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/policy/RestfulAuthenticationPolicy.java:96
val principal = authentication.getPrincipal();
try {
val entity = MAPPER.writeValueAsString(principal);
val headers = CollectionUtils.<String, String>wrap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
headers.putAll(Objects.requireNonNull(properties).getHeaders());
val exec = HttpExecutionRequest.builder()
.url(properties.getUrl())
.basicAuthUsername(properties.getBasicAuthUsername())
.basicAuthPassword(properties.getBasicAuthPassword())
.method(HttpMethod.POST)
.entity(entity)
.headers(headers)
.maximumRetryAttempts(properties.getMaximumRetryAttempts())
.build();
response = HttpUtils.execute(exec);
val statusCode = HttpStatus.valueOf(response.getCode());
if (statusCode != HttpStatus.OK) {
val ex = handleResponseStatusCode(statusCode, principal);
throw new GeneralSecurityException(ex);
}
return AuthenticationPolicyExecutionResult.success();
} finally {
HttpUtils.close(response);
}
}
@Override
public Map<String, Object> toConfiguration() {
val config = super.toConfiguration();
config.put("url", properties.getUrl());
config.put("basicAuthUsername", properties.getBasicAuthUsername());
config.put("basicAuthPassword", properties.getBasicAuthPassword());
config.put("maximumRetryAttempts", properties.getMaximumRetryAttempts());
config.put("headers", properties.getHeaders());
return config;
}
}View on GitHub (pinned to e7288fc434)