apereo/cas · error · FailedLoginException
cannot be authorized
Error message
<callbackUrl> cannot be authorized
What it means
ProxyAuthenticationHandler.authenticate throws FailedLoginException when the service's ProxyPolicy refuses to authorize the PGT callback URL presented by the HttpBasedServiceCredential. Proxy authentication (proxy-granting tickets) requires the callback URL to pass the service's proxy policy (e.g. RegisteredServiceProxyPolicy regex), otherwise the proxy credential is rejected before any HTTP validation.
Solutions
- Update the registered service's proxyPolicy (e.g. RegexMatchingRegisteredServiceProxyPolicy) pattern to include the callback URL.
- Ensure the callback URL scheme/host matches the policy exactly (https vs http, ports, query strings).
- If proxying is intended, confirm the service definition allows proxy authentication and the access strategy permits it.
- Change the client application's pgtUrl to one already authorized by the proxy policy.
- Check the WARN log naming the service and callback URL to see the exact mismatch.
Example fix
// before (service JSON)
"proxyPolicy": { "@class": "org.apereo.cas.services.RefuseRegisteredServiceProxyPolicy" }
// after
"proxyPolicy": { "@class": "org.apereo.cas.services.RegexMatchingRegisteredServiceProxyPolicy", "pattern": "^https://app\\.example\\.org/.*" } Defensive patterns
Strategy: validation
Validate before calling
// before requesting a PGT, check the policy client-side if you know the service def
boolean allowed = registeredService.getProxyPolicy()
.isAllowedProxyCallbackUrl(registeredService, callbackUrl);
if (!allowed) { throw new IllegalArgumentException("Callback URL not authorized: " + callbackUrl); } Try / catch
try {
return handler.authenticate(credential, service);
} catch (FailedLoginException e) {
if (e.getMessage() != null && e.getMessage().endsWith("cannot be authorized")) {
LOGGER.error("Proxy callback URL [{}] rejected by service proxy policy", ((HttpBasedServiceCredential) credential).getCallbackUrl(), e);
}
throw e;
} Prevention
- Keep proxy policy regexes in the service registry aligned with every pgtUrl your clients use.
- Prefer https callback URLs and ensure patterns cover scheme, host, and port.
- When changing a client's callback URL, update the registered service definition in the same change.
When it happens
Trigger: A PGT callback credential (pgtUrl) is submitted and httpCredential.getService().getProxyPolicy().isAllowedProxyCallbackUrl(...) returns false — the callback URL doesn't match the registered service's proxy pattern or the proxy policy denies all callbacks.
Common situations: Registered service has proxying disabled or its proxy policy regex doesn't match the callback host/URL; callback URL uses http while the pattern allows only https; service registry entry updated and the old proxy pattern no longer matches; client app changed its pgtUrl after deployment.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- Denied
- Authentication handler is disabled
- No user can be accepted because none is defined
- not found in backing map.
- Unable to authenticate
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/53411212dfa679fd.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/handler/support/ProxyAuthenticationHandler.java:45
@Slf4j
public class ProxyAuthenticationHandler extends AbstractAuthenticationHandler {
private final HttpClient httpClient;
public ProxyAuthenticationHandler(final String name,
final PrincipalFactory principalFactory,
final Integer order, final HttpClient httpClient) {
super(name, principalFactory, order);
this.httpClient = httpClient;
}
@Override
public AuthenticationHandlerExecutionResult authenticate(final Credential credential, final Service service) throws Throwable {
val httpCredential = (HttpBasedServiceCredential) credential;
if (!httpCredential.getService().getProxyPolicy()
.isAllowedProxyCallbackUrl(httpCredential.getService(), httpCredential.getCallbackUrl())) {
LOGGER.warn("Proxy policy for service [{}] cannot authorize the requested callback url [{}].",
httpCredential.getService(), httpCredential.getCallbackUrl());
throw new FailedLoginException(httpCredential.getCallbackUrl() + " cannot be authorized");
}
LOGGER.debug("Attempting to authenticate [{}]", httpCredential);
val callbackUrl = httpCredential.getCallbackUrl();
if (!httpClient.isValidEndPoint(callbackUrl)) {
throw new FailedLoginException(callbackUrl.toExternalForm() + " sent an unacceptable response status code");
}
val principalId = httpCredential.getCredentialMetadata().getId();
val proxyPrincipal = principalFactory.createPrincipal(principalId);
return new DefaultAuthenticationHandlerExecutionResult(this, httpCredential, proxyPrincipal);
}
@Override
public boolean supports(final Credential credential) {
return credential instanceof HttpBasedServiceCredential;
}
@OverrideView on GitHub (pinned to e7288fc434)