Payara GET BASIC auth request fails with 401

This really isn’t a Payara issue, and I’ll explain below, but perhaps someone here has run into this working with Payara 5.2022.3 and/or Payara 4/Jersey-2.25.1.

I’ve got a REST endpoint protected with BASIC auth. This was working, and then it stopped. I’m not sure what happened here, as the code didn’t change. Perhaps a system update to Java?

It looks like there is a pre-flight that’s not sending the BASIC auth header. If I run the same request with curl, it works fine. So, this is a Jersey client issue.

The following code is a slimmed down version, for brevity, just to give you an idea of what I’m going:

Server code:

@BasicAuthenticationMechanismDefinition(
        realmName = "appSearch"
)
@DatabaseIdentityStoreDefinition(
        dataSourceLookup = "jdbc/MyDB",
        callerQuery = "select passhash from User where username = ?",
        groupsQuery = "select groupname from Group where username = ?",
        hashAlgorithm = javax.security.enterprise.identitystore.Pbkdf2PasswordHash.class,
        priority = 30)
@DeclareRoles({"SOME_GROUP"})
@Singleton
@Startup
public class ApplicationSettingsBean implements ApplicationSettingsBeanLocal {


@RolesAllowed({"SOME_GROUP"})
@Path("search")
public class SearchRequestFacade {

    @GET
    @Path("file-number/{uuid: [a-zA-F0-9]{8}-([a-zA-F0-9]{4}-){3}[a-zA-F0-9]{12}}")
    @Produces({MediaType.TEXT_PLAIN})
    public Response request(
            @PathParam("uuid") String uuid
    ) {
        return Response.ok(search.fileNumber(uuid)).build();
    }

}

Jersey Client (from Payara 4 application):

ClientConfig clientConfig = new ClientConfig();
clientConfig.register(GsonProvider.GsonFeature.class);
clientConfig.property("jersey.config.server.disableMoxyJson", Boolean.TRUE);
clientConfig.property("jersey.config.client.disableMoxyJson", Boolean.TRUE);
clientConfig.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);

clientConfig.register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials(username, password).build());
           
clientConfig.register(new LoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, null, null));

clientConfig.register(MultiPartFeature.class);
int timeout = Math.toIntExact(TimeUnit.SECONDS.toMillis(10));
clientConfig.property("jersey.config.client.connectTimeout", timeout);
clientConfig.property("jersey.config.client.readTimeout", timeout);
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target(url).path(urlPath);

Invocation.Builder invocationBuilder = webTarget.request(MediaType.TEXT_PLAIN_TYPE);
Response response = invocationBuilder.get();

String fileNumber = response.readEntity(String.class)
            

image

Jersey Client logging:

INFO:   1 * Sending client request on thread http-thread-pool::http-listener-1(2)
1 > GET http://127.0.0.1:8080/app/api/v1/search/file-number/0c6c437e-e459-43d3-a8e6-942e128f2b80
1 > Accept: text/plain

INFO:   1 * Client response received on thread http-thread-pool::http-listener-1(2)
1 < 401
1 < Content-Language: 
1 < Content-Length: 1076
1 < Content-Type: text/html
1 < Server: Payara Server  5.2022.3 #badassfish
1 < WWW-Authenticate: Basic realm="appSearch"
1 < X-Frame-Options: SAMEORIGIN
1 < X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.2022.3 #badassfish Java/Ubuntu/11)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>Payara Server  5.2022.3 #badassfish - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 401 - Unauthorized</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Unauthorized</p><p><b>description</b>This request requires HTTP authentication.</p><hr/><h3>Payara Server  5.2022.3 #badassfish</h3></body></html>

INFO:   2 * Sending client request on thread http-thread-pool::http-listener-1(2)
2 > GET http://127.0.0.1:8080/app/api/v1/search/file-number/0c6c437e-e459-43d3-a8e6-942e128f2b80
2 > Accept: text/plain
2 > Authorization: Basic SOMEBASE64USERPASS
2 > User-Agent: Jersey/2.25.1 (HttpUrlConnection 1.8.0_352)

INFO:   2 * Client response received on thread http-thread-pool::http-listener-1(2)
2 < 200
2 < Content-Length: 0
2 < Content-Type: text/plain
2 < Server: Payara Server  5.2022.3 #badassfish
2 < X-Frame-Options: SAMEORIGIN
2 < X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.2022.3 #badassfish Java/Ubuntu/11)
1234567890

Using curl:

$ curl -k --ssl --basic -i http://127.0.0.1:8080/app/api/v1/search/file-number/FD36C98C-6A37-47FA-BBDD-3388CB79010B?user-id=test -u username:password
HTTP/1.1 200 OK
Server: Payara Server  5.2022.3 #badassfish
X-Powered-By: Servlet/4.0 JSP/2.3 (Payara Server  5.2022.3 #badassfish Java/Ubuntu/11)
Content-Type: text/plain
Content-Length: 0
X-Frame-Options: SAMEORIGIN
1234567890

It appears that the problem was how the HttpAuthenticationFeature was constructed. Adding .nonPreemptive() caused a two-part request, where the BASIC auth header was only sent when it failed with a 401 on the first attempt. Although removing this fixed the request issue, leaving it in place should have worked, too, as the second attempt succeeded in the single request. Perhaps this is a bug with Jersey client.

Old:

HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials(username, password).build();

New:

HttpAuthenticationFeature.basicBuilder().credentials(username, password).build();

Sauce: Testing Authentication with Jersey REST Client - HowToDoInJava