Skip to content

Conversation

@raccoonback
Copy link
Contributor

@raccoonback raccoonback commented Jun 23, 2025

Motivation

This PR adds support for SPNEGO (Kerberos) authentication to HttpClient, addressing #3079.
SPNEGO is widely used for HTTP authentication in enterprise environments, particularly those based on Kerberos.

Changes

SpnegoAuthProvider

Provides SPNEGO authentication by generating a Kerberos-based token and attaching it to the Authorization header of outgoing HTTP requests.

JaasAuthenticator

Provides a pluggable way to perform JAAS-based Kerberos login, making it easy to integrate with various authentication backends.

HttpClient.spnego(...) API

Adds a new API to configure SPNEGO authentication for HttpClient instances.

HttpClient client = HttpClient.create()
    .spnego(SpnegoAuthProvider.create(new JaasAuthenticator("KerberosLogin")));

client.get()
      .uri("http://protected.example.com/")
      .responseSingle((res, content) -> content.asString())
      .block();

jaas.conf

A JAAS(Java Authentication and Authorization Service) configuration file in Java for integrating with authentication backends such as Kerberos.

KerberosLogin {
    com.sun.security.auth.module.Krb5LoginModule required
    client=true
    useKeyTab=true
    keyTab="/path/to/test.keytab"
    principal="test@EXAMPLE.COM"
    doNotPrompt=true
    debug=true;
};

krb5.conf

krb5.conf is a Kerberos client configuration file used to define how the client locates and communicates with the Kerberos Key Distribution Center (KDC) for authentication.

[libdefaults]
    default_realm = EXAMPLE.COM
[realms]
    EXAMPLE.COM = {
        kdc = kdc.example.com
    }
[domain_realms]
    .example.com = EXAMPLE.COM
    example.com = EXAMPLE.COM

How It Works

  • When a server responds with 401 Unauthorized and a WWW-Authenticate: Negotiate header,
    the client automatically generates a SPNEGO token using the Kerberos ticket and resends the request with the appropriate Authorization header.
  • The implementation is based on Java's GSS-API and is compatible with standard Kerberos environments.

Environment Configuration

Requires proper JAAS (jaas.conf) and Kerberos (krb5.conf) configuration.
See the updated documentation for example configuration files and JVM options.

Additional Notes

  • The SpnegoAuthProvider allows for easy extension and testing by supporting custom authenticators and GSSManager injection.
  • The feature is fully compatible with Java 1.6+ and works on both Unix and Windows environments.

@raccoonback
Copy link
Contributor Author

I tested Kerberos authentication using the krb5 available at https://formulae.brew.sh/formula/krb5.

@raccoonback raccoonback force-pushed the issue-3079 branch 5 times, most recently from 19ccf13 to 090e1c2 Compare June 26, 2025 06:49
@raccoonback raccoonback changed the title Support SPNEGO (Kerberos) Authentication in HttpClient Support SPNEGO Authentication in HttpClient Jun 27, 2025
@raccoonback raccoonback force-pushed the issue-3079 branch 3 times, most recently from a6efd89 to 96aa2ba Compare July 1, 2025 08:57
@raccoonback raccoonback force-pushed the issue-3079 branch 2 times, most recently from 8fcac3f to a77c0a5 Compare July 5, 2025 03:43
@raccoonback
Copy link
Contributor Author

@violetagg
Hello!
Please check this PR when you have a chance. 😃

@wendigo
Copy link

wendigo commented Jul 23, 2025

This is so great! Looking forward to get this in :)

@wendigo
Copy link

wendigo commented Jul 23, 2025

I can provide some guidance around APIs and configuration. Not every kerberos-enabled client uses JAAS, therefore the direct Subject/SPNEGO token support should be provided

@raccoonback
Copy link
Contributor Author

@wendigo
Hello!
Thank you for the great point. 😀

I was thinking of allowing users to implement the SpnegoAuthenticator interface, similar to JaasAuthenticator, to support custom authentication logic if needed.

If I understood you correctly, you're suggesting that we should provide a way for users to directly supply a Subject, as in the example below:

public class DirectSubjectAuthenticator implements SpnegoAuthenticator {

    // ...
    private Subject subject;

    @Override
    public Subject login() throws LoginException {
        return subject;
    }

    // ...
}

Would you be able to share a more concrete example or use case?
It would help refine the design in the right direction.

@wendigo
Copy link

wendigo commented Jul 24, 2025

Sure @raccoonback.

I'd like to use reactor-netty in the trino CLI/JDBC/client libraries.

We support delegated/constrained/unconstrained kerberos authentication. Relevant code is here:

https://github.com/trinodb/trino/tree/master/client/trino-client/src/main/java/io/trino/client/auth/kerberos

This is how we add it to the okhttp: https://github.com/trinodb/trino/blob/master/client/trino-client/src/main/java/io/trino/client/auth/kerberos/SpnegoHandler.java

Configurability is important as we expose configuration that allows the user to pass remote service name, service principal name, whether to canonicalize hostname: https://github.com/trinodb/trino/blob/master/client/trino-client/src/main/java/io/trino/client/auth/kerberos/SpnegoHandler.java#L50C5-L54C48

