wildflyjaspic

How to setup an application level SAM in WildFly


I previously had some code working on Glassfish, but I want to port it to WildFly.

However, I cannot seem get the module to be invoked by WildFly. The ServletContextListener initializes the module as follows

AuthConfigFactory.getFactory()
            .registerConfigProvider(new OpenIdConnectModuleConfigProvider(options, null),
             "HttpServlet", getAppContext(sce), null);

"HttpServlet" is not Glassfish specific and appears to be referenced in https://github.com/wildfly/wildfly/blob/master/undertow/src/main/java/org/wildfly/extension/undertow/security/jaspi/JASPIAuthenticationMechanism.java?source=cc

Glassfish does not require a <logon-config> block on the web.xml and putting any variant in WildFly does not work (as expected)

The other place I am suspecting is how I calculate the application context identifier. For Glassfish I had

private String getAppContext(final ServletContextEvent sce) {

    return sce.getServletContext()
        .getVirtualServerName() + " "
            + sce.getServletContext()
                .getContextPath();
}

Could it be different in WildFly? Though I saw the similar code in https://github.com/rdebusscher/secSpikeWeb/blob/master/src/main/java/org/omnifaces/security/jaspic/core/Jaspic.java#L300 as well

I have also tried adding to standalone.xml this block

<security-domain name="jaspi" cache-type="default">
  <authentication-jaspi>
    <login-module-stack name="dummy">
      <login-module code="Dummy" flag="optional"/>
    </login-module-stack>
    <auth-module code="org.wildfly.extension.undertow.security.jaspi.modules.HTTPSchemeServerAuthModule" flag="required"/>
  </authentication-jaspi>
</security-domain>

And set <default-security-domain value="jaspi"/>

However it had no effect and putting a breakpoint in the module didn't show that it gets hit either.

In addition, there I couldn't find a to be a way of doing the following, in WildFly like I would in glassfish-web.xml but that can be another question

<security-role-mapping>
    <role-name>users</role-name>
    <group-name>https://helloworld</group-name>
</security-role-mapping>

The code is pretty big, but the gist of it can be found in

https://github.com/trajano/openid-connect/tree/openid-connect-1.0.1/openid-connect-jaspic-module

and

https://github.com/trajano/openid-connect/tree/openid-connect-1.0.1/openid-connect-jaspic-sample

Note I am looking for it on the application level and not set a global server JASPI.


Solution

  • "HttpServlet" is not Glassfish specific

    That's correct, AFAIK this is a standard identifier to say for which subsystem in Java EE the auth module is going to be registered. But there's only one other valid value and that's something with "soap" in it (not sure).

    Could it be different in WildFly?

    No, it's a standard way.

    And set <default-security-domain value="jaspi"/>

    The recommended way should be this in standalone.xml:

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

    And then putting the following in WEB-INF/jboss-web.xml:

    <jboss-web>
        <security-domain>jaspitest</security-domain>
    </jboss-web>
    

    This should be enough. It's what I use on WildFly 8.2 and 9.0, and it's what the Java EE samples project uses. But setting the default domain should also really work as you did, and you activation code is close enough too, so I'm not sure if the above will make a difference in your case.

    Alternatively there's a JBoss specific programmatic way to activate JASPIC:

       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) {
                    throw new RuntimeException(e);
                }
            }
    
            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));
    

    You need to execute that from a io.undertow.servlet.ServletExtension

    It's a shame that JBoss requires JASPIC to be activated. But one of the above shown methods should really work. I just verified them on a stock WildFly 9.0 and it worked there. The validateRequest method of a testing SAM is called correctly.