apereo/cas · error

INVALID_PROXY_CALLBACK

INVALID_PROXY_CALLBACK

Error message

Failed to authenticate service credential [{}]

What it means

During proxy ticket validation, CAS extracted a service credential (e.g. a PGT callback's proxy credentials) from the request but failed to authenticate it, so handleTicketValidation catches AuthenticationException and returns an INVALID_PROXY_CALLBACK error view. The proxy callback chain could not be completed.

Solutions

  1. Verify the pgtUrl/proxy callback endpoint is reachable over HTTPS and presents valid credentials; test the callback URL manually.
  2. Check the CAS log for the underlying AuthenticationException detail to identify which authentication handler rejected the service credential.
  3. Remove the pgtUrl parameter if proxy authentication is not actually needed, so no service credential is extracted and authenticated.

Example fix

// before
GET /p3/proxyValidate?ticket=PT-1-abc&pgtUrl=https://myapp.example.org/callback-bad

// after (valid, reachable callback or none)
GET /p3/proxyValidate?ticket=PT-1-abc&pgtUrl=https://myapp.example.org/pgtCallback
Defensive patterns

Strategy: try-catch

Validate before calling

// before requesting proxy tickets, verify pgtUrl is reachable and TLS-valid
const resp = await fetch(pgtUrl, { method: 'HEAD' });
if (!resp.ok) throw new Error('pgtUrl callback is not reachable: ' + pgtUrl);

Try / catch

try {
    proxyGrantingTicket = handleProxyGrantingTicketDelivery(serviceTicketId, credential);
} catch (AuthenticationException e) {
    // INVALID_PROXY_CALLBACK: fix/verify pgtUrl credentials and reachability
}

Prevention

When it happens

Trigger: handleTicketValidation calls getServiceCredentialsFromRequest (with pgtUrl callback processing enabled), gets a non-null credential, then handleProxyGrantingTicketDelivery authenticates it; the thrown AuthenticationException triggers the warn and the INVALID_PROXY_CALLBACK error response.

Common situations: pgtUrl points to an endpoint whose client certificate/credentials fail authentication; custom credential-to-handler setup mismatched; proxy callback URL unreachable or returning unexpected content causing the auth flow to fail.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/3017e2076ff94dee. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-validation-core/src/main/java/org/apereo/cas/web/AbstractServiceValidateController.java:170

    protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) {
        if (serviceValidateConfigurationContext.getCasProperties().getSso().isRenewAuthnEnabled()) {
            binder.setRequiredFields(CasProtocolConstants.PARAMETER_RENEW);
        }
    }

    protected void prepareForTicketValidation(final HttpServletRequest request, final WebApplicationService service, final String serviceTicketId) {
    }

    protected ModelAndView handleTicketValidation(final HttpServletRequest request,
                                                  final HttpServletResponse response,
                                                  final WebApplicationService service, final String serviceTicketId) throws Throwable {
        var proxyGrantingTicket = (Ticket) null;
        val serviceCredential = getServiceCredentialsFromRequest(service, request);
        if (serviceCredential != null) {
            try {
                proxyGrantingTicket = handleProxyGrantingTicketDelivery(serviceTicketId, serviceCredential);
            } catch (final AuthenticationException e) {
                LOGGER.warn("Failed to authenticate service credential [{}]", serviceCredential);
                val description = getTicketValidationErrorDescription(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
                    new Object[]{serviceCredential.getId()}, request);
                return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK, description, request, service);
            } catch (final InvalidTicketException e) {
                LOGGER.error("Failed to create proxy granting ticket due to an invalid ticket for [{}]", serviceCredential);
                LoggingUtils.error(LOGGER, e);
                val description = getTicketValidationErrorDescription(e.getCode(), new Object[]{serviceTicketId}, request);
                return generateErrorView(e.getCode(), description, request, service);
            } catch (final AbstractTicketException e) {
                LOGGER.error("Failed to create proxy granting ticket for [{}]", serviceCredential);
                LoggingUtils.error(LOGGER, e);
                val description = getTicketValidationErrorDescription(e.getCode(), new Object[]{serviceCredential.getId()}, request);
                return generateErrorView(e.getCode(), description, request, service);
            }
        }
        val assertion = validateServiceTicket(service, serviceTicketId);
        if (!validateAssertion(request, serviceTicketId, assertion, service)) {
            val description = getTicketValidationErrorDescription(CasProtocolConstants.ERROR_CODE_INVALID_TICKET, new Object[]{serviceTicketId}, request);

View on GitHub (pinned to e7288fc434)