0

Testing an authenticated REST-api with HtmlUnit seems simple enough. Yet, I can't seem to get it to work.

I have the following code:

@Test
public void testApi() throws IOException {

    WebClient webClient = new WebClient();
    webClient.addRequestHeader("ACCEPT", "application/json");
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);

    // Incorrect authentication data
    DefaultCredentialsProvider userCredentials = new DefaultCredentialsProvider();
    userCredentials.addCredentials("someuser", "wrongpass");
    webClient.setCredentialsProvider(userCredentials);

    page = webClient.getPage("http://localhost:8081/apiv/");
    assertEquals(401, page.getWebResponse().getStatusCode());
    userCredentials.clear();

    // Correct authentication data
    userCredentials.addCredentials("someuser", "correctpass");
    webClient.setCredentialsProvider(userCredentials);
    page = webClient.getPage("http://localhost:8081/apiv/");

    assertEquals(200, page.getWebResponse().getStatusCode());
}

When inspecting with a packet-sniffer, no authentication headers are sent. Neither for the 'Incorrect authentication data', nor for the 'Correct authentication data'.

TinkerTank
  • 5,685
  • 2
  • 32
  • 41

1 Answers1

0

My test wasn't working due to two separate problems:

No WWW-Authenticate header from the server.

Note that HtmlUnit will, regardless of the credentials-provider set, sent a plain request at first. Only if the server asks for authentication, will the basic-auth header be sent.

It turns out that the api I was testing had a bug, not sending the WWW-Authenticate header.

After adding:

resp.setHeader("WWW-Authenticate", "BASIC realm=\"apiv2\"");

Authentication headers were sent for the 'incorrect' test-case.


How to send Authentication Information on every request?

A solution can be found here:

Passing basic auth credentials with every request with HtmlUnit WebClient

This answer seems to be borrowed from: http://www.drawbackz.gq/stack/386935/passing-basic-auth-credentials-with-every-request-with-htmlunit-webclient.html


A CredentialsProvider can only be used once

As suggested here, a CredentialsProvider can be used only once. After that, it gets marked as 'answered', and auth-headers are not sent again on subsequent request.

As such, it is necessary to create a new WebClient with a new CredentialsProvider on every request. The following code works for me:

@Test
public void testApi() throws IOException {

    WebClient webClient = new WebClient();
    webClient.addRequestHeader("ACCEPT", "application/json");
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);

    // Incorrect authentication data
    DefaultCredentialsProvider userCredentials = new DefaultCredentialsProvider();
    userCredentials.addCredentials("someuser", "wrongpass");
    webClient.setCredentialsProvider(userCredentials);

    page = webClient.getPage("http://localhost:8081/apiv/");
    assertEquals(401, page.getWebResponse().getStatusCode());
    userCredentials.clear();


    // Correct authentication data
    webClient = new WebClient();
    webClient.addRequestHeader("ACCEPT", "application/json");
    userCredentials= new DefaultCredentialsProvider();
    userCredentials.addCredentials("someuser", "correctpass");
    webClient.setCredentialsProvider(userCredentials);
    page = webClient.getPage("http://localhost:8081/apiv/");

    assertEquals(200, page.getWebResponse().getStatusCode());
}
Community
  • 1
  • 1
TinkerTank
  • 5,685
  • 2
  • 32
  • 41