apereo/cas · error

Interrupt response has blocked the authentication flow

Error message

Interrupt response has blocked the authentication flow

What it means

FinalizeInterruptFlowAction checks the interrupt response's execute behavior; when the user was instructed to acknowledge/blocked and the flow must not continue, CAS logs 'Interrupt response has blocked the authentication flow' and throws UnauthorizedServiceException.denied("Rejected"), aborting the webflow. This is the enforcement point of CAS's interrupt (notification/forced-action) feature.

Solutions

  1. Have the user accept the required interrupt message/options on the interrupt screen
  2. Review the service's interrupt policy (WebflowInterruptAttribute/registered service interrupt settings) if blocking is unintended
  3. Check the interrupt response stored for the user and remove stale blocked responses if the policy has changed
  4. Disable the interrupt feature for the service if it should not be enforced

Example fix

// before (service registered with mandatory interrupt the user never accepted)
"interrupt": true
// after (interrupt satisfied via accepted response or disabled)
"interrupt": false
Defensive patterns

Strategy: fallback

Validate before calling

// check the user's interrupt response state before entering the flow
val response = interruptInformer.findInterruptResponse(authentication, service);
if (response != null && response.isBlocked()) {
    // do not attempt login; route user to acknowledgment screen
}

Try / catch

try { flowExecutor.execute(...); } catch (UnauthorizedServiceException e) { LOGGER.info("Interrupt blocked flow for user"); redirectToInterruptScreen(); }

Prevention

When it happens

Trigger: A user finishes the interrupt screen with a response whose policy blocks continuation (e.g. required terms not accepted, mandatory notification not acknowledged, or a block URL configured — in the shown source an external redirect to accessUrl occurs for a block URL; otherwise the warn/exception path fires).

Common situations: Admins enabling forced interrupt messages (usage policy, terms of use) that users must accept; service access denied until the user completes the required interrupt action.

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


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

Appendix: source

Thrown at support/cas-server-support-interrupt-webflow/src/main/java/org/apereo/cas/interrupt/webflow/actions/FinalizeInterruptFlowAction.java:43

public class FinalizeInterruptFlowAction extends BaseCasWebflowAction {
    private final InterruptTrackingEngine interruptTrackingEngine;

    @Override
    protected @Nullable Event doExecuteInternal(final RequestContext requestContext) throws Throwable {
        val response = InterruptUtils.getInterruptFrom(requestContext);
        if (response.isBlock() && !requestContext.getRequestParameters().contains("link")) {
            val registeredService = WebUtils.getRegisteredService(requestContext);
            val accessUrl = Optional.ofNullable(registeredService)
                .map(service -> service.getAccessStrategy().getUnauthorizedRedirectUrl())
                .orElse(null);
            if (accessUrl != null) {
                val url = accessUrl.toURL().toExternalForm();
                val externalContext = requestContext.getExternalContext();
                externalContext.requestExternalRedirect(url);
                externalContext.recordResponseComplete();
                return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_STOP);
            }
            LOGGER.warn("Interrupt response has blocked the authentication flow");
            throw UnauthorizedServiceException.denied("Rejected");
        }

        if (requestContext.getRequestParameters().contains("link")) {
            val link = requestContext.getRequestParameters().get("link");
            LOGGER.debug("Finalizing interrupt flow with link [{}]", link);
            val validLink = response.getLinks().containsValue(link);
            if (!validLink) {
                LOGGER.warn("Link [{}] is not valid and is not part of the interrupt response", link);
                throw UnauthorizedServiceException.denied("Rejected");
            }
        }
        
        val authentication = WebUtils.getAuthentication(requestContext);
        interruptTrackingEngine.trackInterrupt(requestContext, response);
        WebUtils.putAuthentication(authentication, requestContext);
        WebUtils.putInterruptAuthenticationFlowFinalized(requestContext);
        return success();

View on GitHub (pinned to e7288fc434)