{"record":{"id":"a510f4fb100cd1a2","repo":"apereo/cas","slug":"user-agent-header-is-not-supported-in-the-lis","errorCode":null,"errorMessage":"User Agent header [{}] is not supported in the list of supported browsers [{}]","messagePattern":"User Agent header \\[(.+?)\\] is not supported in the list of supported browsers \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-spnego-webflow/src/main/java/org/apereo/cas/web/flow/SpnegoNegotiateCredentialsAction.java","lineNumber":75,"sourceCode":"     */\n    private final boolean mixedModeAuthentication;\n\n    @Override\n    protected @Nullable Event doExecuteInternal(final RequestContext context) {\n        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);\n        val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context);\n\n        val authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);\n        val userAgent = HttpRequestUtils.getHttpServletRequestUserAgent(request);\n\n        LOGGER.debug(\"Authorization header [{}], User Agent header [{}]\", authorizationHeader, userAgent);\n        if (!StringUtils.hasText(userAgent) || this.supportedBrowser.isEmpty()) {\n            LOGGER.warn(\"User Agent header [{}] is empty, or no browsers are supported\", userAgent);\n            return error();\n        }\n\n        if (!isSupportedBrowser(userAgent)) {\n            LOGGER.warn(\"User Agent header [{}] is not supported in the list of supported browsers [{}]\",\n                userAgent, this.supportedBrowser);\n            return error();\n        }\n\n        if (!StringUtils.hasText(authorizationHeader)\n            || !authorizationHeader.startsWith(SpnegoConstants.NEGOTIATE)\n            || authorizationHeader.length() <= SpnegoConstants.NEGOTIATE.length()) {\n\n            LOGGER.debug(\"Authorization header not found or does not match the message prefix [{}]. Sending [{}] header [{}]\",\n                SpnegoConstants.NEGOTIATE, SpnegoConstants.HEADER_AUTHENTICATE, SpnegoConstants.NEGOTIATE);\n            response.setHeader(SpnegoConstants.HEADER_AUTHENTICATE, SpnegoConstants.NEGOTIATE);\n\n            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n            /*\n             The responseComplete flag tells the pausing view-state not to render the response\n             because another object has taken care of it. If mixed mode authentication is allowed\n             then responseComplete should not be called so that webflow will display the login page.\n              */","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-spnego-webflow/src/main/java/org/apereo/cas/web/flow/SpnegoNegotiateCredentialsAction.java#L57-L93","documentation":"SpnegoNegotiateCredentialsAction only initiates SPNEGO negotiation for user agents on the configured supported-browsers list. If the request's User-Agent header is empty or does not match any configured supported browser pattern, it logs this warning and returns the webflow error so a normal (form) login proceeds. This is the deliberate gate that keeps SPNEGO for known-capable browsers.","triggerScenarios":"doExecuteInternal receives a request whose User-Agent is blank, or cas.authn.spnego[...].supportedBrowsers patterns (default targeting MSIE/Trident, Firefox, AppleWebKit) do not regex-match the UA string, causing isSupportedBrowser to return false.","commonSituations":"Non-browser clients (curl, Java HttpClient, mobile apps) hitting CAS; very new or exotic browser UA strings not matching the default patterns; proxy/gateway scrubbing the User-Agent header; supportedBrowsers misconfigured to a too-narrow regex.","solutions":["Add the client's user-agent pattern to cas.authn.spnego.supported-browsers (regex list).","If the client is a script/app, either bypass SPNEGO or use an alternative auth mechanism (Basic/OAuth) instead of forcing Negotiate.","Check that proxies do not strip or rewrite the User-Agent header.","Keep the default wildcard-inclusive patterns if you want broad browser coverage."],"exampleFix":"// before\ncas.authn.spnego.supported-browsers=MSIE,Trident\n// after\ncas.authn.spnego.supported-browsers=MSIE,Trident,Firefox,AppleWebKit,Chrome,Edg","handlingStrategy":"validation","validationCode":"String ua = request.getHeader(\"User-Agent\");\nboolean supported = supportedBrowsers.stream().anyMatch(p -> ua != null && ua.matches(p));\nif (!supported) { /* route to form login instead of negotiate */ }","typeGuard":"static boolean hasUserAgent(HttpServletRequest r) {\n    String ua = r.getHeader(\"User-Agent\");\n    return ua != null && !ua.isBlank();\n}","tryCatchPattern":null,"preventionTips":["Keep supported-browsers patterns broad and current","Don't strip User-Agent at the edge proxy","Use form-login fallback for scripts/APIs"],"tags":["spnego","user-agent","browser-detection","configuration"],"backgroundTag":"unsupported-config-value","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"}