This article takes a look at the state of security support in Java EE 6, with a focus on applications that wish to do their own authentication and the usage of the JASPI/JASPIC/JSR 196 API.

Update: the further reading section has been moved to my ZEEF page about JASPIC. This contains links to articles, background, questions and answers, and more.

Declarative security is easy

How is authentication traditionally implemented?

Heavyweight

What about JAAS?

A common mistake is to think that JAAS (Java Authentication and Authorization Service) is the standardized and portable API that can be used to take care of authentication in Java EE without having to resort to vendor specific APIs.





JASPIC to the rescue... sort off

Tomcat in particular does ship with a JAASRealm (javadoc), which it says is an early prototype of JASPIC that was probably created somewhere around 2004)

(The GlassFish developer documentation mentions the programmatic option, but doesn't go into detail how that exactly works)

Programmatically registering JASPIC auth modules

Sample code

Step 1 - Registering via the factory-factory-factory

@WebListener public class StartupListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { AuthConfigFactory factory = AuthConfigFactory.getFactory(); factory.registerConfigProvider( new TestAuthConfigProvider(), "HttpServlet", null, "The test" ); } @Override public void contextDestroyed(ServletContextEvent sce) { } }

Step 2 - Implementing the factory-factory

public class TestAuthConfigProvider implements AuthConfigProvider { private static final String CALLBACK_HANDLER_PROPERTY_NAME = "authconfigprovider.client.callbackhandler"; private Map<String, String> providerProperties; public TestAuthConfigProvider() { } /** * Constructor with signature and implementation that's required by API. * * @param properties * @param factory */ public TestAuthConfigProvider(Map<String, String> properties, AuthConfigFactory factory) { this.providerProperties = properties; // API requires self registration if factory is provided. Not clear // where the "layer" (2nd parameter) // and especially "appContext" (3rd parameter) values have to come from // at this place. if (factory != null) { factory.registerConfigProvider( this, null, null, "Auto registration" ); } } /** * The actual factory method that creates the factory used to eventually * obtain the delegate for a SAM. */ @Override public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException, SecurityException { return new TestServerAuthConfig(layer, appContext, handler == null ? createDefaultCallbackHandler() : handler, providerProperties); } @Override public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException, SecurityException { return null; } @Override public void refresh() { } /** * Creates a default callback handler via the system property * "authconfigprovider.client.callbackhandler", as seemingly required by the * API (API uses wording "may" create default handler). * * @return * @throws AuthException */ private CallbackHandler createDefaultCallbackHandler() throws AuthException { String callBackClassName = System .getProperty(CALLBACK_HANDLER_PROPERTY_NAME); if (callBackClassName == null) { throw new AuthException( "No default handler set via system property: " + CALLBACK_HANDLER_PROPERTY_NAME); } try { return (CallbackHandler) Thread.currentThread() .getContextClassLoader().loadClass(callBackClassName) .newInstance(); } catch (Exception e) { throw new AuthException(e.getMessage()); } } }

Step 3 - Implementing the factory

/** * This class functions as a kind of factory for {@link ServerAuthContext} * instances, which are delegates for the actual {@link ServerAuthModule} (SAM) * that we're after. * */ public class TestServerAuthConfig implements ServerAuthConfig { private String layer; private String appContext; private CallbackHandler handler; private Map<String, String> providerProperties; public TestServerAuthConfig(String layer, String appContext, CallbackHandler handler, Map<String, String> providerProperties) { this.layer = layer; this.appContext = appContext; this.handler = handler; this.providerProperties = providerProperties; } /** * WebLogic 12c, JBoss EAP 6 and GlassFish 3.1.2.2 call this only once per * request, Geronimo V3 calls this before sam.validateRequest and again * before sam.secureRequest in the same request. * */ @Override public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, @SuppressWarnings("rawtypes") Map properties) throws AuthException { return new TestServerAuthContext(handler); } @Override public String getMessageLayer() { return layer; } @Override public String getAuthContextID(MessageInfo messageInfo) { return appContext; } @Override public String getAppContext() { return appContext; } @Override public void refresh() { } @Override public boolean isProtected() { return false; } public Map<String, String> getProviderProperties() { return providerProperties; } }

Step 4 - Implementing the delegator

/** * The Server Authentication Context is an extra (required) indirection between * the Application Server and the actual Server Authentication Module (SAM). * This can be used to encapsulate any number of SAMs and either select one at * run-time, invoke them all in order, etc. * <p> * Since this simple example only has a single SAM, we delegate directly to that * one. Note that this {@link ServerAuthContext} and the * {@link ServerAuthModule} (SAM) share a common base interface: * {@link ServerAuth}. * */ public class TestServerAuthContext implements ServerAuthContext { private ServerAuthModule serverAuthModule; public TestServerAuthContext(CallbackHandler handler) throws AuthException { serverAuthModule = new TestServerAuthModule(); serverAuthModule.initialize(null, null, handler, Collections.<String, String> emptyMap()); } @Override public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { return serverAuthModule.validateRequest(messageInfo, clientSubject, serviceSubject); } @Override public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { return serverAuthModule.secureResponse(messageInfo, serviceSubject); } @Override public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { serverAuthModule.cleanSubject(messageInfo, subject); } }

Step 5 - Implementing the authentication module

// This returns a success code but I'm not sure what to do with it.

authenticator.secureResponse(request, response, authResult);

/** * The actual Server Authentication Module AKA SAM. * */ public class TestServerAuthModule implements ServerAuthModule { private CallbackHandler handler; private Class<?>[] supportedMessageTypes = new Class[] {HttpServletRequest.class, HttpServletResponse.class }; @Override public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) throws AuthException { this.handler = handler; } @Override public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { // Normally we would check here for authentication credentials being // present and perform actual authentication, or in absence of those // ask the user in some way to authenticate. // Here we just create the user and associated roles directly. // Create a handler (kind of directive) to add the caller principal (AKA // user principal) "test" (=basically user name, or user id) // This will be the name of the principal returned by e.g. // HttpServletRequest#getUserPrincipal CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, "test"); // Create a handler to add the group (AKA role) "architect" // This is what e.g. HttpServletRequest#isUserInRole and @RolesAllowed // test for GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback( clientSubject, new String[] { "architect" } ); // Execute the handlers we created above. This will typically add the // "test" principal and the "architect" // role in an application server specific way to the JAAS Subject. try { handler.handle(new Callback[] { callerPrincipalCallback, groupPrincipalCallback }); } catch (IOException | UnsupportedCallbackException e) { e.printStackTrace(); } return SUCCESS; } /** * A compliant implementation should return HttpServletRequest and * HttpServletResponse, so the delegation class {@link ServerAuthContext} * can choose the right SAM to delegate to. In this example there is only * one SAM and thus the return value actually doesn't matter here. */ @Override public Class<?>[] getSupportedMessageTypes() { return supportedMessageTypes; } /** * WebLogic 12c calls this before Servlet is called, Geronimo v3 after, * JBoss EAP 6 and GlassFish 3.1.2.2 don't call this at all. WebLogic * (seemingly) only continues if SEND_SUCCESS is returned, Geronimo * completely ignores return value. */ @Override public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { return SEND_SUCCESS; } @Override public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { } }

Step 6 - Setting up declarative security in web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <security-constraint> <web-resource-collection> <web-resource-name>Test</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>architect</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>architect</role-name> </security-role> </web-app>

Step 7 - Setting up the mandatory proprietary descriptors

java.lang.NoClassDefFoundError: jaspic/TestServerAuthConfig at jaspi.TestAuthConfigProvider.getServerAuthConfig(TestAuthConfigProvider.java:50) at org.apache.geronimo.tomcat.BaseGeronimoContextConfig.configureSecurity(BaseGeronimoContextConfig.java:177) at org.apache.geronimo.tomcat.WebContextConfig.authenticatorConfig(WebContextConfig.java:51) at org.apache.geronimo.tomcat.BaseGeronimoContextConfig.configureStart(BaseGeronimoContextConfig.java:116)

Servers -> Server Types -> WebSphere application servers -> Security Domain -> Application Security -> Enable application security

Servers -> Server Types -> WebSphere application servers -> Security Domain -> Enable Java Authentication SPI (JASPI)

"

Users and Groups -> Manage Users -> Create -> [User id = test]

Users and Groups -> Manage Groups -> Create -> [Group name = architect]

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Servlet 2.5//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd"> <sun-web-app> <security-role-mapping> <role-name>architect</role-name> <group-name>architect</group-name> </security-role-mapping> </sun-web-app>

<?xml version="1.0" encoding="UTF-8"?> <nec-web-app> <security-role-mapping> <role-name>architect</role-name> <group-name>stGroup</group-name> </security-role-mapping> </nec-web-app>

<?xml version = "1.0" encoding = "UTF-8"?> <weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app.xsd" xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app"> <security-role-assignment> <role-name>architect</role-name> <principal-name>architect</principal-name> </security-role-assignment> </weblogic-web-app>

<?xml version="1.0"?> <jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="7.0"> <role-mapping> <role-permission> <role>architect</role> <principal>architect</principal> </role-permission> </role-mapping> </jeus-web-dd>

<?xml version="1.0" encoding="UTF-8"?> <application-bnd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-application-bnd_1_1.xsd" xmlns="http://websphere.ibm.com/xml/ns/javaee" version="1.1"> <security-role name="architect"> <group name="architect" /> </security-role> </application-bnd>

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <web:web-app xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2" xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0" xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1" > <dep:environment> <dep:moduleId> <dep:groupId>default</dep:groupId> <dep:artifactId>jaspic</dep:artifactId> <dep:version>1.0</dep:version> </dep:moduleId> </dep:environment> <web:security-realm-name>geronimo-admin</web:security-realm-name> <sec:security> <sec:role-mappings> <sec:role role-name="architect"> <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" name="architect" /> </sec:role> </sec:role-mappings> </sec:security> </web:web-app>

<?xml version="1.0"?> <jboss-web> <security-domain>other</security-domain> <valve> <class-name>patch.jboss.WebJASPIAuthenticator</class-name> </valve> </jboss-web>

(note that for JBoss EAP/AS normally jaspi.WebJASPIAuthenticator is used to activate it)

<?xml version="1.0"?> <jboss-web> <security-domain>jaspitest</security-domain> </jboss-web>

(UPDATE: See Activating JASPIC in JBoss WildFly for other WildFly versions)

Step 8 - Implementing a test Servlet

@WebServlet(urlPatterns = "/servlet") public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write( "Username " + request.getUserPrincipal().getName() ); } }