@raccoonback
Copy link
Contributor Author

@violetagg
I think supporting not only JAAS-based authentication but also allowing the user to provide a GSSCredential directly could improve configurability and flexibility.
This would be especially useful in environments where JAAS is not preferred or where credentials need to be managed programmatically.
What do you think about this direction?

cc. @wendigo

@violetagg
Copy link
Member

violetagg commented Jul 28, 2025 via email

@raccoonback
Copy link
Contributor Author

@wendigo
I've added GSSCredential-based SPNEGO authentication and support for service name and canonical hostname configuration.
Thank you for the suggestion.

@raccoonback raccoonback force-pushed the issue-3079 branch 2 times, most recently from 685924c to b082661 Compare July 30, 2025 23:39
@raccoonback
Copy link
Contributor Author

@violetagg
Hello.
I would appreciate it if you could review this PR.
Thanks!

@violetagg
Copy link
Member

@violetagg Hello. I would appreciate it if you could review this PR. Thanks!

I'm just returning fro vacation, will check it in the next days or so

@raccoonback
Copy link
Contributor Author

@violetagg
Hello.
I would appreciate it if you could review this PR.
Thanks!

@violetagg
Copy link
Member

I will check this one ... just need to finalise some other tasks.

@violetagg
Copy link
Member

@raccoonback We discussed to introduce this feature for 1.3.0 version but it is based on 1.2.x branch. Which version are we targeting?

@raccoonback
Copy link
Contributor Author

@violetagg
I think I got confused earlier.
As you mentioned, I’ll make the changes based on version 1.3.x.
ref. #3079 (comment)

@raccoonback
Copy link
Contributor Author

@violetagg
I couldn’t find the 1.3.x branch — would it be okay if I open the PR based on the main branch instead?

@violetagg
Copy link
Member

@violetagg I couldn’t find the 1.3.x branch — would it be okay if I open the PR based on the main branch instead?

yes main is the branch that is for 1.3.x version

Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
… authentication

Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
Signed-off-by: raccoonback <kosb15@naver.com>
@raccoonback raccoonback changed the base branch from 1.2.x to main October 29, 2025 14:15
@raccoonback
Copy link
Contributor Author

@violetagg
I’ve changed the target branch to 1.3.x.
Please review PR.

@violetagg
Copy link
Member

After following the discussion here and having in mind the Reactor Netty API, I think that the solution should be a bit more generic. Also IMO, Reactor Netty is responsible to provide the infrastructure while the user - to provide the concrete implementation (for example how the token will be generated, cached etc).
My proposal is the following (similar to how follow redirect is implemented)

HttpClient.httpAuthentication(
			BiPredicate<HttpClientRequest, HttpClientResponse> authenticationPredicate,
			Consumer<HttpClientRequest> authenticationConsumer)

And the usage will be

HttpClient.create()
			.httpAuthentication(
					(request, response) ->
						return response.status().code() == 401 &&
								response.responseHeaders().contains("WWW-Authenticate", "Negotiate", true)
					,
					request -> {
							String token = getOrGenerateToken(request.resourceUrl());
							request.header("Authorization", "Negotiate " + token);
					}
			);

@violetagg
Copy link
Member

The API is a bit rough, if we have a delayed token generation we may need the authenticationConsumer to return Mono that we can execute before sending the request. What do you think?

@raccoonback
Copy link
Contributor Author

@violetagg
Thank you for the review. 😀
I have a few questions.

  1. Regarding the token issuance and re-authentication behavior you mentioned — should we let users define only those parts?
    Or do you also think users should define the handling of components like GssCredentialAuthenticator?

  2. Currently, the authentication header value is stored and reused for subsequent HTTP requests.
    Do you also expect this kind of handling to be managed through user-defined logic?

@raccoonback
Copy link
Contributor Author

@violetagg
I initially considered this as a feature addition specifically for SPNEGO authentication...
However, are you suggesting adding only the authentication flow itself, without including the detailed SPNEGO implementation or dependencies ~~?

@violetagg
Copy link
Member

@violetagg Thank you for the review. 😀 I have a few questions.

  1. Regarding the token issuance and re-authentication behavior you mentioned — should we let users define only those parts?
    Or do you also think users should define the handling of components like GssCredentialAuthenticator?
  2. Currently, the authentication header value is stored and reused for subsequent HTTP requests.
    Do you also expect this kind of handling to be managed through user-defined logic?

I would like the user to be responsible for the token generation (thus our API will work not only for SPNEGO) and also the decision how the token will be used/reused, what kind of cache that will be used, what will be the expiration time etc., it is not Reactor Netty decision but user's.

@violetagg
Copy link
Member

violetagg commented Nov 3, 2025

@violetagg I initially considered this as a feature addition specifically for SPNEGO authentication... However, are you suggesting adding only the authentication flow itself, without including the detailed SPNEGO implementation or dependencies ~~?

yes, I understand
however I think that if we try to provide a generic API, later it will be able to be used not only with SPNEGO

We can show case in the examples various http authentications

@raccoonback
Copy link
Contributor Author

@violetagg
I’ll update it according to the direction you suggested.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support For Spnego Auth scheme support for netty HttpClient similar to Apache's

3 participants