3

I'm used to using WildFly for Java/Jakarta EE development and lately I wanted to update a project that is using JAAS for authentication/authorization to the new Jakarta Security API from Jakarta EE 9.1.

I couldn't make it work, so I decided to create the simplest example possible and experiment on different application servers. I took an example from Soteria (as I understand, the reference implementation for Jakarta Security) and created a new Jakarta EE project to deploy on my application servers. The code is available here.

The example includes a very simple identity store for a user reza with password secret1:

@ApplicationScoped
public class TestIdentityStore implements IdentityStore {
    public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {

        if (usernamePasswordCredential.compareTo("reza", "secret1")) {
            return new CredentialValidationResult("reza", new HashSet<>(asList("foo", "bar")));
        }

        return INVALID_RESULT;
    }
}

A servlet contains the custom form authentication mechanism definition that redirects the user to /login.jsf if she's not authenticated. When authenticated, it displays the user name (principal name) and tests the user roles. There's also a logout feature:

@CustomFormAuthenticationMechanismDefinition(
    loginToContinue = @LoginToContinue(
        loginPage="/login.jsf",
        errorPage="" // DRAFT API - must be set to empty for now
    )
)

@WebServlet("/servlet")
@DeclareRoles({ "foo", "bar", "kaz" })
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
public class Servlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String webName = null;
        if (request.getUserPrincipal() != null) {
            webName = request.getUserPrincipal().getName();
        }
        
        response.getWriter().write(
                "<html><body> This is a servlet <br><br>\n" +
        
                    "web username: " + webName + "<br><br>\n" +
                            
                    "web user has role \"foo\": " + request.isUserInRole("foo") + "<br>\n" +
                    "web user has role \"bar\": " + request.isUserInRole("bar") + "<br>\n" +
                    "web user has role \"kaz\": " + request.isUserInRole("kaz") + "<br><br>\n" + 

                        
                    "<form method=\"POST\">" +
                        "<input type=\"hidden\" name=\"logout\" value=\"true\"  >" +
                        "<input type=\"submit\" value=\"Logout\">" +
                    "</form>" +
                "</body></html>");
    }
    
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if ("true".equals(request.getParameter("logout"))) {
            request.logout();
            request.getSession().invalidate();
        }
        
        doGet(request, response);
    }
}

The login.xhtml page is rendered if the user is redirected to /login.jsf:

<!DOCTYPE html>
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:jsf="http://xmlns.jcp.org/jsf">

    <h:head/>
    
    <h:messages />
    
    <body>
        <p>
            Login to continue
        </p>
    
         <form jsf:id="form">
            <p>
                <strong>Username </strong> 
                <input jsf:id="username" type="text" jsf:value="#{loginBacking.username}" />
            </p>
            <p>
                <strong>Password </strong> 
                <input jsf:id="password" type="password" jsf:value="#{loginBacking.password}" />
            </p>
            <p>
                <input type="submit" value="Login" jsf:action="#{loginBacking.login}" />
            </p>
        </form>
    </body>
</html>

Finally, a JSF backing bean is responsible for dealing with the form and calling the Jakarta Securty API to perform authentication:

@Named
@RequestScoped
public class LoginBacking {
    @Inject
    private SecurityContext securityContext;

    @NotNull
    @Size(min = 3, max = 15, message="Username must be between 3 and 15 characters")
    private String username;
    
    @NotNull
    @Size(min = 5, max = 50, message="Password must be between 5 and 50 characters")
    private String password;
    
    public void login() {
        FacesContext context = FacesContext.getCurrentInstance();
        Credential credential = new UsernamePasswordCredential(username, new Password(password));
        
        AuthenticationStatus status = securityContext.authenticate(
            getRequest(context),
            getResponse(context), 
            withParams().credential(credential));
        
        if (status.equals(SEND_CONTINUE)) {
            // Authentication mechanism has send a redirect, should not
            // send anything to response from JSF now.
            context.responseComplete();
        } else if (status.equals(SEND_FAILURE)) {
            addError(context, "Authentication failed");
        }
        
    }
    
