Wildfly 39 JSF Login authentication

4 days ago 7
ARTICLE AD BOX

I'm trying to configure a form-based authentication for my JSF app in Wildfly 39, and I've come across these sources:

https://rieckpil.de/howto-simple-form-based-authentication-for-jsf-2-3-with-java-ee-8-security-api/ https://www.baeldung.com/java-ee-8-security

I'm trying to go with the approach of implementing IdentityStore in code and configuring @CustomFormAuthenticationMechanismDefinition with my JSF form calling securityContext.authenticate(...), following rieckpil's tutorial.

I tried to configure the Undertow's subsystem according to the docs:

standalone.xml <application-security-domains> <application-security-domain name="other" security-domain="ApplicationDomain" enable-jacc="true" integrated-jaspi="false"/> </application-security-domains>

And reference it in the jboss-wex.xml:

<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.com/xml/ns/javaee/jboss-web_10_0.xsd" version="10.0"> <context-root>/</context-root> <security-domain>other</security-domain> </jboss-web>

I've implemented a custom CallerPrincipal implementation that holds my user data, and I'm expecting that from the securityContext, but the following behaviour actually happened: Whenever I try to login the first time after running the app, it returns the SEND_CONTINUE status everytime, even if it fails. When it fails, however, the server protects the /app/* resources, and only permits access when I do login with valid input. Only after I logout and then try to do the same steps again, it does return the SUCESS or SEND_FAILURE.

Is this expected? Are there nuances I've ignored so far?

Here's the code:

<!DOCTYPE html> <html lang="pt_BR" xmlns="http://www.w3.org/1999/xhtml" xmlns:h="jakarta.faces.html" xmlns:p="http://primefaces.org/ui" xmlns:f="jakarta.faces.core" xmlns:ui="jakarta.faces.facelets"> <h:head> (...) </h:head> <h:body> <h:form id="loginForm"> <p:panel header="Login"> <p:messages id="messages" globalOnly="true" /> <p:outputLabel for="@next" value="Username" /> <p:inputText value="#{loginBean.username}" required="true" /> <p:outputLabel for="@next" value="Password" /> <p:password value="#{loginBean.password}" required="true" /> <p:commandButton value="Login" action="#{loginBean.login}" ajax="false" update="messages" /> </p:panel> </h:form> </h:body> </html> // LoginBean @Named @RequestScoped public class LoginBean { @Inject SecurityContext securityContext; @Inject FacesContext facesContext; private String username; // getter setter private String password; // getter setter public void login() { var status = securityContext.authenticate( JsfUtil.request(), JsfUtil.response(), AuthenticationParameters.withParams().credential( new UsernamePasswordCredential(username, password) ) ); if (status == SEND_CONTINUE) { facesContext.responseComplete(); } else if (status == SUCCESS) { JsfUtil.redirect("app/home.xhtml"); } else if (status == SEND_FAILURE) { errorMessage("Login failed", "Invalid credentials"); } } } //IdentityStore @ApplicationScoped public class UserIdentityStore implements IdentityStore { @EJB UserService userService; @Override public CredentialValidationResult validate(Credential credential) { if (credential instanceof UsernamePasswordCredential upc) { var u = userService.findUserLogin(upc.getCaller(), upc.getPasswordAsString()); if (u != null) { return new CredentialValidationResult(new UserCallerPrincipal(u), Set.of("USER")); } return CredentialValidationResult.INVALID_RESULT; } return CredentialValidationResult.NOT_VALIDATED_RESULT; } } web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0"> <!-- Faces Servlet ... --> <security-constraint> <web-resource-collection> <web-resource-name>App</web-resource-name> <url-pattern>/app/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>USER</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>USER</role-name> </security-role> </web-app>
Read Entire Article