{"record":{"id":"5cbce587707cd09b","repo":"apereo/cas","slug":"no-client-ip-or-user-agent-was-provided-skipping","errorCode":null,"errorMessage":"No client IP or user-agent was provided. Skipping adaptive authentication policy...","messagePattern":"No client IP or user-agent was provided\\. Skipping adaptive authentication policy\\.\\.\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/adaptive/DefaultAdaptiveAuthenticationPolicy.java","lineNumber":38,"sourceCode":" * @author Misagh Moayyed\n * @since 5.1.0\n */\n@Slf4j\n@RequiredArgsConstructor\npublic class DefaultAdaptiveAuthenticationPolicy implements AdaptiveAuthenticationPolicy {\n\n    private final GeoLocationService geoLocationService;\n\n    private final IPAddressIntelligenceService ipAddressIntelligenceService;\n\n    private final AdaptiveAuthenticationProperties adaptiveAuthenticationProperties;\n\n    @Override\n    public boolean isAuthenticationRequestAllowed(final RequestContext requestContext, final String userAgent,\n                                                  final GeoLocationRequest location) throws Throwable {\n        val clientInfo = ClientInfoHolder.getClientInfo();\n        if (clientInfo == null || StringUtils.isBlank(userAgent)) {\n            LOGGER.warn(\"No client IP or user-agent was provided. Skipping adaptive authentication policy...\");\n            return true;\n        }\n        val clientIp = clientInfo.getClientIpAddress();\n        if (isIpAddressRejected(requestContext, clientIp)) {\n            LOGGER.warn(\"Client IP [{}] is rejected for authentication\", clientIp);\n            return false;\n        }\n\n        if (isUserAgentRejected(userAgent)) {\n            LOGGER.warn(\"User agent [{}] is rejected for authentication\", userAgent);\n            return false;\n        }\n        LOGGER.debug(\"User agent [{}] is authorized to proceed\", userAgent);\n        if (this.geoLocationService != null && location != null && StringUtils.isNotBlank(clientIp)\n            && StringUtils.isNotBlank(this.adaptiveAuthenticationProperties.getPolicy().getRejectCountries())) {\n            val loc = this.geoLocationService.locate(clientIp, location);\n            if (loc != null) {\n                LOGGER.debug(\"Determined geolocation for [{}] to be [{}]\", clientIp, loc);","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/adaptive/DefaultAdaptiveAuthenticationPolicy.java#L20-L56","documentation":"DefaultAdaptiveAuthenticationPolicy.isAuthenticationRequestAllowed skips all adaptive checks (IP rejection, user-agent rejection, geo/country rejection) and returns true when ClientInfoHolder has no client info or the user-agent string is blank. Adaptive policy is effectively bypassed for that request.","triggerScenarios":"ClientInfoHolder.getClientInfo() returns null (no ClientInfoFilter/filter servlet not applied, e.g. non-web or mis-ordered filter chain) or the userAgent parameter is blank when the webflow action invokes the policy.","commonSituations":"Reverse proxy not forwarding X-Forwarded-For so client info is empty; CasClientInfoHttpRequestFilter removed/ordered out; internal calls to the flow (e.g. REST/scripted) without headers; tests invoking the policy directly.","solutions":["Ensure the client info filter is registered and ordered first so ClientInfoHolder is populated.","Configure the reverse proxy to forward X-Forwarded-For and User-Agent headers.","Verify cas.authn.adaptive.policy configuration exists and the request carries a User-Agent.","If bypass is unacceptable, fail closed by wrapping the policy and rejecting requests with missing client info."],"exampleFix":"// before: filter missing, adaptive policy silently bypassed\n// after: register the filter\n@Bean\npublic FilterRegistrationBean<CasClientInfoHttpRequestFilter> clientInfoFilter(\n        final CasClientInfoHttpRequestFilter filter) {\n    val reg = new FilterRegistrationBean<>(filter);\n    reg.setOrder(Ordered.HIGHEST_PRECEDENCE);\n    return reg;\n}","handlingStrategy":"fallback","validationCode":"if (ClientInfoHolder.getClientInfo() == null || StringUtils.isBlank(request.getHeader(\"User-Agent\"))) {\n    LOGGER.warn(\"Client info/user-agent missing — adaptive policy will be skipped\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the client-info servlet filter is present and highest precedence after proxy/LB changes.","Monitor for skipped adaptive checks in logs; silent bypass hides policy gaps.","Fail closed in a wrapper if adaptive enforcement is a hard requirement."],"tags":["adaptive-authentication","client-info","bypass"],"backgroundTag":"missing-configuration","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}