0

I am using JDK11 HTTP client library, where I try to create an authentication with specific scope which can be related to host, port, realm or scheme(BASIC, DIGEST, etc.).

My class implementation is:

class CredentialsProviderAuthenticator extends Authenticator {

    private final AuthScope authScope;
    private final Credentials credentials;

    private CredentialsProviderAuthenticator(AuthScope authScope, Credentials credentials) {
        this.authScope = authScope;
        this.credentials = credentials;
    }


    public static Authenticator createAuthenticator(AuthScope authScope, Credentials credentials) {
        return new CredentialsProviderAuthenticator(authScope, credentials);
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        if ( (authScope.getScheme() == null ||  authScope.getScheme().equalsIgnoreCase(getRequestingScheme()))
                && ((authScope.getHost() == null || authScope.getHost().equalsIgnoreCase(getRequestingHost())))
                && ((authScope.getRealm() == null || authScope.getRealm().equalsIgnoreCase(getRequestingPrompt())))
                && ((authScope.getPort() < 0) || authScope.getPort() == getRequestingPort())) {

            return new PasswordAuthentication(credentials.getUserPrincipal().getName(), credentials.getPassword().toCharArray());
        }

        return null;
    }

}

This implementation works fine when the PasswordAuthentication instance returned, but the client throws an exception when no suitable authentication found.

Caused by: java.io.IOException: No credentials provided
    at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:565)
    at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:119)
    at com.imperva.shcf4j.java.http.client.ClosableSyncHttpClient.execute(ClosableSyncHttpClient.java:50)
    ... 27 more
Caused by: java.io.IOException: No credentials provided
    at java.net.http/jdk.internal.net.http.AuthenticationFilter.response(AuthenticationFilter.java:303)
    at java.net.http/jdk.internal.net.http.MultiExchange.responseFilters(MultiExchange.java:181)
    at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsyncImpl$5(MultiExchange.java:245)
    at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
    at java.base/java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:610)
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:649)
    at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:478)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(HttpClientImpl.java:153)
    at java.base/java.util.concurrent.CompletableFuture$UniCompletion.claim(CompletableFuture.java:568)
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:638)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
    at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.handle(Http1Response.java:677)
    at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.handle(Http1Response.java:603)
    at java.net.http/jdk.internal.net.http.Http1Response$Receiver.accept(Http1Response.java:594)
    at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.tryAsyncReceive(Http1Response.java:650)
    at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:228)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run(SequentialScheduler.java:175)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:147)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)

I am looking for a way that in such cases the client will return a 401 HTTP error code. One idea is to create an instance that generates a username & password upon each invocation, but maybe a more simple approach exists?

daniel
  • 2,665
  • 1
  • 8
  • 18
Maxim Kirilov
  • 2,639
  • 24
  • 49

1 Answers1

1

I am afraid there is no better alternative at this time: either you provide an authenticator, and you will get an exception if the authenticator doesn't provide valid credentials, or you don't provide any authenticator, and you can handle the 401 response in your own code (which you might chose to implement within a dependent action registered asynchronously with the completable future returned by HttpClient::sendAsync). For instance:

client.sendAsync(...).thenApplyAsync(resp -> handle401(client, resp))...
daniel
  • 2,665
  • 1
  • 8
  • 18