Activating JASPIC in JBoss WildFly

JBoss WildFly has a rather good implementation of JASPIC, the Java EE standard API to build authentication modules.

Unfortunately there's one big hurdle for using JASPIC on JBoss WildFly; it has to be activated. This activation is somewhat of a hack itself, and is done by putting the following XML in a file called standalone.xml that resides with the installed server:

<security-domain name="jaspitest" cache-type="default">
    <authentication-jaspi>
        <login-module-stack name="dummy">
            <login-module code="Dummy" flag="optional"/>
        </login-module-stack>
        <auth-module code="Dummy"/>
    </authentication-jaspi>
</security-domain>

UPDATE Dec 23, 2015: Since WildFly 10rc5 the "jaspitest" domain is included by default in standalone.xml and no longer has to be added manually. For JBoss EAP 7 beta1 and earlier this is still needed as it just predates the moment this domain was added.

Subsequently in the application a file called WEB-INF/jboss-web.xml needs to be created that references this (dummy) domain:

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

UPDATE Dec 23, 2015: For WildFly 10rc5 and later the below is not longer relevant. Only the jboss-web.xml file as shown above has to be added to activate JASPIC

While this works it requires the installed server to be modified. For a universal Java EE application that has to run on multiple servers this is a troublesome requirement. While not difficult, it's something that's frequently forgotten and can take weeks if not months to resolve. And when it finally is resolved the entire process of getting someone to add the above XML fragment may have to be repeated all over again when a new version of JBoss is installed.

Clearly having to activate JASPIC using a server configuration file is less than ideal. The best solution would be to not require any kind of activation at all (like is the case for e.g. GlassFish, Geronimo and WebLogic). But this is currently not implemented for JBoss WildFly.

The next best thing is doing this activation from within the application. As it appears this is indeed possible using some reflective magic and the usage of JBoss (Undertow) internal APIs. Here's where the OmniSecurity JASPIC Undertow project comes in. With this project JASPIC can be activated by putting the following in the pom.xml of a Maven project:

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces-security-jaspic-undertow</artifactId>
    <version>1.0</version>
</dependency>

The above causes JBoss WildFly/Undertow to load an extension that uses a number of internal APIs. It's not entirely clear why, but some of those are directly available, while other ones have to be declared as available. Luckily this can be done from within the application as well by creating a META-INF/jboss-deployment-structure.xml file with the following content:

<?xml version='1.0' encoding='UTF-8'?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <dependencies>
            <module name="org.wildfly.extension.undertow" services="export" export="true" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

So how does the extension exactly work?

The most important code consists out of two parts. A reflective part to retrieve what JBoss calls the "security domain" (the default is "other") and another part that uses the Undertow internal APIs to activate JASPIC. This is basically the same code Undertow would execute if the dummy domain is put in standalone.xml.

For completeness, the reflective part to retrieve the domain is:

String securityDomain = "other";

IdentityManager identityManager = deploymentInfo.getIdentityManager();
if (identityManager instanceof JAASIdentityManagerImpl) {
 try {
  Field securityDomainContextField = JAASIdentityManagerImpl.class.getDeclaredField("securityDomainContext");
  securityDomainContextField.setAccessible(true);
  SecurityDomainContext securityDomainContext = (SecurityDomainContext) securityDomainContextField.get(identityManager);

  securityDomain = securityDomainContext.getAuthenticationManager().getSecurityDomain();

 } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
  logger.log(Level.SEVERE, "Can't obtain name of security domain, using 'other' now", e);
 }
}

The part that uses Undertow APIs to activate JASPIC is:

ApplicationPolicy applicationPolicy = new ApplicationPolicy(securityDomain);
JASPIAuthenticationInfo authenticationInfo = new JASPIAuthenticationInfo(securityDomain);
applicationPolicy.setAuthenticationInfo(authenticationInfo);
SecurityConfiguration.addApplicationPolicy(applicationPolicy);

deploymentInfo.setJaspiAuthenticationMechanism(new JASPIAuthenticationMechanism(securityDomain, null));
deploymentInfo.setSecurityContextFactory(new JASPICSecurityContextFactory(securityDomain));
The full source can be found on GitHub.

Conclusion

For JBoss WildFly it's needed to activate JASPIC. There are two hacks available to do this. One requires a modification to standalone.xml and a jboss-web.xml, while the other requires a jar on the classpath of the application and a jboss-deployment-structure.xml file.

It would be best if such activation was not required at all. Hopefully this will indeed be the case in a future JBoss.

Arjan Tijms

Comments

  1. Hi, Arjan.
    Can you help me?

    I'm trying to @Inject some @Stateless AccountService to your TestServerAuthModule (from javaee7-samples). I want to do something like this: accountService.find(..) in TestServerAuthModule#validateRequest.

    But service is always null.

    How can I do this ?

    ReplyDelete
    Replies
    1. >But service is always null.

      That's 'correct'. You can only use @Inject in things that are themselves managed by CDI. A ServerAuthModule is not managed by CDI.

      What you need to do instead in JBoss/WildFly is a programmatic lookup inside the validateRequest method via CDI.current().select(AccountService.class).get(), or alternatively using new InitialContext().lookup(...), where ... is the JNDI name of your AccountService.

      Delete
  2. I keep winding up back on pages written by you when I'm looking for what I'm doing, so I'm hoping you might know the answer. I've brought up JASPIC under Wildfly but I'm running into an error in that it's not finding the class specified that implements ServerAuth. The error that keeps coming up is: java.lang.IllegalStateException: PBOX00050: No ServerAuthModule configured to support type class io.undertow.servlet.spec.HttpServletRequestImp

    Any chance you've seen this before or can point me in a direction that might be helpful?

    ReplyDelete
  3. Hi Arjan,
    Is there any way to activate JASPIC on WildFly 8?
    Due to JDK limitations of the code, I cannot use WildFly 10...When I built the code, it gives me the error-
    java.lang.LinkageError: Failed to link org/omnifaces/security/UndertowJaspicHandlerExtension (Module "deployment.dv-cdos.ear:main" from Service Module Loader)
    Any help will be appreciated!

    Many Thanks, Anand Joshi

    ReplyDelete
    Replies
    1. Wildfly 8 is a bit of an early alpha of EAP 7, and JASPIC wasn't really fully implemented there, so its not really recommended.

      Jaspic can be activated, but just by using the security-domain as given in standalone.xml and the jboss-web as given in jboss-web.xml. If you really want programmatic, you can look at an older revision of https://github.com/omnifaces/omnisecurity-jaspic-undertow which I think supported WildFly 8.

      Best would be to overcome your JDK limitation and use WildFly 10. WildFly 8 is an alpha version. Good luck!

      Delete

Post a Comment

Popular posts from this blog

Implementing container authentication in Java EE with JASPIC

What’s new in Jakarta Security 3?

Jakarta EE Survey 2022