Skip to main content

Authentication

The classic Juneau REST client delegates authentication to the underlying Apache HTTP Client, so you configure it with HTTP Client APIs or the builder shortcuts.

When to use it​

Use this when the server you call requires credentials: BASIC with a one-line shortcut, or FORM-based and OIDC logins by customizing the HTTP client.

Classic-client-only

The auth-shortcut convenience methods shown on these pages (such as basicAuth(...)) are builder methods on the classic RestClient.Builder (org.apache.juneau.rest.client.classic), which wraps Apache HttpClient 4.5. The Next-Generation REST Client has no equivalent auth-shortcut methods at all — it has no dependency on Apache HttpClient, so authentication must instead be configured on the transport you choose (for example, the native Apache HC 4.5/5, OkHttp, or JDK HttpClient builder) before wrapping it in an HttpTransport.

Example​

import org.apache.juneau.rest.client.classic.*;

public class BasicAuthExample {

public static void main(String[] args) throws Exception {
// Create a client that performs BASIC authentication using the specified user/pw.
try (RestClient client = RestClient.create()
.json()
.basicAuth("localhost", 8080, "me", "mypassword")
.build()) {
System.out.println("Built RestClient");
}
}
}

Options​

MechanismHow
BASICbasicAuth(host, port, user, pw) on the builder
FORM-basedOverride createHttpClient() and log in before returning the client
OIDCOverride createHttpClient() and run the redirect handshake before returning the client

BASIC authentication​

The basicAuth() method can be used to quickly enable BASIC authentication support.

RestClient client = RestClient.create()
.basicAuth(HOST, PORT, USER, PW)
.build();

This is functionally equivalent to the following:

RestClient.Builder builder = RestClient.create();
AuthScope scope = new AuthScope(HOST, PORT);
Credentials up = new UsernamePasswordCredentials(USER, PW);
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(scope, up);
builder.setDefaultCredentialsProvider(provider);

FORM-based authentication​

The RestClient.Builder class does not itself provide FORM-based authentication since there is no standard way of providing such support. Typically, to perform FORM-based or other types of authentication, you'll want to create your own subclass of RestClient.Builder and override the RestClient.Builder.createHttpClient() method to provide an authenticated client. The following example shows an implementation of a client that performs FORM-based authentication against the IBM Jazz platform.

/**
* Constructor.
*/
public JazzRestClient.Builder(URI jazzUri, String user, String pw) throws IOException {
...
}

/**
* Override the createHttpClient() method to return an authenticated client.
*/
@Override /* RestClient.Builder */
protected CloseableHttpClient createHttpClient() throws Exception {
CloseableHttpClient client = super.createHttpClient();
formBasedAuthenticate(client);
visitAuthenticatedURL(client);
return client;
}

/*
* Performs form-based authentication against the Jazz server.
*/
private void formBasedAuthenticate(HttpClient client) throws IOException {

URI uri2 = jazzUri.resolve("j_security_check");
HttpPost request = new HttpPost(uri2);
request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());

// Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

List params = List.of(
BasicNameValuePair.of("j_username", user),
BasicNameValuePair.of("j_password", pw)
);
request.setEntity(new UrlEncodedFormEntity(params));

HttpResponse response = client.execute(request);
try {
int rc = response.getStatusLine().getStatusCode();

Header authMsg = response.getFirstHeader("X-com-ibm-team-repository-web-auth-msg");
if (authMsg != null)
throw new IOException(authMsg.getValue());

// The form auth request should always respond with a 200 ok or 302 redirect code
if (rc == SC_MOVED_TEMPORARILY) {
if (response.getFirstHeader("Location").getValue().matches(".*/auth/authfailed.*"))
throw new IOException("Invalid credentials.");
} else if (rc != SC_OK) {
throw new IOException("Unexpected HTTP status: " + rc);
}
} finally {
EntityUtils.consume(response.getEntity());
}
}

