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);
            return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_TICKET, description, request, service);
        }

        val ctxResult = serviceValidateConfigurationContext.getRequestedContextValidator()
            .validateAuthenticationContext(assertion, request, response);
        if (!ctxResult.isSuccess()) {
            throw new UnsatisfiedAuthenticationContextTicketValidationException(assertion.getService());
        }

        var proxyIou = StringUtils.EMPTY;
        val proxyHandler = serviceValidateConfigurationContext.getProxyHandler();
        if (serviceCredential != null && proxyHandler != null && proxyHandler.canHandle(serviceCredential)) {
            val registeredService = ((HttpBasedServiceCredential) serviceCredential).getService();
            val authorizedToReleaseProxyGrantingTicket = registeredService.getAttributeReleasePolicy().isAuthorizedToReleaseProxyGrantingTicket();
            if (authorizedToReleaseProxyGrantingTicket) {
                LOGGER.debug("Service [{}] is authorized to release the PGT directly, skip the proxy callback", registeredService);
            } else {
                LOGGER.debug("Service [{}] is not authorized to release the PGT directly, make a proxy callback", registeredService);
                proxyIou = handleProxyIouDelivery(serviceCredential, proxyGrantingTicket);
                if (StringUtils.isEmpty(proxyIou)) {
                    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);
                }
            }
        } else {
            LOGGER.debug("No service credentials specified, and/or the proxy handler [{}] cannot handle credentials", proxyHandler);
        }
        onSuccessfulValidation(serviceTicketId, assertion);
        LOGGER.debug("Successfully validated service ticket [{}] for service [{}]", serviceTicketId, service.getId());
        return generateSuccessView(assertion, proxyIou, service, request, ctxResult.getContextId(), proxyGrantingTicket);
    }
Add validation specification.

@param validationSpecification the validation specification
support/cas-server-support-validation-core/src/main/java/org/apereo/cas/web/AbstractServiceValidateController.java