Implementing container authentication in Java EE with JASPIC

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

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 the <security-constraint> element in web.xml, while for EJB beans there's the @RolesAllowed annotation. 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 HttpServletRequest#isUserInRole, 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.

How is authentication traditionally implemented?

Traditionally, Java EE simply didn't say how authentication 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 User). 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 the User entity is persisted to reside at two places.

Heavyweight

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 heavyweight, 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.

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.

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).

JASPIC to the rescue... sort off

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 "There is a requirement that the SPI be used in J2SE 1.4 environments". -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. (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))UPDATE: In 2016, Tomcat 9 M4 has implemented JASPIC.

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 encourage the user 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. (The GlassFish developer documentation mentions the programmatic option, but doesn't go into detail how that exactly works)

Programmatically registering JASPIC auth modules

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. UPDATE: In JASPIC 1.1/Java EE this problem has been solved.

Sample code


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.

Step 1 - Registering via the factory-factory-factory

We first obtain a reference to the factory-factory-factory (AuthConfigFactory), which we use to register our own factory-factory (shown highlighted). We need to specify for which layer we're doing the registration, which needs to be the constant "HttpServlet" for the Servlet Container Profile. For this example we evade the problems with the appContext and provide a null, which means we're doing the registration for all applications running on the server.
@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

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 friend null again.

The real meat of this class is in the getServerAuthConfig method (shown highlighted), which simply has to return a factory. The flexibility that this factory-factory offers is the ability to create factories with a given handler or when this is null give the factory-factory the chance to create a default handler of some sorts. There's also a refresh method 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.
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

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 the handler through. 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 the getAuthContext method twice per request, while the others only do so once.
/**
 * 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

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.
/**
 * 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

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 an initialize method 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, getSupportedMessageTypes 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 that secureResponse was treated differently by most servers. Only WebLogic and Geronimo call this method, but where WebLogic insists on seeing SEND_SUCCESS returned, Geronimo just ignores the return value. In its class org.apache.geronimo.tomcat.security.SecurityValve, it contains the following code fragment:
// This returns a success code but I'm not sure what to do with it.
authenticator.secureResponse(request, response, authResult);
Another difference for this same secureResponse method, is that WebLogic calls it before a protected resource (e.g. Servlet) is called, while Geronimo does so after.
/**
 * 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

To test our code we first setup a security constraint using web.xml. It will simply require the role architect for all resources in our web application.
<?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

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 map architect to architect. 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 domain, but Geronimo calls a security realm. 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 called domain.xml inside 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 moduleId, or otherwise ClassNotFoundExceptions would be thrown:
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)


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:
  • 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)
After that and adding the role mapping, authentication kept failing. The only thing that was logged was: "SECJ0056E: Authentication failed for reason ", 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:
  • Users and Groups -> Manage Users -> Create -> [User id = test]
  • Users and Groups -> Manage Groups -> Create -> [Group name = architect]
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.

GlassFish 3.1.2.2
WEB-INF/sun-web.xml
<?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>

WebOTX 9
WEB-INF/nec-web.xml
<?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>

WebLogic 12c (12.1.1)
WEB-INF/weblogic.xml
<?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>

JEUS 8
WEB-INF/jeus-web-dd.xml
<?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>

WebSphere 8.5
[EAR]/META-INF/ibm-application-bnd.xml
<?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>

Geronimo v3.0
WEB-INF/geronimo-web.xml
<?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>

JBoss EAP 6.0.0.GA
WEB-INF/jboss-web.xml
<?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).

JBoss WildFly 10.0.0.Final
WEB-INF/jboss-web.xml
<?xml version="1.0"?>
<jboss-web>
    <security-domain>jaspitest</security-domain>
</jboss-web>
(UPDATE: See Activating JASPIC in JBoss WildFly for other WildFly versions)

It's amazing really how the exact same nonsense mapping of architect to architect can be expressed in so many nearly identical but still different ways.

Step 8 - Implementing a test Servlet

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 a NullPointerException.
@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

Of 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 in jboss-web.xml it's relatively easy to patch ourselves.

Geronimo v3.0 needs the extra gibberish in geronimo-web.xml, 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


Step 10 - Taking behavioral differences into account

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 (the TestServlet from 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. request.getUserPrincipal() 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 to request#logout was made. All other servers did call the SAM again. In case we didn't re-authenticate again, then WebLogic would still remember the principal (request.getUserPrincipal() 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: "SEVERE: No valid EE environment for injection of ...". 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 when request#authenticate was 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 the authenticate() call 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 the validateRequest() method of an authentication module, the following differences were observed:

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

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

The full example source code can be obtained from Google code.

Update

As of August 2015, the situation regarding implementation differences and bugs has considerably improved. requestDispatcher#forward and request#logout are 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.

Conclusion


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.

Arjan Tijms


Further reading:
See my ZEEF page about JASPIC.

Comments

  1. Arjan,

    great post! I'm using your example code in a EE7/glassfish 4 environment.

    You say to set up the security-constraint in web.xml.
    I tried to restrict the constraint to a certain part of the application (say url-pattern = /admin/*). Still validateRequest in the SAM is called for every request even when its not a restricted (admin) resource.

    And idea?

    Thanks
    Christian

    ReplyDelete
  2. >Still validateRequest in the SAM is called for every request even when its not a >restricted (admin) resource.

    >And idea?

    Yes, this is the intended behavior of JASPIC. validateRequest should be called for every request, independent of whether the request is to a protected resource and independent of whether the user is logged-in or not.

    This feature is very important, since it allows a SAM to e.g. automatically log you in when you go the public home page of a site based on say a cookie. This is called preemptive authentication.

    If the request is to a protected resource, the SAM has to do authentication, otherwise it can opt to "do nothing". You can check if the request is to a protected resource via the following code:

    Boolean.valueOf((String) messageInfo.getMap().get(IS_MANDATORY));


    Also see http://code.google.com/p/omnisecurity/source/browse/src/org/omnifaces/security/jaspic/Jaspic.java#179

    ReplyDelete
  3. Great article. Really helpful. Thanks!

    ReplyDelete
  4. What are the pros/cons of using a ServerAuthModule over an AppServPasswordLoginModule?

    ReplyDelete
    Replies
    1. >What are the pros/cons of using a ServerAuthModule over an AppServPasswordLoginModule?

      The ServerAuthModule is a standardized type. It works on all Java EE servers, like JBoss, GlassFish, JEUS, WebLogic, WebSphere, Geronimo, etc.

      With AppServPasswordLoginModule I guess you mean com.sun.appserv.security.AppservPasswordLoginModule. This is a GlassFish specific type that thus only works on GlassFish.

      If you know how to write such module you can't use that same knowledge to implement a similar module on JBoss; you have to relearn from scratch how to do it.

      Delete
    2. Arjan, thanks, and also really informative article! For the sake of argument, let's say I commit to using Glassfish, is there a good argument to using one technique over the other?

      Delete
  5. Hi Arjan Tijms,
    I'm having this exact problem below. Could you help me?

    http://stackoverflow.com/questions/19731679/how-to-obtain-httpservletrequest-in-appservpasswordloginmodule-ssl

    ReplyDelete
    Replies
    1. I'm afraid I can't really help you with that. As you may have noticed I mainly write about Java EE standard auth modules and thus have little or no experience with login modules that are proprietary for a specific container.

      When you use a JASPIC auth module instead of the GlassFish proprietary mechanism you already get the HttpServletRequest instance, so you might want to look at that.

      Delete
  6. Hi Arjan Tijms,

    Would be possible to implement a ServerAuthModule which accepts multiple login methods, let's say username/password or Oauth?

    Do you mind point me on the right direction?

    Thanks,
    Bruno

    ReplyDelete
    Replies
    1. Theoretically yes. A ServerAuthModule is a very low level thing and can basically do anything. It gets called by the container at the start of an HTTP request, and what code it runs itself or what other code it calls is completely up to the module.

      When an authentication (login) has eventually happened by whatever mechanism(s), the ServerAuthModule communicates the username and roles to the container. The container naturally should not know nor care where this username and these roles were obtained from.

      When you do multiple login methods, you yourself have to keep track though of what method was used. If the login is a multi-step thing you have to remember somewhere (e.g. in the session) which method is currently being used (or tried, if you execute multiple ones in sequence).

      Because JASPIC is such a low-level mechanism there's barely any higher level API to help you with this task, you have to do it yourself. The only thing that's there is that the ServerAuthModule should more or less implement 1 method only, while the ServerAuthContext (see example in the article) should be the place where you manage multiple ServerAuthModules (but the container can't enforce this and it's up to you whether you use this specific devision of responsibilities).

      I've been working on and off on a library that provides some of this higher level functionality, and does indeed allow you to choose between various mechanism. It's however not entirely polished yet and a bit difficult to learn from, but maybe it's useful for you anyway: https://github.com/omnifaces/omnisecurity

      Delete
    2. Arjan,

      Thanks for the reply. I've been reading about Login context in JAAS:

      http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/AcnOnly.html

      Do you think it might be a way to do it?

      Thanks,

      Bruno Palermo

      Delete
    3. >Do you think it might be a way to do it?

      Unfortunately not. As I've explained in the article and which is even better explained by Raymond K. NG (see references above), Java EE does not automatically know about JAAS.

      Using the Login Context will NOT communicate anything to the Java EE container. There is no automatic connection whatsoever between the JAAS Login Context and Java EE.

      Just think of it as implementing your own class, e.g.:

      public class MyLogin {
      public boolean login;
      }

      Then you execute:

      MyLogin myLogin = new MyLogin()
      myLogin.login = true;

      Now you're logged-in according to the instance of the MyLogin class, but of course it's trivial to see that setting this boolean to true does nothing for Java EE. It's exactly the same with JAAS.

      In order to communicate the login details from a JAAS LoginModule to Java EE a connecting thing is needed. This thing is JASPIC, which is the topic of this article ;)

      Delete
    4. Arjan,

      Great explanation! Thank you very much for your time.

      Delete
  7. Arjan,

    I implemented your example for Glassfish and added a few modifications based on this article:

    https://blogs.oracle.com/nasradu8/entry/loginmodule_bridge_profile_jaspic_in

    But now I'm struggling to integrate with Glassfish SSO solution.

    I hope that maybe you could help me with some points.

    Thanks,
    Bruno Palermo

    ReplyDelete
  8. Regarding WebSphere, you have to specify the user registry to be used by your installation:

    http://www-01.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/tsec_useregistry.html?cp=SSAW57_8.5.5%2F1-8-2-33-2-1&lang=en

    ReplyDelete
    Replies
    1. Thanks a lot for the pointer dbaltor!

      I half suspected something like that, but not being anywhere near a WebSphere expert I had no idea where to look and will certainly try out specifying that user registry. I do think it's not entirely correct, because when JASPIC is used a SAM should be in total control and not depend on some server specific configuration to work.

      Then again, in the case of WebSphere you also need to activate (Servlet) security itself which always baffled me as well. Why is WebSphere out of the box not Servlet compliant and do you need to switch a toggle to make it so?

      Anyway, thanks again for your pointer and I'll surely take a look.

      Delete
    2. Welcome Arjan!

      Indeed, I've found a reference that states:
      "The WebSphere Application Server implementation of CallbackHandler supports three callbacks:
      CallerPrincipalCallback
      GroupPrincipalCallback
      PasswordValidationCallback
      WebSphere Application Server expects the name values obtained with PasswordValidationCallback.getUsername() and CallerPrincipalCallback.getName() to be identical. If they are not, unpredictable results occur."

      So it looks like your implementation didn't work as it was supposed to and maybe WebSphere was still looking for an user id in its configured repository, in your case a default (file) repository which was empty (but wasadmin account). That possibly can explain why you had to create an user id using the admin console.

      Check it out here:
      http://www-01.ibm.com/support/knowledgecenter/api/content/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/tsec_jaspi_develop.html

      Delete
    3. >So it looks like your implementation didn't work as it was supposed to and maybe WebSphere was still looking for an user id in its configured repository, in your case a default (file) repository which was empty (but wasadmin account).

      Indeed, after a debugging session (WebSphere being closed source is horribly difficult to debug, but you can deduct a lot) this is what seemed to happen indeed. I posted about this some year ago here: https://www.ibm.com/developerworks/community/forums/thread.jspa?threadID=470426

      The problem is that the implementation I used should be what's mandated by the spec. I had a quick chat with the JASPIC spec lead about this a while ago and he seemed to support that.

      As mentioned, when a JASPIC SAM is configured, the SAM is in control of the authentication process. The SAM can delegate to other systems (repositories), but that's all under the direct control of the SAM's code. Whatever name + groups the SAM puts in the CallerPrincipalCallback and GroupPrincipalCallback have to be accepted at face value by the container.

      WebSphere is the only server that still checks the name + groups with this proprietary repository. This then means that for WebSphere you always have to pair your JASPIC module with an IBM specific repository plug-in, unless there's a default NO-OP repository that just says "yes" or "accepted" to every check. I'll still have to dive deeper into this subject to see if this is indeed possible.

      At any length, having to do this pairing and/or coding something specifically for WebSphere goes against the very idea of JASPC, which is to provide portable authentication modules.

      Delete
    4. I do agree with you that WebSphere's behavior of checking the username on a repository is neither the right action it should take and nor the expected one according to the product documentation. That's why I’ve pointed you to the post which mentions:

      "WebSphere Application Server expects the name values obtained with PasswordValidationCallback.getUsername() and CallerPrincipalCallback.getName() to be identical. If they are not, unpredictable results occur."

      I suppose (it’s just an assumption!!) this behavior is being sparked by the absence of a PasswordValidationCallback in your Callback[] array. So, if I may say so, I suggest you add the following lines on your validateRequest method:

      PasswordValidationCallback passwordValidationCallback =
      new PasswordValidationCallback(clientSubject, "test", null);

      try {
      handler.handle(new Callback[] { callerPrincipalCallback,
      groupPrincipalCallback,
      passwordValidationCallback });
      } catch (IOException | UnsupportedCallbackException e) {
      e.printStackTrace();
      }

      Delete
    5. > I suppose (it’s just an assumption!!) this behavior is being sparked by the absence of a PasswordValidationCallback in your Callback[] array.

      May indeed be something to look into as well. I personally interpreted that statement though as "IFF you use the PasswordValidationCallback, THEN the name values should be identical". This indeed sounds reasonable to me.

      The passwordValidationCallback however is not applicable if the SAM manages the passwords (or, as in increasing many cases, there's not a password to begin with). Therefore using the passwordValidationCallback should be completely optional, but I'll certainly try if it does anything on WebSphere. Thanks again!

      Delete
    6. They sort of fixed this on 8.5.5.7. You still require the group to be defined in the registry, but you don't require the user anymore.

      Delete
  9. This comment has been removed by the author.

    ReplyDelete
  10. Your example code is not conforming all JSR-196 API requirements.

    AuthConfigProvider should return not null implementation of ClientAuthConfig and ServerAuthConfig. If provider do not support client side authentication, implementation of ClientAuthConfig should return null from getAuthContext method.

    Look at page 140 of JSR-196 1.1 version maintenance release B.

    Note that GlassFish has "Default Principal To Role Mapping" security flag (defaulted to false). If you switch it to true (by admin console or asadmin tool), additional XML descriptor for role mapping is not necessary.

    ReplyDelete
    Replies
    1. Thanks for your comment Michał :)

      The AuthConfigProvider as demonstrated attempts to be the most minimal example to get a SAM installed for the Servlet Container Profile. Indeed, client side authentication is completely ignored here. But you're right, just for correctness it maybe should do just that, even when in practice this is never really a problem given the very limited applicability of this very, very specific AuthConfigProvider.

      >Note that GlassFish has "Default Principal To Role Mapping" security flag (defaulted to false).

      Yes, I'm fully aware of that ;) But the intend here was to do everything from within the archive. If only that flag could be switched from within the archive (e.g in sun/glassfish-web.xml) then I surely would have used that. But asadmin tools and especially admin consoles are just not an option here.

      Delete
  11. Arjan,

    Where would you put settings information, like JDNI Datasource, so it can be retreived by the ServerAuthModule?

    Thanks,
    Bruno Palermo

    ReplyDelete
  12. What do you make of https://docs.oracle.com/middleware/1213/wls/SECMG/jaspic.htm#SECMG663
    which mentions requirements to make configurations on the weblogic console and have classes on the system classpath?
    My attempts with weblogic 12.1.3 work for my web application but seem to break the weblogic console. I can't find anything that explains how to forward to other authentication mechanisms a la REQUIRED/REQUISITE/SUFFICIENT/OPTIONAL flags.

    ReplyDelete
  13. Hi Arjan, I am trying to run authentication module example with wildfly8.2.0 but it is not working. Can you pls suggest me, Do I need to add anything in standalone.xml. Pls guide.

    Thanks - Pradeep

    ReplyDelete
    Replies
    1. It's indeed necessary to add something in standalone.xml for WildFly. This is *extremely* unfortunate as it should "just-work", but it is what it is I guess.

      You need to add the XML to standalone.xml as mentioned here:

      https://github.com/javaee-samples/javaee7-samples/issues/243

      And then add a jboss-web.xml like here:

      https://github.com/arjantijms/javaee7-samples/blob/master/jaspic/basic-authentication/src/main/webapp/WEB-INF/jboss-web.xml

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Hi Arjan,

      Actually I am trying to implement Form Based authentication Using Jaspic and I refer the given links but could not make it. Here is code ..

      -------------Web.xml----------------------------
      {security-constraint}
      {web-resource-collection}
      {web-resource-name}Home{/web-resource-name}
      {url-pattern}/*{/url-pattern}
      {/web-resource-collection}
      {auth-constraint}
      {role-name}admin{/role-name}
      {/auth-constraint}
      {/security-constraint}

      {login-config}
      {auth-method}FORM{/auth-method}
      {realm-name}form-auth{/realm-name}
      {form-login-config}
      {form-login-page}/login.jsp{/form-login-page}
      {form-error-page}/loginerror.jsp{/form-error-page}
      {/form-login-config}
      {/login-config}

      {security-role}
      {role-name}admin{/role-name}
      {/security-role}
      {/web-app}
      ------------------------jboss-web.xml-----------------------
      {jboss-web}
      {security-domain}form-auth{/security-domain}
      {disable-audit}true{/disable-audit}
      {/jboss-web}
      -----------------------standalone.xml-------------------------
      {security-domain name="form-auth" cache-type="default"}
      {authentication-jaspi}
      {login-module-stack name="lm-stack"}
      {login-module code="com.pkc.TestAuthModule" flag="required"}
      {/login-module}
      {/login-module-stack}
      {auth-module code="org.jboss.as.web.security.jaspi.modules.HTTPFormServerAuthModule" flag="required" login-module-stack-ref="lm-stack"/}
      {/authentication-jaspi}
      {/security-domain}

      Delete
    4. Hi Arjan,
      Could you please review above code and suggest to make it work ? Awaiting for your response..!
      Thank you..

      Delete
  14. Hello Arjan; thanks for all the time and effort you've tirelessly invested into the improvement of the Java EE security landscape!

    I've set up a trivial "app-agnostic" JASPIC Servlet profile-specific SAM to integrate an external authentication system with my AS. It's functioning decently, except for the case of its validateRequest method getting invoked "during service invocation" --i.e. due to applications invoking HttpServletRequest#authenticate()-- which irks me a tiny bit. Initially I handled this case the usual, simplistic (and probably naive) way; call sendRedirect on the MessageInfo-encapsulated HttpServletResponse and return a SEND_CONTINUE, so as to ask for the user to be redirected the external login endpoint. As you can imagine, I soon started running into IllegalStateExceptions due to some apps' Filters being unable to modify the HttpServletResponse. I then thought about wrapping the response in MessageInfo, but, unless I'm misinterpreting the spec here, I can only do so when returning SUCCESS. The simple workaround I've employed for the time being has been to set a redirection HTTP status code on the response without committing it. What annoys me, regardless of wrapping, is that, since the SAM is in that case principally treated as just another Filter within the chain (and certainly not the first one) by the runtime, there's no guarantee that preceeding Filter(s) won't potentially alter the status code set on the response by the SAM. How am I supposed to issue a client redirect then? Common sense tells me that, since I want the SAM to be able to cater for the authentication needs of an arbitrary number of applications (which themselves should just ignore the SAM's existence to the greatest extend possible), the SAM should be capable of "overruling" whatever Servlet Filters or other request/response-modifying components have been set up by the applications it serves --i.e. "just break their chain", if you will-- when it needs to do so. However, "during service invocation" I'm afraid it just can't.

    Am I missing something here or am I just plain wrong? I'd appreciate any insight you could provide on the matter.

    ReplyDelete
    Replies
    1. >As you can imagine, I soon started running into IllegalStateExceptions due to some apps' Filters being unable to modify the HttpServletResponse.

      I can imagine that, and there's another common cause for IllegalStateExceptions in this case and that's the fact that a response may already have been committed.

      >I then thought about wrapping the response in MessageInfo, but, unless I'm misinterpreting the spec here, I can only do so when returning SUCCESS.

      I've to double check, but for those cases where the SAM is called before the service invocation this is the only logical moment indeed, since in all other situations the response wouldn't be used anymore after that. The case for a SAM invocation during the response is more difficult, as how exactly would the wrapped response arrive at the next Filter or Servlet in that case?

      >Am I missing something here or am I just plain wrong?

      It's a difficult situation. The fact is that to a certain extent many Filters are also required to work with an arbitrary amount of applications. But they too have a hard time coping with things Filters that happen to appear in the chain before them do, and they can also realistically only wrap the response for Filters that come *after* them, not for Filters that come before them. The Filter that comes before a Filter that wraps a response still has a reference to the original response that can't be changed from a later Filter.

      A SAM does have a specific edge over a Filter, and that's the fact that it's ALSO invoked before any other Filter or Servlet. At this point your SAM must have returned SUCCESS already, otherwise the Filter or Servlet from which you called HttpServletRequest#authenticate() could not have been invoked.

      During this initial first call to the SAM you may be able to wrap the response. When the SAM is called the second time (after calling #authenticate) it can recognise that the response has been wrapped and just set the response code and perhaps even commit. The wrapped response response can then be set to ignore further writes to it.

      Depending on the exact structure of your SAM you have one other opportunity to do something and that's in secureResponse, which is called after all other filters have been called. If I'm not mistaken JBoss has a bug here that prevents writing to the response here (this too I've too double check again and make a test for)

      Delete
    2. Thank you for leading me to the right direction.

      I feel stupid admitting this: I wasn't aware that, *regardless* of the second SAM invocation's outcome, #secureResponse still gets called, eventually. Unfortunately, as they say, "common sense is not so common", and I believe that a tiny clarification at the particular section of the spec might be beneficial to novice devs like myself.

      I'm using GlassFish 4.1 and response (un-)wrapping in #validateRequest / #secureResponse works like a charm. Still, I unfortunately haven't found enforcing response content (or absence thereof) and status code, either in the "nested" (i.e. second) #validateRequest invocation, or in #secureResponse, to be trivial to correctly implement.

      Disregarding any "SAM vs Filter-X" precedence "conflicts" which may arise (i.e. "should the SAM honor an HTTP 5xx set by some Filter, when the former wishes to redirect?"), and for which no generic solution can probably exist (except for allowing SAM behavior customization on a case-by-case basis), I found the more troublesome aspect to be the difficulty accessing the *original* response from within the second #validateRequest invocation.

      > When the SAM is called the second time (after calling #authenticate) it can recognise that the response has been wrapped and just set the response code and perhaps even commit. The wrapped response response can then be set to ignore further writes to it.

      I can sadly not expect all hosted apps, depending on the SAM for authentication, to employ the same wrapper implementation. In fact, I cannot even assume that the wrappers are "well-behaved"; e.g. wrapper#sendRedirect might choose to "do nothing"; wrapper#getResponse might just return *this*. Access to the original response (or the initial wrapper established during the first #validateRequest invocation) is unfortunately essential.

      But it's not exactly simple to access that instance either, since, at least on GlassFish, the SAM is instantiated for each request, thus the two #validateRequest invocations occur on disparate instances. It would be a joy if the two at least shared the same MessageInfo, but of course they do not. Some ugly workarounds have crossed my mind for enabling communication between the two SAM instances, but the sole reliable solution there probably is, would be to implement a ServerAuthContext (plus the two further abstraction layers that inevitably accompany it), appropriately reusing the SAM instance, in the particular case.

      The actual question I'd like to ask, is if there's a way to *centrally* make an AuthConfigProvider available to all applications. By "centrally" I mean so as to avoid packaging the four classes with all apps, and/or the need to use a listener to register the provider on behalf of the app. Ironically I can have just that, as long as I stick to using just a plain SAM... I could of course have one app registering the provider on all others' behalf, but it's not quite pretty. I'm aware that the AuthConfigFactory of each AS is expected to offer some sort of persistent provider store but, at least in the case of GlassFish, the documentation on it is as good as nonexistent. Hopefully I won't have to implement an AuthConfigFactory as well, just to be able to programmatically and centrally register AuthConfigProviders... I'd still be struggling long after your work on JSR-375 had been completed :P.

      Delete
  15. Arjan Tijms, how to check login and password?
    I add PasswordValidationCallback, but I'm getting error: Invalid call to login while pluggable authentication method is configured.

    ReplyDelete
    Replies
    1. I implement it as part of the validateRequest method. But I will not use that callback, I just use the CallerPrincipalCallback and GroupPrincipalCallback. The PasswordValidationCallback uses the runtime's password handling mechanism according to JavaDoc, but when I implement JASPIC module I would be doing the check myself on the module

      Delete
  16. ideally JAPIC should have corrected the mistake perpetrated by almost all of the application servers in naming a role as a group. it is surprising how so many experts ignored the possibility/need for a group of users having a same role, or a caller having permission over a group of resources.

    At least in the next version of the spec it should correct this anomaly and introduce three principals :

    CallerPrincipal
    GroupPrincipal -- this is group of users or resources, not role
    RolePrincipal

    I believe this is the fundamental flaw in JEE security design by almost all containers without any exception. people can't and won't change their language to fit the system, rather they change the system.

    ReplyDelete
    Replies
    1. Isn't there a group to role mapping that is done by the application server? There's no "default" specified, it is expected the application server will have their own mapping implementation.

      Delete
  17. forgot to mention : with the introduction of these principals the containers can easily map which are callers, and which are roles( by their terminology groups -- which i propose to change, immediately) and thus we can be freed from the mundane task of writing mapping definitions between JASPIC Roles and container Groups

    ReplyDelete
    Replies
    1. Yes I agree, this would be nice and easier to deal with. Not sure what the impact would be though... what if you had both container managed group-role mapping and application level role mapping what should happen?

      Delete
  18. Arjan, how do you deal with the fact that there're some servers than call JASPIC even when the user has been authenticated in a previous request? How do you check, for example, if the session ID provided by the user on a cookie is'isn't valid?

    Thanks!

    ReplyDelete
    Replies
    1. Hi Alex,

      With the current crop of servers, all of them call JASPIC when a user has been authenticated in a previous request. This is actually what the JASPIC spec requires.

      JASPIC's native mode of operation is stateless; a user is only authenticated for the duration of a single request. If you want a user to be logged-in for the duration of an HTTP session, then you mainly have to manage that yourself -> store the details in the session scope, then every time your SAM is called check if those details are present an if so pass them again on to the container.

      JASPIC does have a feature where it can store those details for you, but it will not automatically apply them. In other words, it's an alternative for HttpSession#setAttribute. It's presumably somewhat more secure for if you happen to don't trust your own code (e.g. if the SAM and application code are developed by different parties that don't 100% trust each other).

      See http://arjan-tijms.omnifaces.org/2013/04/whats-new-in-java-ee-7s-authentication.html#RegisterSession

      Delete
  19. Hi again Arjan!

    In "classic" certificate realm on JEE, you can get the user's certificate by making a call like req.getAttribute("javax.servlet.request.X509Certificate");
    And if you get a not-null certificate, you can be sure the user has been previously authenticated by the container and its truststore with cryptography.
    Obviously, in JASPIC you can also get the caller's certificate with the same API but, since you don't have here any "realm" and/or "login-config", how can the authentification achieved? You can see if the user's cert is in truststore, but how is the SSL handshake done?

    Many thanks for your help!!

    ReplyDelete
    Replies
    1. It would happen in much the same way. A "realm" is just another term for what's more generally called "identity store". A SAM can either delegate to this or perform it's logic itself.

      For further details, see: http://arjan-tijms.omnifaces.org/2015/10/how-servlet-containers-all-implement.html

      Delete
    2. Thanks for your help Arjan. What I still don't understand is: when I read (req.getAttribute...) the certificate attached to the request, did the server previously checked (SSL/TLS handshake) the remote user knows the associated private key?

      Thanks for your help!!

      Delete
    3. I'm going to elaborate a little more (sorry for my poor english):

      I understand the SAM has the responsibility of knowing, in this case, where the accepted certificates are, and of comparing the certificate attached to the request with the accepted certificates to see if someone matches.

      My question is, since that is not enough for ensuring the user is who says he is (as he can attach an arbitrary certiicate), who performs the SSL handshake, and when is it done?

      Many thanks

      Delete
    4. Don't worry about the English, it's perfectly fine ;)

      The answer is really that JASPIC doesn't have anything specific to say about this. It's just an SPI/API. It's called at exactly the same moment when the server would otherwise call a proprietary authentication mechanism/identity store.

      So, the best way to find an answer is to study how existing certificate implementations do this.

      At any length, for JSR 375 we'll be looking at implementing a certificate authentication mechanism and identity store before long. Since the authentication mechanism there is JASPIC based you could use that as an example too.

      Delete
    5. Thanks for your answers Arjan!

      For those with the same kind of problems, here you have a proprietary solution for Glassfish:
      https://blogs.oracle.com/nasradu8/entry/extend_certificaterealm_with_loginmodule_glassfish

      There, the server cryptografically checks the remote client certificate against the truststore, and later you can perform your login logic with a custom login module.

      Not standard, but custom solution. I didn't tried yet with JASPIC over Glassfish, but anyway, as Arjan says, it will be implementation-specific...

      Delete
    6. Hello Arjan!

      I read your article, as well as a couple of related ones. Currently I am trying to solve this problem:


      http://stackoverflow.com/questions/36348377/implement-a-custom-serverauthmodule-for-jboss

      but I am not able to.
      May I ask you for advice?

      Thank you,
      Kind regards: Alex

      Delete
    7. Hi Alex,

      I implemented almost exactly what you're asking for in Soteria (the JSR 375 RI). See here: https://github.com/javaee-security-spec/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/cdi/RememberMeInterceptor.java and https://github.com/javaee-security-spec/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/mechanisms/FormAuthenticationMechanism.java

      Although this is based on an Interceptor and a helper class (HttpMessageContext), it's essentially build on top of JASPIC, so whatever that code is doing is something you could do.

      I do wonder why you specifically use JBoss 7.1.1.Final? This is a very old beta version of JBoss that contained many bugs. Specifically JASPIC barely works in that version. From the top of my head I don't think it was even in a half usable state before at least 7.2.0 or 7.2.1, somewhere along that version.

      For a time I used a patch for JBoss 7.x, see here: https://github.com/javaeekickoff/jboss-as-jaspic-patch That fixed a couple of worst bugs, but even that needed 7.2.x at least if I remember correctly.

      Your best bet would be to use the latest beta of JBoss EAP 7, which is WildFly 10.0.Final.

      Delete
  20. Hi Arjan,
    Thanks for the article, it was helpful and I was able to use JASPIC in payara.
    I am trying to use this same example (custom auth) JASPIC in jetty 9, but I can't seem to make it work.
    Could you help with that ?

    Could you also provide some details about how to use JASPIC in soteria for this same example.

    Thanks.

    ReplyDelete
    Replies
    1. I'm glad you liked the article. Payara is one of the best servers to use for JASPIC.

      I'm regularly testing JASPIC on almost all servers (see e.g. http://arjan-tijms.omnifaces.org/2016/06/the-state-of-portable-authentication-in.html) but Jetty is the one server I didn't test yet. Instead of Jetty you could try Tomcat (8.5 or 9), which also works really well.

      I did ask about Jetty a while back, so maybe the answer given back then is helpful to you: http://stackoverflow.com/questions/14224792/how-to-use-jaspi-jaspic-on-jetty

      (I really have to followup on Jetty and add it to the test set, so thanks for reminding me)

      >Could you also provide some details about how to use JASPIC in soteria for this same example.

      You wouldn't so much use JASPIC -in- Soteria. Instead, Soteria builds on top of JASPIC and it provides a more HTTP specific CDI enabled variant of the ServerAuthModule called the AuthenticationMechanism.

      Soteria is still in active development and not officially released yet. See this for (early) details: http://arjan-tijms.omnifaces.org/p/whats-new-in-java-ee-security-api-10.html#32

      Delete
    2. Hi Arjan,
      Thanks for the prompt reply.

      Our company uses jetty as server and as of now we are not using any standard authentication mechanism. To port the authentication to JASPIC, I need to do a POC for jetty, that's why Jetty is important for my use case.

      About the stack overflow link you mentioned, I tried the github project mentioned in answer, but I can't find a place to plug my custom auth module. I am still trying to make it work and will let you know if it works.

      For the soteria, I'll look into it for using custom auth modules.

      Thanks again.

      Delete
    3. I see, for now it may be best to ask on the Jetty mailing list about this. I'm going to try myself too soon, but that will take some time.

      Do note that Soteria at the moment is only supported on a limited amount of servers, namely Payara, JBoss and TomEE. It should theoretically also work on Tomcat if you add a CDI implementation to it, but I haven't tested that. Same goes for Jetty, it should work if you add CDI, but you just as well need to activate JASPIC then first.

      Since Soteria asks a lot from the server in terms of compatible JASPIC features, my guess would be that it initially would not work on Jetty, but who knows.

      Delete
    4. Hi Arjan,
      Have you found out anything for jaspic in jetty?
      Thanks.

      Delete
  21. Awesome article, and you still update it after all these years(I have seen the Note about Tomcat 8.5.x and 9.x)
    Are you planning to add them to the matrix (server / functionality) comparasion ?

    ReplyDelete
    Replies
    1. Good to hear you like the article :)

      Tomcat will indeed be added to the matrix, the Java EE 7 samples project that's the home of the tests for that has already been updated for Tomcat. Tomcat is a special case since it doesn't need to be tested for the integration with EJB, CDI, JSF, etc. I'm planning to update the matrix when the next version of Liberty is released. Small spoiler ahead of that: Tomcat passes all tests till so far ;)

      Delete
    2. Great news, and thanks for the spoiler :)

      Delete
  22. Hi Arjan.

    This article describe a very interesting topic, I have to confess that I never heard from JASPIC before. I'm developing a Java EE 7 application on WildFly 10.1.0 and currently the authentication mechanism is FORM based linked with a Security Domain at database level. Altough this strategy is running and supporting this important secutiry requirement, I want to adopt JASPIC to have more control and to build our own authentication module. Following the steps described in the article the username and password (credentials) to authenticate are not present, but in my application I have a form with those fields to send the values and a rule in the web application descriptor to map the form-login and form-error pages. How can I adapt my application to work with JASPIC using those defined form rules and identifying the scenarios in which the validateRequest method don't have to review the user an password to authenticate if a valid process was runned before?

    ReplyDelete
  23. Hi Arjan,

    In one of your updates you say "WebLogic doesn't require the mandatory role mapping anymore." I don't think this is the case. I'm trying to work with WebLogic 12.2.1.2.0. So far the only way I got things working is to have my roles defined in web.xml and a mapping defined in weblogic.xml too. Furthermore, the value for in web.xml, and the values for and in weblogic.xml all had to be the same value. I tried doing a mapping where I gave in weblogic.xml a different value and set that value in GroupPrincipalCallback, but so far I've not has success with WebLogic doing this mapping. WebLogic also doesn't seem to like @DeclareRoles and prefers everything in web.xml otherwise you get deployment errors.

    ReplyDelete
    Replies
    1. >In one of your updates you say "WebLogic doesn't require the mandatory role mapping anymore." I don't think this is the case.

      It can be confusing under which circumstances this is exactly the case, but it really should work. See https://github.com/javaee-samples/javaee7-samples/tree/master/jaspic/basic-authentication/src/main/webapp/WEB-INF

      There's no weblogic.xml file, and that test passed on WebLogic.

      Note that for Java EE 8 default group to role mapping is mandatory when there is no container specific configuration .

      Delete
    2. I'll take a look at this. It looks like I'm doing essentially the same thing so I'll see if I can find the difference.

      Delete
    3. Hi Arjan. I've been able to take a look at basic-authentication and I got it working on WebLogic 12.2.1. I was able to get it working both with and without weblogic.xml. I also was able to successfully use the weblogic.xml `security-role-assignment` to provide a mapping between application-specific roles and whatever groups an identity-management solution might assign to the user. So that is all good.

      However, WebLogic 12.2.1 seems to have a hard time with remembering groups after logging in. I updated "doLogin" with map.put("javax.servlet.http.registerSession", TRUE.toString()); to remember the session. That works OK, and a new session is created. But on subsequent requests if I rebuild the callback with the existing Principal like this:


      if (principal != null) {
      callback = new Callback[]{
      new CallerPrincipalCallback(clientSubject, principal)
      };
      }

      I lose all the group information. I can print the Principal name successfully, but isUserInRole('architect') will return false again.

      Since rebuilding the callback from the existing Principal wasn't working quite right, I also decided to try "do nothing" like this:

      callback = new Callback[] { new CallerPrincipalCallback(clientSubject, (Principal) null) };

      I get even stranger behavior with this. I can print the principal name. And programmatically isUserInRole('architect') will return true. However, declaratively if I attempt to go to an 'architect' protected page I'll get 403 forbidden.

      The only thing that seems to work is the trick of storing the username and groups in session variables. So as part of "doLogin" I store 2 session variables with the principal name and list of groups. I then ignore the existing Principal on subsequent request, preferring to rebuild the callback using the 2 stored session variables. WebLogic 12.2.1 was happier with this. From previous testing, I know GlassFish and Payara are also good with this.

      Is using stored session variables vs. using an existing Principal the safest way to rebuild the callback values across EE servers?

      Delete
  24. Looks like WebLogic 12.2.1.0 doesn't propagate a custom Principal object either.

    ReplyDelete
    Replies
    1. Indeed, the WLS Security team has strong opinions about the necessity for custom principals and indeed does not support them.

      See this matrix where I last tested WebLogic 12.2.1.2 and the tests with custom principals indeed fail: http://arjan-tijms.omnifaces.org/2016/12/the-state-of-portable-authentication-in.html

      After quite a huge discussion we have reached a kind of compromise support for custom principals in JSR 375, since the spec lead (Will) is from WebLogic and explained that WebLogic cannot really support custom principals in the way that all other application servers can.

      Delete
  25. Hi Arjan,

    I tried to test the example code from this blog, but I have problems with Websphere. I crossposted it on SO: https://stackoverflow.com/questions/46359348/why-doesnt-websphere-work-with-my-jaspi-login-module

    JASPI worked quickly with Wildfly. At that point, I just implemented the ServerAuthModule interface and configured that module in the server configuration, and everything was fine. Note that the auth module class was just part of my application.

    I couldn't make it work in Websphere, this time, as far as I know, implementing exactlywhat was found on this blog. As soon as my app was deployed, I had those problems:

    * Authentication is completely ignored in my app, though it's specified as required for all resources in web.xml . The login module is not invoked.
    * Now the strangest part: Websphere's own admin console fails with a 403 Forbidden status code. I can somehow force some parts of it to display when forcing the right username in my authentication module! Every request to the console triggers breakpoints in my login module.
    Deploying using wsadmin command-line console or even from java instead of the administration console doesn't seem to change that (as expected, but I've seen weird things in Websphere in that aspect).

    Enabling JASPI and application security in Websphere doesn't change anything.

    Anything that I could correct? Do you know of any sample using JASPI that works on Websphere?

    ReplyDelete
  26. Hi Arjun,
    In Wildfly utilizing Soteria’s JSR375 RI is there a way to specify the security domain to use via @SecurityDomain instead of having to specify it in the jboss-web.xml?
    Thanks!
    -am

    ReplyDelete
    Replies
    1. I don't know of any @SecurityDomain. The WildFly people have promised for years that it should "just work" once Elytron is there. Elytron is in WildFly 12, but I haven't tested it.

      For WildFly 10 and 11 it's still jboss-web.xml for sure. See here: http://arjan-tijms.omnifaces.org/2015/08/activating-jaspic-in-jboss-wildfly.html

      Delete
  27. I gone through you articles which is outstanding.

    https://stackoverflow.com/questions/70769617/how-to-configure-google-oauth-and-form-auth-in-tom-cat-to-work-together-in-the-s

    i though u could help me with this. it would be a great help to me.

    ReplyDelete

Post a Comment

Popular posts from this blog

Jakarta EE Survey 2022

Counting the rows returned from a JPA query