Step 9 - Working around bugs

Step 10 - Taking behavioral differences into account

JBoss EAP GlassFish WebLogic Geronimo WebSphere Invokes SAM for protected resources V V V V V Invokes SAM for non-protected resources X / V (with optional valve) V V V V Invokes SAM after authentication X V V V V CallerPrincipalCallback accepts any name V V V V X (demands name to be known upfront via admin console) GroupPrincipalCallback accepts any name V V V V X (demands name to be known upfront via admin console) javax.security.auth.message.MessagePolicy.isMandatory = false in MessageInfo Map for non-protected resources V (with optional valve) X X X V requestDispatcher.forward works X (but works with proprietary request#getRequest) V X (works when CDI is not enabled) V V/X (effectively works, but exception is logged) ValidateRequest can set wrapped request in MessageInfo X X (almost works, tiny bug prevents it from actually working) X X X SAM method called after request#logout X X X V (cleanSubject()) X Remembers authentication for request.userPrincipal V X V X X Remembers authentication for protected resources V X X X X appContext for web module "app" [getLocalName] /app server /app server /app server /app default_host /app "java:comp/" available in SAM V X X V CDI request/session scope active in SAM (start of request) V V/X X X/? CDI request/session scope active in SAM (after authenticate call) V V V X/? Embedded SAM has same class loader as App V V V X

Please note that an X doesn't mean "bad" in all cases. For instance "Remembers authentication" should NOT be done according to the JASPIC spec, so here a X means "good".



Source code

Update

Conclusion

In Java EE it has always been relatively straightforward to specify to which resources security constraints should be applied.For web resources (Servlets, JSP pages, etc) there is theelement in web.xml, while for EJB beans there's theannotation. Via this so called 'declarative security' the programmer can specify that only a user having the given roles is allowed access to the protected web resource, or may invoke methods on the protected bean.The declarative model has a programmatic counterpart via methods like, where the same kind of role checks can be done from within code (allowing for more elaborate combinations, e.g. has role 'admin' but not has role 'manager', or if price > 5 and not has role 'manager', etc).This is indeed straightforward and easy to use. Unfortunately, when it comes to implementing the actual authentication code (the code that actually loads a user and associated roles from some place and checks e.g. the password), things are not so simple.Traditionally, Java EE simply didn't sayauthentication should be done at all, which greatly confused (new) users. The idea here is that security is setup inside the application server, and is done in a vendor specific way. In addition to that, a WAR or EAR will typically also have to contain vendor specific deployment descriptors which require setting up and configuring vendor specific things, often using vendor specific terminology. For instance, some application servers require specifying something called a "domain", which then approximately but not exactly corresponds to what another server may call a "realm", "zone", or "region". Roles can also rarely just be... roles. Many servers, but not all, require you to first map them to things like a "group", "principal", or "right" (which again are all roughly the same thing).Ignoring the terminology confusion, this model works well for the situation where externally obtained applications need to be integrated in the existing intranet of an enterprise, and where existing user accounts residing in e.g. the enterprise's LDAP server need to access those applications. Examples of such applications are things like JIRA or Sonar. In that situation, if JIRA would use the role name "admin" and your organization uses the name "administrator", it's convenient that there's a way to map between those roles.However, for applications that are developed in-house and are solely aimed to be deployed by the same organization that developed them and which are intended for the general Internet public (i.e. your typical web app), all this mandatory mapping is completely unnecessary.Having the security setup inside the application server is an abstraction that only gets in the way if there is only ever one application deployed to that application server. Worse, because the functionality to create a user account is typically an integrated part of the above mentioned web applications, being forced to setup security outside the application really doesn't work nicely. Among others it prevents the application to easily use its own domain models for the authentication process (like e.g. a JPA entity). There are some popular workarounds for this, like login modules that allow one to directly query a user and its roles from the same database that the application is using, but these are inelegant at best and require the details about how theentity is persisted to reside at two places.The fact that the authentication mechanism is vendor specific doesn't just hurt the portability of Java EE applications, it also hurts learning about Java EE. Namely, in order to secure a Java EE application, you can't just study Java EE books and tutorials, but you also have to learn e.g. JBoss, or GlassFish. Especially for lesser known application servers, it can be very frustrating to dig up that information. Essentially it makes Java EE developers less able to move between jobs and makes it harder for companies to hire experienced employees.All of this unfortunately seems to add to the feeling that Java EE is, a reputation that Sun, now Oracle and partners have been trying hard to shake off. Indeed, a technology like EJB has been massively slimmed down by among others simply not forcing certain restrictions upon users and having smart defaults. Yes, (business) interfaces for services and separating business code into its own layer may be a best practice in some situations, but it's a choice users should make and in EJB 3.1 this choice was finally given to the user.Unfortunately this is not the case. JAAS comes a long way in introducing a basic set of security primitives and overall establishing a very comprehensive security framework, but one thing it doesn't have knowledge about is how to integrate with a Java EE container. Practically this means that JAAS has no way of communicating a successful authentication to the container. A user may be logged-in to some JAAS module, but Java EE will be totally unaware of this fact. The reverse is also true; when a protected resource is accessed by the user, or when an explicit login is triggered via the Servlet 3 HttpServletRequest#login method, the container has no notion of which JAAS login module should be called. Finally, there is a mismatch between the very general JAAS concept of a so-called Subject having a bag of Principals and the Java EE notion of a caller principal and a collection of roles. For further reading about this particular subject; Raymond Ng wrote an excellent article about this a few years ago.Nearly all vendor specific authentication mechanisms are in fact based on JAAS, but each vendor has taken its own approach to implementing the container integration, how to map the above mentioned caller principal and roles to the JAAS Subject, and how to let a user install and specify which authentication modules should be used for a given application (or domain, realm, zone, etc).In actuality, the idea that there should be an API in Java EE that standardized the above mentioned integration already existed a long time ago, in 2002 to be precise when the JASPIC JSR (JSR 196) was created. For some reason or the other, it took a very long time for this JSR to be completed and it wasn't included in Java EE until Java EE 6 (2009).JASPIC finally standardizes how an authentication module is integrated into a Java EE container. However, it's not without its problems and has a few quirks.Probably in order to maintain compatibly with the existing ways that containers use the JAAS Subject, JASPIC did not specify which parts of this Subject correspond to the caller principal and roles. Instead, it uses a trick involving a so called callback handler. This works in 2 steps. First JASPIC introduced 2 types (called callbacks), that do contain this information in clearly specified fields. These are called CallerPrincipalCallback and GroupPrincipalCallback . Secondly the authentication module is given a handler implementation that reads the data from those two types and then stores it in a container specific way into the JAAS Subject. It's a bit convoluted, but it does do the trick.Another strange aspect of JASPIC is its name. Seemingly people can't agree on whether it should be JASPIC or JASPI. Important vendors like JBoss and IBM call it "JASPI" in e.g. documentation and package names of source code. Oracle calls it "JASPIC". It's a small thing perhaps, but terminology is important and even though the difference is just one letter, it makes searching more difficult since many search engines emphasize full words. In e.g. JIRA and on Google I found searching for just "JASPI" did not always gave me the results that "JASPIC" would give me. And although it has now mostly faded away, JASPIC was once known by yet another name; JMAC. It looks like a rather different name, but it's an abbreviation for " Java Message Authentication SPI for Container(s) ", which is almost identical to the current "Java Authentication SPI for Containers". The term "jmac" is still used in the GlassFish source code, which has apparently not been refactored after the name changed.For something that was added to Java EE 6, it really feels out of place that JASPIC is still limited to the Java 1.4 syntax. From Servlet, to JSF to EJB and JPA; pretty much everything has adopted at least the Java 5 syntax. The JASPIC 1.0mr1 spec does mention this issue, but merely states that. -Why- this requirement is there, and why it holds for JASPIC but not for most of the other specifications in Java EE 6 is however not really clear.A serious problem at the moment is the fact that adoption of JASPIC by vendors has been slow. JASPIC may be mandated for a Java EE 6 implementation, but only for the full profile. This means the very important web profile (with implementations like TomEE and Resin) does not need to implement it (not even the Servlet Container Profile, which is a subset of the full JASPIC spec). Web profile implementations do need to implement authentication modules (since security is a mandatory part of both Servlet and EJB-lite), but they have chosen to implement those using their own APIs. This is worrying. JASPIC isn't about something that web profile apps don't need, but is about doing something they need in a specific way. Perhaps this way is not yet good enough or not yet mature enough, or maybe there is just too much investment in proprietary solutions and the advantages of JASPIC are not seen as compelling enough, since otherwise those web profile implementations might have adopted JASPIC of their own account by now, without needing to be forced to implement it. (Full profile implementations have implemented JASPIC of course, but most present it as a secondary option; for those usecases where a user happens to have a JASPIC authentication module that needs to be used. For "normal" security, the vendors' proprietary solutions are still being presented as the primary solution. As a result, various JASPIC implementations are a little buggy. This is however not a rare situation. The story is rather similar for the standardized embedded data source that was introduced in Java EE 6 (@DataSource or data-source in web.xml). Initially adoption of this was rather slow and most if not all vendors kept plugging their own proprietary ways to define data sources. Lately the situation has improved somewhat, but perhaps the Java EE certification process should be a bit stricter here.Then there's the question of how to tell a container to use a particular authentication module for a particular application or perhaps for the entire server. In order to do this there are typically a number of options; via some kind of admin UI or console offered by the application server, declarative via configuration files or annotations (where those configuration files can either reside inside a WAR/EAR or inside the server itself), or programmatically via some API.Unfortunately, the only method that JASPIC standardized is the programmatic option. And this programmatic option seems to be aimed more at vendors needing an internal API to register modules than at user code registering their own at startup. So in practice the already ill-advertised and sometimes buggy standardized method appears to be not that standard at all. The JASPIC documentation of all vendors to install the authentication module inside the server , create or edit proprietary configuration files and as if that isn't insulting enough to a developer not rarely requires interacting with a graphical UI as well. Clearly such documentation is aimed at system administrators setting up the kind of traditional servers that are used to run externally obtained applications that need to integrate with the existing infrastructure. Developers creating applications that need to manage their own users are largely left in the cold here.Nevertheless, the programmatic API is sort of useable for applications to register their own internal authentication module. Of 5 servers that I tested; JBoss EAP 6.0 (JBoss AS 7.1.2.Final-redhat-1), GlassFish 3.1.2.2, WebLogic 12c, Geronimo v3 and WebSphere 8.5, only Geronimo seemed to have overlooked the possibility for web apps registering their own authentication module. For the other servers it did more or less work, but it's striking that even when using programmatic registration none of them could actually do their job without a vendor specific deployment descriptor being present (which is something related to the general concept of security in Java EE and not a specific fault of JASPIC).The first hurdle when attempting to use JASPIC for programmatically registering just an authentication module is the fact that there isn't a convenience API to do just that. Instead there's something that's essentially a factory-factory-factory for a delegator to an actual authentication module. That's right, it's a quadruple indirection. Usefull and flexible for those situations that require it no doubt, but more than a little intimidating for novice JASPIC users. Another hurdle is that the initial factory used for registering the factory-factory requires an "appContext" identifier. This identifier is specified to be either null, or be composed of the pattern [hostname] [space] [context path]. When the identifier is null, the registration is for all (web) applications, otherwise it's only for a specific one. Clearly when an application registers its own internal authentication module the latter form is needed. The problem is that this "hostname" part is not that easy to guess when doing programmatic registration at startup time. It's further defined as being a "logical host", but how does an app knows what its own logical host is? The situation is further complicated by the fact that all servers except JBoss EAP just use a constant here, which is simply "server" in case of GlassFish, Geronimo and WebLogic and "default_host" in case of WebSphere. JBoss EAP however uses ServletRequest#getLocalName here, which is a value that's only available during request processing and not during startup time. It seems likely that if internal application server code is doing both the registration and the subsequent lookups, this is not really a problem. The AS itself knows which key it used for registration and can easily use the same one for lookups later. But when user code needs to do a registration independent of the application server that later on does the lookup, this becomes a problem. Maybe JBoss has interpreted the spec wrongly and the logical host should really be the constant "server", but then the spec needs to be clarified here. If it really should be a logical host of some kind, then there also needs to be a way to express that the application doesn't care about this (for example by specifying "*" as a kind of bind-all). As it stands, the situation is highly confusing.The code below shows how to programmatically register a sample JASPIC authentication module. The module itself will be as simple as can be, and always just "returns" a user with a name and one role.We first obtain a reference to the factory-factory-factory (), which we use to register our own factory-factory (shown highlighted). We need to specify for whichwe're doing the registration, which needs to be the constant "HttpServlet" for the Servlet Container Profile. For this example we evade the problems with theand provide a null, which means we're doing the registration for all applications running on the server.In the next step we look at the factory-factory that we registered above, which is an implementation of AuthConfigProvider . This factory-factory has a required constructor with a required implementation. The implementation seemed trivial; we need to do a self-registration. As I wasn't sure where some of the parameters had to be obtained from, I used my good friendagain.The real meat of this class is in themethod (shown highlighted), which simply has to return a factory. The flexibility that this factory-factory offers is the ability to create factories with a givenor when this is null give the factory-factory the chance to create a default handler of some sorts. There's also amethod that I think asks for updating all factories created by the factory-factory if needed. It's only for dynamic factory-factories though, so I left it unimplemented.The factory that we returned in the previous step is an implementation of ServerAuthConfig . Its main functionality is creating instances of delegators for the authentication module (shown highlighted).In our case the factory functionality is very simply; it just creates a new instance of the delegator passing only thethrough. The factories that are provided by the application servers themselves typically read-in and process the proprietary configuration files here.I observed an interesting difference here between Geronimo and the other servers tested; Geronimo calls themethod twice per request, while the others only do so once.In the delegator (an implementation of ServerAuthContext ) that was returned from the factory above we finally get a chance to create our authentication module (shown highlighted).The rest of the delegator class can be pretty simple. As we don't have any selection between modules to do, we just delegate directly to the one and only module that we encapsulate.At long last, we finally get to implement our authentication module, which is an instance of ServerAuthModule . With respect to the API, it's interesting to note that this time around there's anmethod present instead of a mandatory constructor.As mentioned before, we don't do an actual authentication but just "install" the caller principal and a role into the JAAS Subject. For this example,actually doesn't need to be implemented since it's only called by the delegator that encapsulates it. Since we own that delegator, we know it's not going to call this method. For completeness though I implemented it anyway to be compliant with the Servlet Container Profile.Interesting to note is thatwas treated differently by most servers. Only WebLogic and Geronimo call this method, but where WebLogic insists on seeingreturned, Geronimo just ignores the return value. In its class, it contains the following code fragment:Another difference for this samemethod, is that WebLogic calls it before a protected resource (e.g. Servlet) is called, while Geronimo does so after.To test our code we first setup a security constraint using web.xml. It will simply require the rolefor all resources in our web application.A very unfortunate and nasty step is that we -have- to setup proprietary deployment descriptors for each container.The majority of them (all, except JBoss EAP) don't directly accept the roles that our authentication module puts into the JAAS Subject, but forces us to map them. This necessitates a rather silly and pointless mapping where every time we mapto. This will be extra painful when we are building an application that uses say 20 roles and we want to support those 3 servers out of the box. It will mean not less than 60 completely pointless mapping directives have to be added :(Two servers require us to specify something that JBoss calls a, but Geronimo calls a. The idea behind this concept is that it's a kind of alias for a whole slew of security configuration options (typically which authentication modules should be used). Of course, if we're registering our own authentication modules programmatically this is rather pointless as well. JBoss actually seems to want that we modify a file calledinside the JBoss installation directory (a horror for portable apps that take care of their own security configuration), but luckily there's already a domain defined there by default that we can use. The problem with these default things in JBoss is that JBoss does like to change them on a whim between (major) releases. Today I found a domain called "other" to be useable, but unfortunately I know from experience this might have another name in the next release.Two servers, Geronimo and JBoss also needed extra configuration to work around bugs, where in the case of JBoss this configuration was needed because despite being Java EE 6 certified JBoss seemingly does not want to make JASPIC available by default; the user has to explicitly activate it. In the case of Geronimo, it was required to specify something called a, or otherwisewould be thrown:WebSphere 8.5 was particularly troublesome here. The example application is a WAR, but the file in which the roles mapping had to be done could only reside in an EAR. So, specifically for WebSphere an extra wrapping EAR had to be created. Even more troublesome was that with WebSphere security it self first had to be activated in a graphical admin console (by default at https://localhost:9043/ibm/console). It's a well known caveat . After security was activated, JASPIC had to be separately activated as well. There seemed to be an option for this in the proprietary deployment descriptor, but unfortunately this didn't work. Likely this option is there to register a SAM declaratively, and it doesn't do anything without this SAM being given. More precisely the two settings that needed to be changed via the admin console are:After that and adding the role mapping, authentication kept failing. The only thing that was logged was:, which is a rather poor problem description. After hours of searching, the proprietary alternative to the JASPIC callback handlers hinted at a solution. Namely, most JASPIC handlers are just wrappers around whatever proprietary mechanism the server has or had in place before JASPIC. In this case, the alternative solution asked to "get a unique user id" from some "registry". But how does WebSphere know about these users? As it appeared; creating them via the Admin Console again:After this, authentication finally succeeded. However, it's of course undoable to manually add all groups and especially all users to the admin console. How would this even work when users register themselves via the web? Hopefully there's an option somewhere to disable this, but I haven't found it yet.When it comes to proprietary stuff, Websphere was clearly the worst offender. Having to mock around with a GUI before the app can run is just not tolerable for the kind of application we're trying to build here. But Geronimo was not innocent either. As it stands Geronimo requires both the "security realm" thing and the role mapping to be specified, as well as some gibberish for working around what seems to be a bug.WEB-INF/sun-web.xmlWEB-INF/nec-web.xmlWEB-INF/weblogic.xmlWEB-INF/jeus-web-dd.xml[EAR]/META-INF/ibm-application-bnd.xmlWEB-INF/geronimo-web.xmlWEB-INF/jboss-web.xmlWEB-INF/jboss-web.xmlIt's amazing really how the exact same nonsense mapping oftocan be expressed in so many nearly identical but still different ways.In order to test that a request is getting authenticated, we also need an actual resource. For this I used a simple Servlet that just prints the name of the caller principal. Note that should the authentication module fail to put a caller principal into the JAAS Subject, this will result in aOf the 4 servers tested, 2 of them have severe bugs that make the sample authentication module and programmatic registration as shown above unusable.JBoss AS 7.1.1 and JBoss EAP 6 ignore the GroupPrincipalCallback , which makes it impossible to assign any roles. The way the code is setup a not yet mentioned callback, the PasswordValidationCallback , happens to be required even though the JASPIC spec does not require this one to be used at all. I reported this issue in June 2012 along with a proposal for a fix. Since then, the issue has been cloned , and the patch I proposed was committed around half Oktober of that year. Unfortunately, it wasn't included in JBoss EAP 6.0.1/JBoss AS 7.1.3.Final-redhat-4 that was released the following December, but instead is slated for JBoss AS 7.2 and JBoss AS 7.1.4. It might still take a considerable amount of time before any of those two is released. Since the bug appears in a Tomcat Valve , which is the class we explicitly reference init's relatively easy to patch ourselves.Geronimo v3.0 needs the extra gibberish in, in order to prevent various class not found exceptions. Unfortunately, JASPIC authentication still doesn't work after that. It seems that if a web application registers a JASPIC authentication module, then this registration doesn't take effect for that application itself. In order to make this work we need to start up Geronimo with the app in question deployed, then undeploy the app while the server is still running and immediately deploy it again. After this sequence JASPIC authentication works correctly. An issue for this has been created at https://issues.apache.org/jira/browse/GERONIMO-6423 With respect to the life-cycle of an authentication module and interaction with the rest of the Java platform, no two servers of the ones tested behaved exactly the same.For all application servers, the authentication module was invoked when a protected resource (thefrom our example) was invoked. This is a good thing, otherwise JASPIC wouldn't be working at all. However, there was no universal agreement on what to do with non-protected resources. JBoss EAP didn't call the SAM in this case, but all other servers did. After an initial successful authentication (e.g.subsequently returns a non-null value during the same request), the behavior differed with respect to the follow-up request. JBoss EAP would remember the full authentication, and would not call the SAM again until either the session expired or an explicit call towas made. All other servers did call the SAM again. In case we didn't re-authenticate again, then WebLogic would still remember the principal (would return the one for which we authenticated), but accessing protected resources for which the authenticated principal has the correct roles was still not allowed. GlassFish and Geronimo both didn't remember a single thing.As for accessing environmental Java EE resources, in both JBoss EAP and Geronimo it was possible to request the CDI bean manager from the standardized "java:comp/" JNDI namespace. GlassFish and WebLogic would throw binding exceptions here. When the SAM was called at the initial point during a request (before Servlet Filters are invoked) then in JBoss EAP the CDI request and session scope were already active. In GlassFish the scope seemed to be active, but when requesting a bean reference (after obtaining the bean manager via a globally accessible EJB), a scary warning was logged:. In WebLogic the mentioned contexts definitely weren't active and context not active exceptions were thrown. Geronimo was hard to test at this point, since the SAM seemingly runs in a different class loader. Things changed whenwas called from e.g. a JSF managed bean. In that case the SAM was invoked, and for most servers the CDI scopes simply remained active. Judging from the call stack between thecall and the invocation of the SAM, the CDI scopes are most likely also still active for Geronimo, but because of the class loader issues this was again hard to test.Which brings us to our last point; for all servers the SAM that was embedded and installed by the application would run with the same class loaders as said application, except for Geronimo. Remembering that we needed the trick with the deploy/undeploy/deploy cycle, this perhaps doesn't come as a surprise.To summarize, with respect to calls to themethod of an authentication module, the following differences were observed:The full example source code can be obtained from Google code As of August 2015, the situation regarding implementation differences and bugs has considerably improved.andare now mandated by the spec to be supported, and wrapping the request (and response) which at the time this article was written didn't work with a single server now works everywhere. Furthermore, WebLogic doesn't require the mandatory role mapping anymore. In 2016 Payara, JBoss/WildFly and Liberty were re-tested . GlassFish and WebLogic were additionally re-tested end 2015.JASPIC is one of those things that should have been there relatively early (e.g. for J2EE 1.4 if the original timeline would have hold). By now it could have had its ease-of-use treatment in Java EE 5 and subsequent tuning in Java EE 6. Vendors then might not have had the ~10 years worth of their own proprietary technology in place, which perhaps is currently one of the reasons not all of them are embracing JASPIC beyond what the spec mandates.Originally a JASPIC 1.1 seemed to have been planned for Java EE 6, but eventually this turned into a smaller maintenance release. Given the various issues outlined above, a true JASPIC 1.1 for Java EE 7 would still be very welcome, but as Java EE 7 is nearing completion and to the best of my knowledge no such work has been started, the chance that we'll see any improvements in the short term are slim.As it stands, JASPIC is not very well known among users and not universally embraced by vendors. Some users that do know JASPIC find it a " little technical ". Where unfortunately some vendors go as far as to call it " bloated ", other vendors are waiting for more " widespread adoption " before fully embracing it (which is a kind of chicken-and-egg problem).Despite all this doom and gloom, the fact is that JASPIC -is- here and it really does offer a good portable way to integrate with container authentication. The bugs that are currently pressent in some implementations can of course be fixed and since the API is standardized there's nothing stopping a third party library to offer some convenience utilities that make things a little easier for 'casual' users (like we also see for e.g. JSF and JPA).All JASPIC really needs now is just that extra little push.