apereo/cas · warning · UnauthorizedAuthenticationException

Adaptive authentication policy does not allow this request…

Error message

Adaptive authentication policy does not allow this request for [agent] and [geoLocation]

What it means

AbstractAuthenticationAction checks adaptive authentication policy (user-agent and geolocation) before granting an 'authenticate' event in the login webflow. When the policy rejects the request's device agent or geo location, it logs a warning and returns an 'error' event carrying an UnauthorizedAuthenticationException, driving the webflow to the authentication-failure transition.

Solutions

  1. Update cas.authn.adaptive.expected-user-agent or the adaptive policy config to allow the rejected agent/geo
  2. Remove or narrow the adaptive authentication policy that is blocking the request
  3. Check the CAS logs: the warn line names the exact agent and geoLocation values being denied
  4. If testing, set the request User-Agent to one matching the allowed pattern

Example fix

// before (application.properties)
cas.authn.adaptive.expected-user-agent=^Mozilla.*Chrome$
// after
cas.authn.adaptive.expected-user-agent=^Mozilla.*(Chrome|Firefox|Safari).*|CAS-Automation-Client
Defensive patterns

Strategy: validation

Validate before calling

// Before submitting login flow, check adaptive policy client-side
String userAgent = request.getHeader("User-Agent");
if (userAgent == null || !userAgent.matches(allowedAgentPattern)) {
    // adapt or skip adaptive-protected flow
}

Prevention

When it happens

Trigger: A login request whose User-Agent or GeoLocation from the request context fails the configured adaptive/expected-user-agent or risk/geo rules; the policy engine returns false so doExecuteInternal builds the failure event.

Common situations: Admins enabled cas.authn.adaptive policy for a user-agent regex or IP/geo range that unintentionally blocks legitimate browsers; clients behind new proxies present unlisted agents; testers run curl/automation agents not whitelisted; geolocation data missing or resolving to a disallowed country.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/actions/AbstractAuthenticationAction.java:46

@Slf4j
public abstract class AbstractAuthenticationAction extends BaseCasWebflowAction {

    private final CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver;

    private final CasWebflowEventResolver serviceTicketRequestWebflowEventResolver;

    private final AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy;

    @Override
    protected @Nullable Event doExecuteInternal(final RequestContext requestContext) throws Throwable {
        if (!evaluateAdaptiveAuthenticationPolicy(requestContext)) {
            val agent = WebUtils.getHttpServletRequestUserAgentFromRequestContext(requestContext);
            val geoLocation = WebUtils.getHttpServletRequestGeoLocationFromRequestContext(requestContext);

            val msg = "Adaptive authentication policy does not allow this request for " + agent + " and " + geoLocation;
            LOGGER.warn(msg);
            val map = CollectionUtils.<String, Throwable>wrap(UnauthorizedAuthenticationException.class.getSimpleName(),
                new UnauthorizedAuthenticationException(msg));
            val error = new AuthenticationException(msg, map, new HashMap<>());
            val event = new Event(this, CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE,
                new LocalAttributeMap<>(CasWebflowConstants.TRANSITION_ID_ERROR, error));
            fireEventHooks(event, requestContext);
            return event;
        }

        val serviceTicketEvent = serviceTicketRequestWebflowEventResolver.resolveSingle(requestContext);
        if (serviceTicketEvent != null) {
            fireEventHooks(serviceTicketEvent, requestContext);
            return serviceTicketEvent;
        }

        val finalEvent = initialAuthenticationAttemptWebflowEventResolver.resolveSingle(requestContext);
        fireEventHooks(finalEvent, requestContext);
        return finalEvent;
    }

View on GitHub (pinned to e7288fc434)