Skip to content

Return SPNEGO mutual authentication token - #51560

Open
aw1cks wants to merge 2 commits into
keycloak:mainfrom
aw1cks-forks:main
Open

Return SPNEGO mutual authentication token#51560
aw1cks wants to merge 2 commits into
keycloak:mainfrom
aw1cks-forks:main

Conversation

@aw1cks

@aw1cks aw1cks commented Aug 8, 2026

Copy link
Copy Markdown

Summary

Closes #51559

Propagate the final SPNEGO response token when the GSS security context is established and return it in the WWW-Authenticate header of the authentication response.

Both the standalone Kerberos provider and the LDAP provider now include a generated response token in authenticated credential-validation state.
The browser SPNEGO authenticator extracts that transient protocol state, adds it to the HTTP response, and persists only the remaining credential state as user-session notes.

The GSS output token is optional.
SPNEGOAuthenticator now handles null and empty output tokens without failing authentication or generating a malformed empty Negotiate challenge.
It continues to use basic Base64 encoding because MIME Base64 can insert CRLF characters and is unsuitable for HTTP header values.

This allows clients that request SPNEGO mutual authentication to validate the Keycloak server and complete the exchange.

This follows RFC 4178 section 3.2, which requires a response mechanism token returned by the acceptor to be included in the SPNEGO response.
RFC 4559 section 4.1 and section 5 carry that final token as gssapi-data in the WWW-Authenticate header of the final HTTP response.

RFC 4559 illustrates a 200 final response rather than Keycloak's authorization-endpoint redirect.
Keycloak concludes successful browser authentication with a 302, and RFC 9110 section 11.6.1 permits WWW-Authenticate on response statuses other than 401.
The header is attached to the current HTTP response before the authentication flow continues because an authenticator does not own the flow's final JAX-RS response.

Testing

Extended spnegoMutualAuthenticationTest in the shared Kerberos integration test class.
The test runs through both KerberosStandaloneTest and KerberosLdapTest and verifies that:

  • The immediate successful redirect contains a non-empty WWW-Authenticate: Negotiate <token> header.
  • Processing the returned token establishes the initiating GSS context with mutual authentication enabled.
  • SpnegoResponseToken is not persisted as a user-session note or exposed through a configured session-note protocol mapper.

The header assertion failed against the original implementation because the final token was absent.

Verified with:

./mvnw -pl testsuite/integration-arquillian/tests/base -am install \
  -Dtest=org.keycloak.testsuite.federation.kerberos.KerberosStandaloneTest#spnegoMutualAuthenticationTest \
  -Dsurefire.failIfNoSpecifiedTests=false \
  -DskipProtoLock=true \
  -Dskip.installnodepnpm \
  -Dskip.pnpm \
  -Dmaven.antrun.skip=true

./mvnw -pl testsuite/integration-arquillian/tests/base -am install \
  -Dtest=org.keycloak.testsuite.federation.kerberos.KerberosLdapTest#spnegoMutualAuthenticationTest \
  -Dsurefire.failIfNoSpecifiedTests=false \
  -DskipProtoLock=true \
  -Dskip.installnodepnpm \
  -Dskip.pnpm \
  -Dmaven.antrun.skip=true

./mvnw spotless:check

Documentation

No documentation changes are required because this corrects the existing SPNEGO protocol behavior and does not introduce configuration or user-facing options.
The observable addition of the final response header is described above for reviewers and release-note generation.

AI disclosure

AI agents were used to help investigate the authentication flow, draft the implementation, and prepare the regression tests.
I reviewed and understand the complete change and am responsible for the submitted code.

Propagate the final GSS response token through Kerberos credential
validation and return it on the successful authentication response.

Closes keycloak#51559

Signed-off-by: Alex Wicks <alex@awicks.io>
Copilot AI balanced review requested due to automatic review settings August 8, 2026 14:33
@aw1cks
aw1cks requested review from a team as code owners August 8, 2026 14:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Propagates final SPNEGO mutual-authentication tokens to successful HTTP responses.

Changes:

  • Adds response tokens to authenticated Kerberos validation state.
  • Returns tokens through WWW-Authenticate without session persistence.
  • Adds coverage for standalone and LDAP Kerberos providers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
AbstractKerberosSingleRealmTest.java Tests successful mutual-authentication headers.
SpnegoAuthenticator.java Writes response tokens to HTTP headers.
LDAPStorageProvider.java Propagates LDAP Kerberos response tokens.
KerberosFederationProvider.java Propagates standalone Kerberos response tokens.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Handle optional GSS output tokens and verify that mutual
authentication completes without persisting the response token in user
sessions.

Closes keycloak#51559

Signed-off-by: Alex Wicks <alex@awicks.io>
Copilot AI review requested due to automatic review settings August 8, 2026 15:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SPNEGO mutual authentication response token is not returned after successful login

2 participants