Tuesday, March 22, 2016

Simple OpenAM Post Authentication Plugin

Background: OpenAM Post authentication plugin is a java program you can write which gets executed every time a user is authenticated against OpenAM. You can read more about it here

Requirement: We have three applications which need to perform SSO. If user login on one and navigate to other, it should accept the authenticated user and setup the context.  Nothing special here, all three belong to same realm in OpenAM where two secured via OpenAM Policy Agent and one doing REST authentication. 

Post authentication plugin is opted to fulfill a special need here. Some users are being migrated to OpenDJ without email address and we wanted to make sure that whenever they login first time, they are forced to update the email address. This check is performed by our PAP. If user profile doesn’t have an email address then user is navigated to update email screen.

So let’s start with our PAP and we can discuss the details as we go.

Pre-requisites for PAP:-

Eclipse
OpenAM Client SDK

The easiest way to start this is to download the SamplePAP from ForgeRock website and modify it as per your needs.

Below is the code for PAP:-

public void onLoginSuccess(Map requestParamsMap, HttpServletRequest request, HttpServletResponse response,
SSOToken token) throws AuthenticationException {
try {
AMIdentity id = IdUtils.getIdentity(AccessController.doPrivileged(AdminTokenAction.getInstance()),
token.getProperty(Constants.UNIVERSAL_IDENTIFIER));
Set<String> userEmails = id.getAttribute(MailAttributeName);
String uid = id.getName();
if (userEmails.isEmpty()) {
//PG: User doesn't have an email associated. Setting TokenID in request and redirecting
String tokenId = token.getTokenID().toString();

logger.error("Provider PAP: Email not found for user - " + uid
+ ". Redirecting for update email with token - " + tokenId);

request.setAttribute("bupaProviderPapId", token.getTokenID());
request.getRequestDispatcher("/UpdateEmail.jsp").forward(request, response);
} else {
logger.message("Provider PAP: User - " + uid + " has a valid email address associated");
}
} catch (Exception e) {
logger.error("EXCEPTION occured in Provider PAP: Error message - " + e.getMessage());
logger.message("EXCEPTION details: " + e.getStackTrace());
}
}

Couple of things to note in above code:-

Getting the attributes of the logged in user from AMIdentity class and when not available forwarding the token as a request attribute to UpdateEmail handler.

In order to /UpdateEmail.Jsp page to open in OpenAM context, you would have to create a Servlet which UpdateEmail.Jsp will be part of and a servlet handler. This than can be hooked into your OpenAM by copying the servlet handler, JSP at correct locations and updating web.xml file inside WEB-INF.

If the location in web.xml is correct, the servlet handler will pick the request and update the email.

A downside, since you are altering the usual execution of OpenAM’s post authentication classes, user is not yet logged in and the cookie is not there yet. User will have to login again post updating the email address.

Word of advise: In OpenAM, you can configure post authentication plugin class at two different places. One under Access Control -> Realm -> Authentication -> Authentication Chaining -> Name of Chain -> Post Authentication Processing Class

Second, under Access Control -> Realm -> Authentication -> All Core Settings -> Post Authentication Processing -> Authentication Post Processing Classes

Do not configure at later if you only want this class to be processed for users trying to log in. Apart from user, there are lot of other things which tries to authenticate itself against OpenAM like Policy Agent Groups and profile etc. If you configured your PAP at core Authentication settings as well then it will be executed for everything including users and other ForgeRock components. You might want this or may not need it so choose accordingly.

Also, just for information. We didn’t only rely on our PAP to handle/catch users who doesn’t have email address associated with them. We added an LDAP filter in policy settings of Policy agent to block users who doesn’t have email address associated with them.

Hope this would have helped and happy PAP writing.