Standardized application context identifier (AppContextID) for Servlet Profile

Java EE 7 comes with a lot of improvements in many areas. Things like JSF 2.2 , CDI 1.1, JMS 2.0 and JAX-RS 2.0 have all been massively improved.However, not all specs have seen a significant revision. A number of them like JSP 2.1 and EJB 3.1 only had a minor revision, called a maintenance release. In this post we'll take a look at the changes that were done for the standardized authentication support in Java EE: JASPIC 1.1 (JSR 196).The second maintenance release of JASPIC, called Maintenance Release B officially has seen its version bumped from 1.0 to 1.1. It's sometimes confusing, but a small maintenance release can have the same version increment as a significant revision. E.g. JSF 2.1 was a small maintenance release of its prior version 2.0, but EJB 3.1 was a significant revision of its prior version 3.0.On to the changes in JASPIC 1.1:

One of the big hurdles when trying to register a JASPIC auth module programmatically (which incidentally is the only standardized way) for use in a Servlet app, is the fact that the API requires a so-called "AppContextID".



This AppContextID has to start with a "logical host" name, but there was no standard way for code to obtain this name.



In JASPIC 1.1 this is now possible via the new Servlet 3.1 ServletContext#getVirtualServerName

method. An example of generating a portable AppContextID:



String getAppContextID(ServletContext context) return context.getVirtualServerName() + " " + context.getContextPath(); }

Semi-auto register session

In many of the proprietary (vendor specific) authentication modules that are still in use next to JASPIC you often authenticate once and the container then remembers the authentication until the user logs out, clear his cookies or when the HTTP session expires.



In JASPIC however an authentication module was required to re-authenticate fully manually at the start of each (HTTP) request. In practice this boiled down to storing the authentication details (user name and roles) into the HTTP session after the initial authentication, and then just handing them back to the container at every following call. A relatively simply job, but a tedious one and for many it's a quite unexpected requirement that this needs to be done at all. (even for certified Java EE vendors this can be confusing, as JBoss seemingly misinterpreted the spec and always remembers the authentication)



In JASPIC 1.1 a SAM for the Servlet Profile can now explicitly ask for the container to semi-remember the authentication by putting the key javax.servlet.http.registerSession into the MessageInfo map with as value the string true. An example of setting and getting this:

public void setRegisterSession(MessageInfo messageInfo) { messageInfo.getMap().put("javax.servlet.http.registerSession", TRUE.toString()); } boolean isRegisterSession(MessageInfo messageInfo) { return Boolean.valueOf((String) messageInfo.getMap().get("javax.servlet.http.registerSession")); }

public AuthStatus doValidateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null) { handler.handle(new Callback[] { new CallerPrincipalCallback(clientSubject, userPrincipal) } ); return SUCCESS; } // Rest of auth code here }

(try/catch omitted for brevity)

If doValidateRequest uses the callback handler to handle a CallerPrincipalCallback constructed with the non-null principal obtained from HttpServletRequest#getUserPrincipal, and the principal was originally established using a CallerPrincipalCallback constructed with the principal name, then the groups must be restablished from the container specific principal.

Support for Servlet 3's authenticate, login and logout methods

The container will however not fully remember the authentication. The SAM is still called at each request, and the SAM still has to re-authenticate, but it can indicate that it wants to retain ("inherit") the authenticated identity that was previously established by executing the following protocol:For implementors of the spec it's important the realize that the mere usage of the CallerPrincipalCallBack in this way should fully restore the authentication identity for the session, which includes the groups. A requirement like the following should have been added to the spec, but unfortunately wasn't:Hopefully a next revision of JASPIC will contain this clarification, and implementors will in the meantime do the right thing anyway.

Servlet 3 introduced methods for programmatic login and logout, via the methods authenticate, login and logout on HttpServletRequest.



Unfortunately JASPIC did not officially support any of these methods. All JASPIC 1.0 implementations in fact did call a SAM's validateRequest following a call to HttpServletRequest#authenticate, but the way the various vendors decided to implement the login and logout methods was particularly troublesome.



Nearly all of these implementations called a completely unrelated proprietary login module following a call to login (completely ignoring any configured JASPIC SAM), while logout in most cases was completely ignored as well.



JASPIC 1.1 now defines what should happen when these methods are called. authenticate should indeed have validateRequest called, while for logout the existing cleanSubject method on a SAM should be called. login is explicitly not supported; an exception has to be thrown whenever a JASPIC SAM is configured and this method is called. The following shows an example of what an container could approximately do following a call to HttpServletRequest#logout():

ServerAuthContext serverAuthContext = [...] MessageInfo messageInfo = [...] Subject subject = [...] if (messageInfo == null) { messageInfo = new SomeMessageInfo(request, response); } if (subject = null) { subject = new Subject(); } serverAuthContext.cleanSubject(messageInfo, subject);

Support for Servlet forwards and includes

A JASPIC SAM for the Servlet Profile has access to both the HttpServletRequest and HttpServletResponse objects. This means among others that such SAM should be able to do forwarding & including following the Servlet API. E.g.:

public AuthStatus doValidateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage(); try request.getServletContext() .getRequestDispatcher("somepage") .forward(request, response); } catch (ServletException | IOException e) { throw (AuthException) new AuthException().initCause(e); } return SEND_CONTINUE; }

(this is in part due to the very early stage of the request processing in which a SAM is called)

Clarifications and editorial corrections

JASPIC 1.0 was silent about whether this use case should work or not, and in practice it thus indeed didn't work on all implementations.JASPIC 1.1 now mandates implementations to make sure that forwarding & including via aworks as expected.

Finally, the JASPIC 1.1 spec document contains some clarifications for what e.g. "after the service invocation" means and corrected an inconsistency in the formal JavaDocs.

While these small changes may not seem as exciting as those done for the specs that got a major revision, it's still a great step forward and addresses some very real pain points that existed in JASPIC 1.0.A very convenient full list of what was changed in JASPIC 1.1 with links to the diffs in the spec and associated JIRA issues can be found at the JCP site.