{"record":{"id":"dce52e97722199f0","repo":"apereo/cas","slug":"spnego-authorization-header-is-not-found-under","errorCode":null,"errorMessage":"SPNEGO Authorization header is not found under [{}]","messagePattern":"SPNEGO Authorization header is not found under \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-spnego-webflow/src/main/java/org/apereo/cas/web/flow/SpnegoCredentialsAction.java","lineNumber":62,"sourceCode":"    public SpnegoCredentialsAction(final CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver,\n                                   final CasWebflowEventResolver serviceTicketRequestWebflowEventResolver,\n                                   final AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy,\n                                   final boolean send401OnAuthenticationFailure) {\n        super(initialAuthenticationAttemptWebflowEventResolver, serviceTicketRequestWebflowEventResolver, adaptiveAuthenticationPolicy);\n        this.send401OnAuthenticationFailure = send401OnAuthenticationFailure;\n    }\n\n    @Override\n    protected Credential constructCredentialsFromRequest(final RequestContext context) {\n        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);\n\n        LOGGER.debug(\"Available request headers are [{}]\", Collections.list(request.getHeaderNames()));\n        val authorizationHeader = StringUtils.defaultIfBlank(\n            request.getHeader(HttpHeaders.AUTHORIZATION),\n            request.getHeader(HttpHeaders.AUTHORIZATION.toLowerCase(Locale.ENGLISH)));\n        LOGGER.debug(\"SPNEGO Authorization header located as [{}]\", authorizationHeader);\n        if (StringUtils.isBlank(authorizationHeader)) {\n            LOGGER.warn(\"SPNEGO Authorization header is not found under [{}]\", HttpHeaders.AUTHORIZATION);\n            return null;\n        }\n\n        val authzHeaderLength = authorizationHeader.length();\n        val prefixLength = SpnegoConstants.NEGOTIATE.length();\n        if (authzHeaderLength > prefixLength && authorizationHeader.startsWith(SpnegoConstants.NEGOTIATE)) {\n            LOGGER.debug(\"SPNEGO Authorization header found with [{}] bytes\", authzHeaderLength - prefixLength);\n            val base64 = authorizationHeader.substring(prefixLength);\n            val token = EncodingUtils.decodeBase64(base64);\n            val tokenString = new String(token, Charset.defaultCharset());\n            LOGGER.debug(\"Obtained token: [{}]. Creating credential...\", tokenString);\n            return new SpnegoCredential(token);\n        }\n        LOGGER.warn(\"SPNEGO Authorization header [{}] does not begin with the prefix [{}]\",\n            authorizationHeader, SpnegoConstants.NEGOTIATE);\n        return null;\n    }\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-spnego-webflow/src/main/java/org/apereo/cas/web/flow/SpnegoCredentialsAction.java#L44-L80","documentation":"CAS's SPNEGO webflow action tries to build SPNEGO credentials from the HTTP Authorization header. When the header is absent or blank, it logs this warning and returns null so the webflow falls back (typically to prompting the client for Negotiate). It is a diagnostic for the client not having sent a Kerberos/SPNEGO token at all.","triggerScenarios":"Browser or HTTP client hits /login with SPNEGO enabled but sends no Authorization header (or sends a blank one); getHeader('Authorization') and its lowercase variant both return null/blank in constructCredentialsFromRequest.","commonSituations":"Direct navigation (not via an intranet SSO-aware app) so the browser never pre-authenticates; browser SPNEGO/Integrated Authentication disabled or the CAS host not in the trusted-URI/intranet-sites list; reverse proxy stripping the Authorization header; non-browser clients (curl, scripts) calling the endpoint.","solutions":["Ensure the CAS server hostname is added to the browser's SPNEGO trusted sites / intranet zone so it sends the Negotiate token.","Verify the request actually reaches CAS with the header intact (check reverse proxy/ingress rules that strip Authorization).","Test with curl --negotiate -u : <cas-login-url> to confirm header transmission.","Confirm the SPNEGO webflow is configured so clients are redirected to negotiate instead of expecting a header on first hit."],"exampleFix":"// before\ncurl https://cas.example.org/login\n// after\ncurl --negotiate -u : https://cas.example.org/login","handlingStrategy":"validation","validationCode":"String authz = request.getHeader(\"Authorization\");\nif (authz == null || authz.isBlank()) {\n    // skip SPNEGO / fall back to form login before invoking CAS action\n}","typeGuard":"static boolean hasNegotiateHeader(HttpServletRequest r) {\n    String h = r.getHeader(\"Authorization\");\n    return h != null && h.regionMatches(true, 0, \"Negotiate \", 0, \"Negotiate \".length());\n}","tryCatchPattern":null,"preventionTips":["Add CAS host to browser intranet/trusted SPNEGO sites","Never let proxies strip Authorization","Smoke-test with curl --negotiate regularly"],"tags":["spnego","http-header","kerberos","sso"],"backgroundTag":"missing-credentials","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"}