3

A few times in the past, I've had to make an authenticated HTTP or HTTPS connection in an app running on WebSphere Application Server.
I've typically created some custom way of storing a username and password for the connection - using a text or XML file or, for testing, just hard-coding - and added those details to an Authorization header in a HttpURLConnection, to perform HTTP Basic authentication.

I've recently considered that the WAS Integrated Solutions Console has a way of configuring usernames and passwords (under Security -> Java Authentication and Authorization Service -> J2C authentication data), and it makes sense to use a built-in feature (if possible) instead of always doing this in a custom way, but I haven't been able to figure out how to do so after an hour or so of searching.

So, does anyone know if it's possible, and how to do it if so, to use configured WAS J2C authentication data to authenticate arbitrary HTTP or HTTPS connections?

I assume it requires WebSphere APIs to:

  • Retrieve an object representing a J2C authentication entry by its alias; and
  • Do one of:
    • Get an authenticated HttpURLConnection or HttpsURLConnection object from the JAAS/J2C object;
    • Get the plain-text username and password so they can be added to an Authorization header; or
    • If the connection is to an IBM platform in the same domain: Get an LTPA token from the JAAS/J2C object, which can then be added to a cookie in the connection.

Also: If Apache HttpClient would make this any easier, I'm fine with using that, but appropriate WebSphere APIs would still be needed.

I have found a class that might be useful for WebSphere Portal (com.ibm.wps.portletservice.credentialvault.credentials.LtpaTokenCredential), but I need to do this in a non-Portal environment, so I can't use any Portal-specific classes or interfaces.

I would also need this to work in at least the following types of application: Servlets, and WebSphere Trust Association Interceptors.

Scott Leis
  • 2,810
  • 6
  • 28
  • 42

2 Answers2

1

I just tried the following (Modified from here) in a servlet and it worked to grab the auth alias user/pwd.

import com.ibm.websphere.security.NotImplementedException;
import com.ibm.wsspi.security.auth.callback.Constants;
import com.ibm.wsspi.security.auth.callback.WSMappingCallbackHandlerFactory;
import javax.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

Map map = new HashMap();
map.put(Constants.MAPPING_ALIAS, "REPLACE_WITH_YOUR_AUTH_ALIAS");
CallbackHandler callbackHandler = null;
try {
    callbackHandler = WSMappingCallbackHandlerFactory.getInstance().getCallbackHandler(map, null);
} catch (NotImplementedException e) {
}

LoginContext loginContext = null;
try {
loginContext = new LoginContext("DefaultPrincipalMapping", callbackHandler);
    loginContext.login();
} catch (LoginException e) {
}

Subject subject = loginContext.getSubject();
Set credentials = subject.getPrivateCredentials();

PasswordCredential passwordCredential = (PasswordCredential) credentials.iterator().next();

String user = passwordCredential.getUserName();
String password = new String(passwordCredential.getPassword());

response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>User: " + user + " --- Password: " + password+"</h1>");
mswatosh
  • 466
  • 2
  • 8
  • There's a safer way of getting the `PasswordCredentials`. Isn't there an `iterator()` method that takes a `Class` parameter? – user207421 Aug 12 '17 at 01:23
  • Not sure what you're referencing. Iterator doesn't have any methods which take parameters until Java 8, which only introduces forEachRemaining, which takes a Consumer. – mswatosh Aug 12 '17 at 13:28
  • It'll be several days until I've had a chance to test this myself, but it looks good. I'll mark as answer when I'm able to test it successfully. – Scott Leis Aug 13 '17 at 02:47
1

If the authentication is between WAS servers, you don't need to go to these lengths; the LTPA token should be sufficient so long as the participating WAS servers form part of the same security domain and trust each other.

If your idea is to go from a non-WAS server to a WAS server, you can look at the TAI option, but you still don't need to use Authentication Aliases.

When we authenticated from one service to a downstream service, we simply passed in the LTPA token, like below (LTPA token is not a Portal thing, it's a WAS thing). The assumption is that someone authenticated already on your service, e.g. from a Portal or WAS login and we want to call a downstream REST service on another WAS server.

/** For call to a REST client, use apache wink that comes with WAS:
* This method shows how to pass in the available LTPA token to create
* a RestClient
private org.apache.wink.client.RestClient getRestClient() {
  if (restClient == null) {
    org.apache.wink.client.ClientConfig config = new ClientConfig();    
    config.handlers(new org.apache.wink.client.handlers.LtpaAuthSecurityHandler());
    restClient = new RestClient(config);
  }
  return restClient;
}
  • I have a specific case where I need to call IBM Connections REST APIs that require admin access (for creating Connections user profiles) before any user has been authenticated. I'm sure there won't be any LTPA token at that point. – Scott Leis Aug 15 '17 at 07:19
  • 100%; it will then work to store a system user's credentials as an Authentication Alias, fetch it as described and add the BASIC auth header using your REST client. – Roan Bester Aug 15 '17 at 09:09