-
Notifications
You must be signed in to change notification settings - Fork 689
Support HTTP Authentication in HttpClient #3813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
I tested Kerberos authentication using the krb5 available at https://formulae.brew.sh/formula/krb5. |
19ccf13 to
090e1c2
Compare
a6efd89 to
96aa2ba
Compare
reactor-netty-http/src/main/java/reactor/netty/http/client/JaasAuthenticator.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/SpnegoAuthProvider.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/SpnegoAuthProvider.java
Outdated
Show resolved
Hide resolved
8fcac3f to
a77c0a5
Compare
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
|
@violetagg |
|
This is so great! Looking forward to get this in :) |
|
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 |
|
@wendigo I was thinking of allowing users to implement the 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? |
|
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: 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 |
|
@violetagg cc. @wendigo |
|
I'm currently on vacation. When I return I'll check it.
На нд, 27.07.2025 г. в 18:21 KOSEUNGBIN ***@***.***> написа:
… *raccoonback* left a comment (reactor/reactor-netty#3813)
<#3813 (comment)>
@violetagg <https://github.com/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 <https://github.com/wendigo>
—
Reply to this email directly, view it on GitHub
<#3813 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFKCVJR2CIDYFH4XV65ZID3KTU6LAVCNFSM6AAAAAB75EX2K6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTCMRUGQ4DSNBVGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
@wendigo |
685924c to
b082661
Compare
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/JaasAuthenticator.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/SpnegoAuthProvider.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/SpnegoAuthProvider.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/SpnegoAuthProvider.java
Outdated
Show resolved
Hide resolved
|
@violetagg |
I'm just returning fro vacation, will check it in the next days or so |
|
@violetagg |
|
I will check this one ... just need to finalise some other tasks. |
31276ce to
ed6a545
Compare
| * Generates an authentication token for the given remote address. | ||
| * In a real application, this would retrieve or generate a valid token. | ||
| */ | ||
| static String generateAuthToken(SocketAddress remoteAddress) { |
Check notice
Code scanning / CodeQL
Useless parameter Note documentation
|
@violetagg |
violetagg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This is the direction. I have some comments related to the implementation.
| * @return a new {@link HttpClient} | ||
| * @since 1.3.0 | ||
| */ | ||
| public final HttpClient httpAuthentication( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have two APIs (similar to what we have already)
- httpAuthentication - this will provide just a BiConsumer
- httpAuthenticationWhen - this will use the current signature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@violetagg
Since the decision to apply the authenticator is made using a BiPredicate<HttpClientRequest, HttpClientResponse> predicate, I think that httpAuthentication requires both a BiPredicate and a BiConsumer.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
| HttpClientOperations operations = connection.as(HttpClientOperations.class); | ||
| if (operations != null && handler.authenticationPredicate != null) { | ||
| if (handler.authenticationPredicate.test(operations, operations)) { | ||
| if (log.isDebugEnabled()) { | ||
| log.debug(format(operations.channel(), "Authentication predicate matched, triggering retry")); | ||
| } | ||
| sink.error(new HttpClientAuthenticationException()); | ||
| return; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to use the mechanism that we use for follow redirect. reactor.netty.http.client.HttpClientOperations#onInboundNext -> see how "redirecting" is used. The idea is that once we decide to retry we will need to take care of this connection, which means we drain the incoming messages, no message goes to the end user etc (we don't know whether the server will send only the headers or also some message). Basically we need to return to the connection pool a clean connection. Also because we don't send any message to the end user we can switch to auto-read = true
| if (authenticator != null && authenticationAttempted) { | ||
| return authenticator.apply(ch, ch.address()) | ||
| .then( | ||
| Mono.defer( | ||
| () -> Mono.from(requestWithBodyInternal(ch)) | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| return requestWithBodyInternal(ch); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it too early here to provide the HttpClientOperations? HttpClientOperations is still empty and we need to configure it which happens in the new method requestWithBodyInternal.
...or-netty-http/src/main/java/reactor/netty/http/client/HttpClientAuthenticationException.java
Show resolved
Hide resolved
|
@violetagg |
yes |
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
… authentication Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
…ecific design Following the review feedback, this change updates the proposed SPNEGO-specific implementation into a more generic authentication flow that can support various HTTP authentication mechanisms. Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Moved authenticator to occur after the HTTP request is prepared, ensuring configuration is completed before authentication logic runs. Signed-off-by: raccoonback <[email protected]>
Move authentication predicate evaluation from RESPONSE_RECEIVED state to response processing, ensuring all response messages are consumed before retrying with authentication. Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Introduce httpAuthentication(BiFunction) for automatic 401 retry, and rename the predicate-based method to httpAuthenticationWhen for clarity when custom retry conditions are needed. Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
Signed-off-by: raccoonback <[email protected]>
17fbdbe to
723ac4b
Compare
reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientOperationsTest.java
Fixed
Show fixed
Hide fixed
reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientOperationsTest.java
Fixed
Show fixed
Hide fixed
| /** | ||
| * Configure HTTP authentication that retries on 401 Unauthorized responses. | ||
| * <p> | ||
| * This method provides a generic authentication framework that allows users to implement | ||
| * their own authentication mechanisms (e.g., Negotiate/SPNEGO, OAuth, Bearer tokens, custom schemes). | ||
| * The framework automatically retries requests when a 401 Unauthorized response is received. | ||
| * </p> | ||
| * | ||
| * <p>Example - Token-based Authentication:</p> | ||
| * <pre> | ||
| * {@code | ||
| * HttpClient client = HttpClient.create() | ||
| * .httpAuthentication( | ||
| * // Add authentication header before request | ||
| * (req, addr) -> { | ||
| * String token = generateAuthToken(addr); | ||
| * req.header(HttpHeaderNames.AUTHORIZATION, "Bearer " + token); | ||
| * return Mono.empty(); | ||
| * } | ||
| * ); | ||
| * } | ||
| * </pre> | ||
| * | ||
| * @param authenticator applies authentication to the request, receives the request and remote address, | ||
| * returns a Mono that completes when authentication is applied | ||
| * @return a new {@link HttpClient} | ||
| * @since 1.3.0 | ||
| * @see #httpAuthenticationWhen(BiPredicate, BiFunction) | ||
| */ | ||
| public final HttpClient httpAuthentication( | ||
| BiFunction<? super HttpClientRequest, ? super SocketAddress, ? extends Mono<Void>> authenticator) { | ||
| return httpAuthenticationWhen(HttpClientConfig.AUTHENTICATION_PREDICATE, authenticator); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
support HTTP authentication that retries on 401 Unauthorized responses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed to be more concrete - I was thinking about these below. The idea is that if you don't need to delay the token generation (i.e. asking some other component/service for a token) then you can immediately calculate it and add it to the headers thus no need of Mono otherwise you use the variant with Mono. Does this make sense?
public final HttpClient httpAuthentication(
BiPredicate<HttpClientRequest, HttpClientResponse> predicate,
BiConsumer<? super HttpClientRequest, ? super SocketAddress> authenticator) {
}
public final HttpClient httpAuthenticationWhen(
BiPredicate<HttpClientRequest, HttpClientResponse> predicate,
BiFunction<? super HttpClientRequest, ? super SocketAddress, ? extends Mono<Void>> authenticator) {
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@violetagg
Feedback has been applied.
Please review it again. 🙏
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClient.java
Show resolved
Hide resolved
...or-netty-http/src/main/java/reactor/netty/http/client/HttpClientAuthenticationException.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConfig.java
Outdated
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientOperations.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientOperations.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientOperations.java
Show resolved
Hide resolved
reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientOperations.java
Show resolved
Hide resolved
Signed-off-by: raccoonback <[email protected]>
|
@violetagg |
Signed-off-by: raccoonback <[email protected]>
This change updates the httpAuthentication() method to require both a retry predicate and an authenticator, allowing users to customize when authentication retry should occur. Signed-off-by: raccoonback <[email protected]>
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
Authorizationheader 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.
jaas.conf
A JAAS(Java Authentication and Authorization Service) configuration file in Java for integrating with authentication backends such as Kerberos.
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.
How It Works
401 Unauthorizedand aWWW-Authenticate: Negotiateheader,the client automatically generates a SPNEGO token using the Kerberos ticket and resends the request with the appropriate
Authorizationheader.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
SpnegoAuthProviderallows for easy extension and testing by supporting custom authenticators and GSSManager injection.