spring-projects/spring-security · warning
Negotiate Header was invalid: %s
Error message
Negotiate Header was invalid: %s
What it means
SpnegoAuthenticationProcessingFilter.doFilterInternal logs this warning when the AuthenticationManager throws an AuthenticationException while authenticating the SpnegoAuthenticationToken built from the Authorization: Negotiate header. Since a bad client token normally means 'continue the handshake' rather than an exception, this signals a server-side problem: Kerberos validation of the ticket failed. The security context is cleared and the failure handler runs (defaulting to HTTP 500).
Source
Thrown at kerberos/kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoAuthenticationProcessingFilter.java:184
|| header.startsWith("Kerberos "))) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Received Negotiate Header for request " + request.getRequestURL() + ": " + header);
}
byte[] base64Token = header.substring(header.indexOf(" ") + 1).getBytes("UTF-8");
byte[] kerberosTicket = Base64.getDecoder().decode(base64Token);
KerberosServiceRequestToken authenticationRequest = new KerberosServiceRequestToken(kerberosTicket);
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
Authentication authentication;
try {
if (this.authenticationManager == null) {
throw new IllegalStateException("authenticationManager must be set");
}
authentication = this.authenticationManager.authenticate(authenticationRequest);
}
catch (AuthenticationException ex) {
// That shouldn't happen, as it is most likely a wrong
// configuration on the server side
this.logger.warn("Negotiate Header was invalid: " + header, ex);
this.securityContextHolderStrategy.clearContext();
if (this.failureHandler != null) {
this.failureHandler.onAuthenticationFailure(request, response, ex);
}
else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.flushBuffer();
}
return;
}
this.sessionStrategy.onAuthentication(authentication, request, response);
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
this.securityContextHolderStrategy.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.successHandler != null) {
this.successHandler.onAuthenticationSuccess(request, response, authentication);View on GitHub (pinned to 96852e8860)
Solutions
- Verify the keytab matches the service principal and the SPN the client uses (HTTP/fqdn@REALM); re-export with ktpass/kadmin if needed and confirm with `klist -k keytab`.
- Check the full warning stack trace in the logs — the underlying GSS/KrbException (e.g. 'Clock skew too great', 'Key version number for principal in key table is incorrect') names the root cause.
- Ensure server clock sync with the KDC (NTP) within the 5-minute skew window, and confirm kvno consistency between KDC and keytab.
- Validate KDC reachability and krb5.conf (default_realm, realms, dns_lookup) on the app server; test with `kinit -k -t keytab principal`.
Example fix
// before (bean wiring SPN that doesn't match the keytab)
validator.setServicePrincipal("HTTP/wrong-host@EXAMPLE.COM");
// after
validator.setServicePrincipal("HTTP/app.example.com@EXAMPLE.COM"); // matches keytab & client SPN
validator.setKeyTabLocation(new FileSystemResource("/etc/security/app.keytab")); Defensive patterns
Strategy: try-catch
Validate before calling
// before deploying: verify keytab/SPN/KDC from the server host // klist -k /etc/security/app.keytab // kinit -k -t /etc/security/app.keytab HTTP/app.example.com@EXAMPLE.COM
Try / catch
The filter handles the AuthenticationException internally (clears the context, invokes failureHandler, default 500). Provide a custom failureHandler: filter.setFailureHandler((req, res, ex) -> { log.warn("SPNEGO failed", ex); res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid Negotiate token"); }); and correlate with the logged stack trace for the root KrbException. Prevention
- Match service principal exactly to the SPN clients request (HTTP/<fqdn>@REALM); check with setspn -L / kadmin lookups.
- Keep server clocks NTP-synced with the KDC (skew limit ~5 minutes).
- Ensure krb5.conf and JAAS config on the server are correct and the KDC is reachable from the app host.
- Keep keytab kvno in sync with the KDC; regenerate via ktpass/kadmin after password/key changes.
- Monitor for this warning — recurring occurrences indicate server/KDC configuration, not client issues.
When it happens
Trigger: A browser sends a Negotiate header with a service ticket, SunJaasKerberosTicketValidator fails to validate it (keytab mismatch with the service principal, wrong SPN registered by the client, clock skew > 5 min, ticket replay, KDC unreachable), and authenticationManager.authenticate() throws, hitting the catch block at SpnegoAuthenticationProcessingFilter.java:184.
Common situations: Keytab generated for a different SPN than the URL the client resolved (e.g. HTTP/host@REALM mismatch); duplicate SPN registrations (setspn -X); AD/FreeIPA KDC not reachable from the app server; host clocks out of sync; outdated kerb5.conf/JAAS config; replayed cached tickets from a load-balanced setup without proper SPN setup.
Related errors
- <ticket validation failure message>
- Error running rest call
- Unknown Callback
- credentials cannot be null
- ticketValidator must be set
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/54882cb7aa0304eb.
Report an issue: GitHub.