    private static HttpServletResponse getResponse(FacesContext context) {
        return (HttpServletResponse) context.getExternalContext().getResponse();
    }
    
    private static HttpServletRequest getRequest(FacesContext context) {
        return (HttpServletRequest) context.getExternalContext().getRequest();
    }
    
    private static void addError(FacesContext context, String message) {
        context.addMessage(null, new FacesMessage(SEVERITY_ERROR, message, null));
    }

    /* Getters and setters. */
}

As I said, it works as expected on Payara, but on WildFly I get the following exception:

ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /jakarta-security-example/login.jsf: jakarta.servlet.ServletException: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:699)
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:437)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at io.opentracing.contrib.opentracing-jaxrs2//io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:52)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at org.wildfly.security.elytron-web.undertow-server@1.10.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
    at org.wildfly.security.elytron-base@1.17.1.Final//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
    at org.wildfly.security.elytron-web.undertow-server@1.10.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59)
    at io.undertow.core@2.2.12.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
    at io.undertow.core@2.2.12.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:280)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:79)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:134)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:131)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
    at org.wildfly.extension.undertow@25.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:260)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:79)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:100)
    at io.undertow.core@2.2.12.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
    at io.undertow.core@2.2.12.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:852)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at org.jboss.xnio@3.8.4.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: jakarta.faces.el.EvaluationException: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:77)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:75)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:64)
    at jakarta.faces.api//jakarta.faces.component.UICommand.broadcast(UICommand.java:213)
    at jakarta.faces.api//jakarta.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:852)
    at jakarta.faces.api//jakarta.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1329)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:56)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:159)
    at jakarta.faces.api//jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:681)
    ... 52 more
Caused by: java.lang.IllegalStateException: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.handleCallbacks(Jaspic.java:184)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.notifyContainerAboutLogin(Jaspic.java:157)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.HttpMessageContextImpl.notifyContainerAboutLogin(HttpMessageContextImpl.java:261)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.HttpMessageContextImpl.notifyContainerAboutLogin(HttpMessageContextImpl.java:239)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism.validateRequest(CustomFormAuthenticationMechanism.java:53)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism$Proxy$_$$_WeldSubclass.validateRequest$$super(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:51)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.processContainerInitiatedAuthentication(LoginToContinueInterceptor.java:182)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.validateRequest(LoginToContinueInterceptor.java:98)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.LoginToContinueInterceptor.intercept(LoginToContinueInterceptor.java:76)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.NonTerminalAroundInvokeInvocationContext.proceedInternal(NonTerminalAroundInvokeInvocationContext.java:66)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.cdi.AutoApplySessionInterceptor.intercept(AutoApplySessionInterceptor.java:65)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.CustomFormAuthenticationMechanism$Proxy$_$$_WeldSubclass.validateRequest(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
    at deployment.jakarta-security-example.war//jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism$1347370719$Proxy$_$$_WeldClientProxy.validateRequest(Unknown Source)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.HttpBridgeServerAuthModule.validateRequest(HttpBridgeServerAuthModule.java:90)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.DefaultServerAuthContext.validateRequest(DefaultServerAuthContext.java:53)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:182)
    at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.0.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:99)
    at io.undertow.servlet@2.2.12.Final//io.undertow.servlet.spec.HttpServletRequestImpl.authenticate(HttpServletRequestImpl.java:475)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.authenticate(Jaspic.java:91)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.SecurityContextImpl.authenticate(SecurityContextImpl.java:82)
    at deployment.jakarta-security-example.war//org.glassfish.soteria.test.LoginBacking.login(LoginBacking.java:59)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:152)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.parser.AstValue.invoke(AstValue.java:261)
    at org.glassfish.jakarta.el@4.0.0//com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:237)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
    at org.jboss.weld.core@4.0.2.Final//org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:70)
    at com.sun.jsf-impl@3.0.0.SP04//com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:67)
    ... 61 more