/*
* This is needed for Tomcat because it responds with SC_BAD_REQUEST when the j_security_check URL is visited before an
* authenticated URL has been visited. This same URL must also be visited after authenticating with j_security_check
* otherwise tomcat will not consider the session authenticated
*/
private int visitAuthenticatedURL(HttpClient httpClient) throws IOException {
HttpGet authenticatedURL = new HttpGet(jazzUri.resolve("authenticated/identity"));
HttpResponse response = httpClient.execute(authenticatedURL);
try {
return response.getStatusLine().getStatusCode();
} finally {
EntityUtils.consume(response.getEntity());
}
}

OIDC authentication​

The following example shows an implementation of a client that performs OIDC authentication against the IBM Jazz platform.

/**
* Constructor.
*/
public JazzRestClient.Builder(URI jazzUri, String user, String pw) throws IOException {
...
}

/**
* Override the createHttpClient() method to return an authenticated client.
*/
@Override /* RestClient.Builder */
protected CloseableHttpClient createHttpClient() throws Exception {
CloseableHttpClient client = super.createHttpClient();
oidcAuthenticate(client);
return client;
}

private void oidcAuthenticate(HttpClient client) throws IOException {

HttpGet request = new HttpGet(jazzUri);
request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());

// Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

HttpResponse response = client.execute(request);
try {
int code = response.getStatusLine().getStatusCode();

// Already authenticated
if (code == SC_OK)
return;

if (code != SC_UNAUTHORIZED)
throw new RestCallException("Unexpected response during OIDC authentication: "
+ response.getStatusLine());

// x-jsa-authorization-redirect
String redirectUri = getHeader(response, "X-JSA-AUTHORIZATION-REDIRECT");

if (redirectUri == null)
throw new RestCallException("Expected a redirect URI during OIDC authentication: "
+ response.getStatusLine());

// Handle Bearer Challenge
HttpGet method = new HttpGet(redirectUri + "&prompt=none");
addDefaultOidcHeaders(method);

response = client.execute(method);

code = response.getStatusLine().getStatusCode();

if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 2: "
+ response.getStatusLine());

String loginRequired = getHeader(response, "X-JSA-LOGIN-REQUIRED");

if (! "true".equals(loginRequired))
throw new RestCallException("X-JSA-LOGIN-REQUIRED header not found on response during OIDC authentication phase 2: "
+ response.getStatusLine());

method = new HttpGet(redirectUri + "&prompt=none");

addDefaultOidcHeaders(method);
response = client.execute(method);

code = response.getStatusLine().getStatusCode();

if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 3: "
+ response.getStatusLine());

// Handle JAS Challenge
method = new HttpGet(redirectUri);
addDefaultOidcHeaders(method);

response = client.execute(method);

code = response.getStatusLine().getStatusCode();

if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 4: "
+ response.getStatusLine());

cookie = getHeader(response, "Set-Cookie");

Header[] defaultHeaders = new Header[] {
BasicStringHeader.of("User-Agent", "Jazz Native Client"),
BasicStringHeader.of("X-com-ibm-team-configuration-versions",
"com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0"),
BasicStringHeader.of("Accept", "text/json"),
BasicStringHeader.of("Authorization", "Basic "
+ StringUtils.base64EncodeToString(user + ":" + pw)),

BasicStringHeader.of("Cookie", cookie)
};

setDefaultHeaders(List.of(defaultHeaders));

} finally {
EntityUtils.consume(response.getEntity());
}
}

private void addDefaultOidcHeaders(HttpRequestBase method) {
method.addHeader("User-Agent", "Jazz Native Client");
method.addHeader("X-com-ibm-team-configuration-versions",
"com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0");
method.addHeader("Accept", "text/json");

if (cookie != null) {
method.addHeader("Authorization", "Basic "
+ StringUtils.base64EncodeToString(user + ":" + pw));

method.addHeader("Cookie", cookie);
}
}

See also​