Caused by: java.io.IOException: java.io.IOException: ELY01177: Authorization failed.
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext$1.handle(JaspiAuthenticationContext.java:112)
    at org.glassfish.soteria@1.0.1-jbossorg-1//org.glassfish.soteria.mechanisms.jaspic.Jaspic.handleCallbacks(Jaspic.java:178)
    ... 122 more
Caused by: java.io.IOException: ELY01177: Authorization failed.
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext$1.handleOne(JaspiAuthenticationContext.java:189)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext$1.lambda$handle$0(JaspiAuthenticationContext.java:101)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.SecurityActions.doPrivileged(SecurityActions.java:39)
    at org.wildfly.security.jakarta.authentication@2.0.0.Beta1//org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext$1.handle(JaspiAuthenticationContext.java:100)
    ... 123 more

Note that my project doesn't contain the jboss-web.xml file from Soteria's example, but If I add it the authentication fails every time (even if I input the correct username and password) when using the login page. If I open http://localhost:8080/jakarta-security-example/servlet directly I get:

This is a servlet

web username: null

web user has role "foo": false
web user has role "bar": false
web user has role "kaz": false

And if I click the logout button I get: jakarta.servlet.ServletException: UT010062: No SecurityContext available.

Tried to find answers on Google, but couldn't, so I'm hoping someone here in Stack Overflow might know what's wrong and help me. Thanks!

Vítor E. Silva Souza
  • 1,575
  • 1
  • 18
  • 23

1 Answers1

7

Solution

The WildFly server needs additional configuration:

  1. Edit the other Application Security Domain, enter image description here set the Integrated JASPI property to off. enter image description here

  2. Reload the server enter image description here

There is also a script for this on: https://github.com/wildfly/quickstart/tree/main/ee-security#configure-the-server

Why? What is Integrated JASPI?

From https://docs.wildfly.org/25/WildFly_Elytron_Security.html#Elytron_and_Java_EE_Security

The EE Security API is built on JASPI. Within JASPI we support two different modes of operation 'integrated', and 'non-integrated'. In integrated mode any identity being established during authentication is expected to exist in the associated security domain. With the EE Security APIs however it is quite likely an alternative store will be in use so configuration the mapping to use 'non-integrated' JASPI allows for identities to be dynamically created as required.

Marcos Zolnowski
  • 2,751
  • 1
  • 24
  • 29
  • 1
    Thanks! Worked perfectly. Updated the [repository](https://github.com/vitorsouza/jakarta-security-example) with instructions to do what you said before deploying on WildFly. Do you mind explaining why this is needed? What is Integrated JASPI and why turning it off makes the example work, please? – Vítor E. Silva Souza Dec 06 '21 at 18:43
  • 1
    Thanks for editing your response and for pointing out the answer in the docs. I should take some time to properly read through the WildFly documentation as I usually just skim for answers to problems and that clearly didn’t work. :) – Vítor E. Silva Souza Dec 06 '21 at 23:31
  • 2
    Well, I found this question interesting. How something that should work out-of-the-box, isn't? I am sure you are not the first, nor the last person with that issue. – Marcos Zolnowski Dec 07 '21 at 04:45
  • 4
    WildFly actually seems to violate the specs here, or at least the intend which I had when writing the specs; that is that security should work without any additional vendor specific configuration. – Arjan Tijms Dec 20 '21 at 23:15
  • 1
    Sir Arjan Tijms , does this means they still get Jakarta certificated by doing this? – Marcos Zolnowski Dec 23 '21 at 00:24
  • 1
    Yes, they are still certified. It's not super easy to add a test in the TCK to ensure security works "out of the box", as obviously we (as the TCK writers) don't deliver that box. Certification is also done by self-certifying based on passing tests. I did speak to Darran (responsible for security at Red Hat) about this, and I'll try to clarify the specs for Security 3.0 here if possible and accepted. – Arjan Tijms Mar 23 '22 at 22:30