TL;DR: In this article, you will learn how to use Java EE (Enterprise Edition) technologies, such as JavaServer Faces (JSF), to build robust web applications. You can find the complete source code developed throughout this article in this GitHub repository.


What You Will Build

To see JavaServer Faces and Java EE in action, in this article, you will build a simple microblog web application. Visitor of your application, without having to authenticate themselves, will be able to see every publication that your users add. However, to share their thoughts through the app, first, visitors will have to authenticate themselves through Auth0. The goal here is to have a sample application that is both complete (in the sense of having public and protected views) and easy to grasp (without complex and long source code).

Why Use Auth0?

Auth0, the global leader in Identity-as-a-Service (IDaaS), provides thousands of enterprise customers with a Universal Identity Platform for their web, mobile, IoT, and internal applications. Along with other features, Auth0 allows you to integrate into your apps features like:

How the Integration with Auth0 Works?

Auth0 is all about open standards. Whenever you are using Auth0 to secure your application, you can rest assured that you are using battle-tested, state-of-the-art solutions that rely on standards like OAuth 2.0, OpenID Connect, and SAML. In fact, in this article, you will secure your JavaServer Faces and Java EE application with OAuth 2.0 and OpenID Connect.

Note: If you are not acquainted with these standards, don't worry. Auth0 facilitates using both standards through mature, open-source libraries. Also, you can always learn more about these topics in our documents.

Basically speaking, when visitors want to authenticate themselves, your app will redirect them to the Auth0 Login Page. There, visitors will be able to choose an authentication method that you have configured in your account (e.g., through a social IdP like Google) and, when they choose, Auth0 will validate their account and will redirect your users back to your web application. At that moment, your web application will get an authorization code that it will use to get an ID Token (i.e., a token that contains user profile attributes represented in the form of claims).

Note: The flow briefly explained above refers to web applications only. If you were developing another application type (like a mobile app or a single-page app (SPA)), you would use another flow.

Sounds complex? Fear not! Most of the flow explained above is covered by the libraries that you will use. As such, you will be able to focus on what matters in your app, its special features.

Prerequisites

To follow this article along, you will need four things:

Having these, you are good to go and can start creating your JavaServer Faces and Java EE web application.

Scaffolding a Java EE Web App

To manage your application's source code and to build a runnable file from it, you will use Maven. Maven is a software project management tool that relies on an XML file called pom.xml to run tasks like compiling and packaging.

Therefore, to start scaffolding your application, you will:

  1. create a new directory to hold all the source code of your application;
  2. create the project structure (i.e., directories and Java packages);
  3. and create and define a starting pom.xml file that you will use with Maven.

To do this, open a terminal, and issue the following commands:

# create a directory for your project
mkdir jsf-javaee-microblog

# move into this new directory
cd jsf-javaee-microblog

# create a Java package for your app
mkdir -p ./src/main/java/com/auth0/microblog

# create a directory for JSF-related files
mkdir -p ./src/main/webapp/WEB-INF

# create the pom.xml file
touch pom.xml

If you have previous experience developing with Java, you will probably remember that there are many ways (way too many) to develop and to deploy your applications. For example, back in the day, the most popular approach among Java developers was to create a .war (Web ARchive) file that they could (theoretically) deploy on any application server. Then, they would have to choose and configure an application server (e.g., JBoss, Wildfly, WebLogic, etc.) to serve their web application.

Although this approach is still feasible, nowadays, a much better (and more popular) approach is to create a fat JAR (Java ARchive) file that encapsulates your source code alongside the application server that serves it. Then, to run this app, instead of installing and configuring an external application server, you can simply execute the web app in a single command.

Although this approach facilitates the process, you still need to choose an application server to run your app. In this article, you will use Thorntail, a lightweight application server developed by RedHat. What is cool about Thorntail is that you can activate Java EE features such as JAX-RS (for RESTful APIs), CDI (for Contexts and Dependency Injection), and JSF just by declaring a single dependency (called a Fraction).

Now that you have a basic understanding about how you will structure your application and about the application server that you will use to run it, you can open the pom.xml file (with any text editor) and add the following code to it:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.auth0</groupId>
  <artifactId>microblog</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>JavaServer Face and Java EE Microblog</name>

  <properties>
    <version.thorntail>2.2.1.Final</version.thorntail>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.thorntail</groupId>
        <artifactId>bom</artifactId>
        <version>${version.thorntail}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.thorntail</groupId>
      <artifactId>jaxrs</artifactId>
    </dependency>
  </dependencies>

  <build>
    <finalName>jsf-javaee-microblog</finalName>
    <plugins>
      <plugin>
        <groupId>io.thorntail</groupId>
        <artifactId>thorntail-maven-plugin</artifactId>
        <version>${version.thorntail}</version>
        <executions>
          <execution>
            <goals>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

With that in place, you can save and close this file. Then, you can import your project in your preferred IDE (all of them have means to import Maven projects) and create a new class called MicroblogApplication inside the com.auth0.microblog package. Inside this class, you will add the following code:

// ./src/main/java/com/auth0/microblog/MicroblogApplication.java
package com.auth0.microblog;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;

@ApplicationPath("/")
@Path("/message")
public class MicroblogApplication extends Application {
  @GET
  @Produces("text/plain")
  public Response doGet() {
    return Response.ok("Hello from Thorntail!").build();
  }
}

What this class is doing is to define a new REST endpoint that simply returns a message saying "Hello from Thorntail!" when users issue HTTP GET request to the /message path.

Now, to run this app, go back to your terminal and issue the following commands:

# use Maven wrapper
mvn -N io.takari:maven:wrapper -Dmaven=3.5.4

# use Maven to package the application in a fat JAR file
./mvnw clean package

# run the fat JAR artifact
java -jar target/jsf-javaee-microblog-thorntail.jar

Note: The first command will add Maven Wrapper to your project. This is both needed (the latest release, v3.6.0, causes an error while trying to build the project) and a good practice. To learn more, check out this resource.

In one or two minutes, after Thorntail finishes bootstrapping (you will see a message saying THORN99999: Thorntail is Ready in your terminal), you can either open http://localhost:8080/message in a web browser, or use HTTP clients like curl to issue a request to your endpoint:

curl http://localhost:8080/message
# response: Hello from Thorntail!%

Bootstrapping a Java EE web application with Thorntail.

Note: You will not develop any other RESTful endpoints from now on. The idea here is to have a simple way to test your application structure for the first time.

Configuring JavaServer Faces in Your Web App

Having confirmed that you are able to run your Java EE application on Thorntail, the next thing you will do is to configure JavaServer Faces in your project. To do so, you will start by opening the pom.xml file and by including the following dependencies on it:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <!-- ... everything else stays untouched ... -->

  <!-- ... dependencyManagement and its children stay untouched ... -->

  <dependencies>
    <!-- ... other dependencies ... -->
    <dependency>
      <groupId>io.thorntail</groupId>
      <artifactId>cdi</artifactId>
    </dependency>
    <dependency>
      <groupId>io.thorntail</groupId>
      <artifactId>jsf</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jboss.spec.javax.faces</groupId>
      <artifactId>jboss-jsf-api_2.2_spec</artifactId>
      <version>2.2.14</version>
    </dependency>
  </dependencies>

  <!-- ... build ... -->
</project>

By adding these three dependencies to your project, you are actually adding only two features to it. First, through the io.thorntail.cdi dependency, you are making the CDI (Contexts and Dependency Injection) feature available in your app. Second, through both io.thorntail.jsf and org.jboss.spec.javax.faces.jboss-jsf-api_2.2_spec dependencies, you are making your app support the JavaServer Faces specification.

With that in place, the next thing you will have to do is to add a file called web.xml to the ./src/main/webapp/WEB-INF directory. Inside this file, you will add the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>

The main goals of this file are to:

  • register an instance of the FacesServlet class to manage HTTP requests and to construct user interfaces (HTML pages) to send as response;
  • and to define that this Servlet will only handle requests that ask for .xhtml files.

Besides that, you are using this file to define that the PROJECT_STAGE of your app is Development. This will make your logs more verbose, which will help you debug any problem that might occur. Note that, when moving your app to production, you must change this parameter to make the app faster and to avoid leaking sensitive information about your source code.

After that, what you will do is to add a controller (an Enterprise Bean) that will encapsulate some business logic. To do so, create a new class called IndexController inside the com.auth0.microblog package with the following code:

// ./src/main/java/com/auth0/microblog/IndexController.java
package com.auth0.microblog;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named
@ApplicationScoped
public class IndexController {
  public String getMessage() {
    return "Hello from JavaServer Faces!";
  }
}

As you can see, for now, your "business logic" is only a method that, when called, returns a static message. What is important to note, though, is that your bean contains two annotations:

  • @Named: Without this annotation, your JSF views (.xhtml files), wouldn't be able to use your bean.
  • @ApplicationScoped: This annotation defines that your controller is going to have a single instance and that this instance will last as long as your web app is running. Soon, you will use similar annotations that will configure different beans with different durations.

After defining your first controller, you can start defining your JSF views. However, before creating a concrete view, you will define a template (a basic structure) for all views. To do so, create a new file called template.xhtml inside the ./src/main/webapp/WEB-INF directory and add the following code to it:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

<h:head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>Thorntail Facelet</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"/>
  <style>
    img.picture {
      margin-right: 10px;
      border-radius: 50%;
      max-width: 45px;
    }
  </style>
</h:head>

<h:body>
  <div class="container">
    <ui:insert name="content"/>
  </div>
</h:body>

</html>

Note: Wondering why you are using the xhtml extension instead of html? This is a convention followed by Java developers while using JavaServer Faces and Facelets (JSF's rendering engine).

There are a few important things to notice about this file:

  1. You are defining that your views will use HTML 5 (<!DOCTYPE html>).
  2. You are adding two XML namespaces to this file: ui and h. These namespaces give you access to JSF features like the ability to define where your app must render the concrete content (ui:insert).
  3. You are adding Bootstrap to your app to make its user interface more appealing.
  4. You are using some tags introduced by JSF to define the structure of your app (h:head and h:body).

With the template defined, you can define your first concrete app. So, create a new file called index.xhtml inside the ./src/main/webapp directory and add the following code to it:

<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

<body>

<ui:composition template="./WEB-INF/template.xhtml">

  <ui:define name="content">
    <div class="row">
      <div class="col-12">
        <h1>
          <h:outputText value="#{indexController.message}" />
        </h1>
      </div>
    </div>
  </ui:define>

</ui:composition>

</body>
</html>

Here, you are defining that your concrete view uses the template.xhtml file to define its basic structure. Then, you are using the ui:define JSF tag to indicate what JSF will render inside the content section of this template. The most interesting feature in this file is that you are using the h:outputText JSF tag to output the message defined in your indexController class.

That's it. You just finished configuring JavaServer Faces in your Java EE web application. Before testing it, you will have to remove your MicroblogApplication class you created before (it won't work anymore), then, you can open your terminal and issue the following commands:

# use Maven to package the application in a fat JAR file
./mvnw clean package

# run the fat JAR artifact
java -jar target/jsf-javaee-microblog-thorntail.jar

After Thorntail finishes bootstrapping your app, if you open http://localhost:8080/index.xhtml in a web browser, you will see a white screen with the static message you defined in your IndexController bean.

Configuring JavaServer Faces in your Java EE application.

"Configuring JSF in Java EE applications supported by Thorntail can't be easier."

Developing a Blog Engine with JavaServer Faces and Java EE

As you already configured your Java EE project to support JSF and CDI, you can now start developing your application per se. For starters, you can create an entity to represent the micro-posts that your users will submit. To do so, create a class called MicroPost inside the com.auth0.microblog package with the following code:

package com.auth0.microblog;

public class MicroPost {
  private String authorId;
  private String author;
  private String authorPicture;
  private String content;

  public MicroPost(String authorId, String author, String authorPicture, String content) {
    this.authorId = authorId;
    this.author = author;
    this.authorPicture = authorPicture;
    this.content = content;
  }

  public String getAuthorId() {
    return authorId;
  }

  public String getAuthor() {
    return author;
  }

  public String getAuthorPicture() {
    return authorPicture;
  }

  public String getContent() {
    return content;
  }
}

As you can see, micro-posts will have the following structure:

  • authorId: A property that will identify (on the application level) the author of the post.
  • author: A property that will have the name of the author.
  • authorPicture: A property that will point to a link with the picture of the author.
  • content: A property that will have the content that the author wrote.

After creating this entity, you can create a service that will work as the facade between your project and a database that persists records of this entity. In this article, to make things easier to grasp, you won't use any real database (not even in memory). However, on any production-ready app, you would do so and you would define a service to interface the communication with the database, just like you are going to do.

So, create a new class called MicroPostsService inside the com.auth0.microblog package and add the following code to it:

package com.auth0.microblog;

import javax.enterprise.context.ApplicationScoped;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@ApplicationScoped
public class MicroPostsService implements Serializable {
  private List<MicroPost> microPosts = new ArrayList<>();

  public void addMicroPost(MicroPost microPost) {
    microPosts.add(microPost);
  }

  public List<MicroPost> getMicroPosts() {
    return microPosts;
  }

  public List<MicroPost> getMicroPosts(String userId) {
    return microPosts.stream()
      .filter(microPost -> microPost.getAuthorId().equals(userId))
      .collect(Collectors.toList());
  }

   MicroPostsService() {
    microPosts.add(
      new MicroPost("id123", "Bruno Krebs", 
        "https://cdn.auth0.com/blog/profile-picture/bruno-krebs.png", 
        "Developing with JavaServer Faces is cool.")
    );

    microPosts.add(
      new MicroPost("id123", "Bruno Krebs",
        "https://cdn.auth0.com/blog/profile-picture/bruno-krebs.png",
        "Java EE and JSF together make a powerful stack.")
    );
  }
}

The implementation of this service is quite simple. There, you have:

  1. a microPosts variable that is an ArrayList (this is where your app will "persist" micro-posts);
  2. a method called addMicroPost that you will use to add new items to the microPosts array;
  3. a method called getMicroPosts that you will use to retrieve all micro-posts in your app;
  4. and another method called getMicroPosts that you will use to return micro-posts of a particular author.

Also, you will notice that this class includes a constructor at the bottom. You are defining this constructor to add some fake data to your service so you can have something to show while creating a nice page for your app.

Adding the PrimeFaces Component Library to your Web App

While developing with JavaServer Faces, the most popular component library that you can use (with the greatest support and biggest community around) to support you in the development process is PrimeFaces. This component library has been around for many years and it counts with more than 100 components (like calendar, data tables, dialogs, etc.). Besides helping you become more productive by giving you access to a library of this size, PrimeFaces also helps you create a good-looking app by letting you choose some predefined theme to your components.

As such, the next thing you will do is to install PrimeFaces in your web app. To do so, open your pom.xml file and add the following dependencies to it:

<?xml version="1.0" encoding="UTF-8"?>
<project...>

  <!-- ... all other properties ... -->

  <!-- ... PrimeFace repository... -->
  <repositories>
    <repository>
      <id>primefaces</id>
      <name>Primefaces Repository</name>
      <url>https://repository.primefaces.org</url>
    </repository>
  </repositories>

  <dependencies>
    <!-- ... all other dependencies ... -->
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>6.2</version>
    </dependency>
    <dependency>
      <groupId>org.primefaces.themes</groupId>
      <artifactId>bootstrap</artifactId>
      <version>1.0.10</version>
    </dependency>
  </dependencies>

  <!-- ... build ... -->
</project>

Note: PrimeFaces themes are not distributed in the Maven Central Repository. That is why you need to define the custom repository that points to https://repository.primefaces.org.

With this change, you are actually adding two dependencies to your project:

  • org.primefaces.primefaces: the PrimeFaces component library itself;
  • and org.primefaces.themes.bootstrap: a theme that makes PrimeFaces components look like Bootstrap components.

To make PrimeFaces use the Bootstrap theme, you will have to update the web.xml file as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
  <!-- ... context-param ... -->

  <context-param>
    <param-name>primefaces.THEME</param-name>
    <param-value>bootstrap</param-value>
  </context-param>

  <!-- ... servlet and servletMapping ... -->
</web-app>

Now, you can open your index.xhtml file and replace its contents with this:

<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">

<body>

<ui:composition template="./WEB-INF/template.xhtml">

  <ui:define name="content">
    <div class="row">
      <div class="col-12">
        <p:dataTable var="microPost" id="microPosts" value="#{indexController.microPosts}">
          <p:column headerText="Author">
            <h:graphicImage styleClass="picture" value="#{microPost.authorPicture}" />
            <h:outputText value="#{microPost.author}"/>
          </p:column>
          <p:column headerText="Content">
            <h:outputText value="#{microPost.content}" />
          </p:column>
        </p:dataTable>
      </div>
    </div>
  </ui:define>

</ui:composition>

</body>
</html>

The new version of this file is making use of the p:dataTable component provided by PrimeFaces to create an HTML table with all micro-posts available in your app. Note that the data table contains a value property that contains an EL (Expression Language) pointing to indexController.microPosts. This is instructing JSF that this component will consume data from the microPosts method of the indexController instance. However, your current IndexController class contains only a method that returns a static string.

To fix that, open the IndexController class and replace its code with this:

package com.auth0.microblog;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@Named
@RequestScoped
public class IndexController {
  @Inject
  private MicroPostsService microPostsService;

  public List<MicroPost> getMicroPosts() {
    return microPostsService.getMicroPosts();
  }
}

There are only three changes that you are introducing with this refactoring. First, you are marking IndexController with the @RequestScoped annotation. With this annotation, Java EE will, from now on, create new instances for each request that arrives at your app.

Second, you are defining a variable called microPostsService and you are telling Java EE to inject an instance for you. In this case, as your MicroPostsService class is marked with @ApplicationScoped, there will be only one instance available that will support all users in your app. Having this single instance enables you to have a single source of truth when it comes to the list of micro-posts.

Lastly, you are removing the method that returned the static message and you are adding in its place the getMicroPosts method that your new index.xhtml view is consuming.

After these modifications, you can issue the following commands to run your app:

# using Maven to build your project
./mvnw clean package

# running your web app
java -jar target/jsf-javaee-microblog-thorntail.jar

After that, if you open http://localhost:8080/index.xhtml in a web browser, you will see the new home page of your microblog engine:

Building apps with JSF and PrimeFaces.

Securing JavaServer Faces and Java EE with Auth0

At this moment, your application contains: a basic entity that represents micro-posts (MicroPost); the MicroPostService class that works as a database; and a public page (index.xhtml) that shows micro-posts. Having these elements properly configured, you can start thinking about enabling your users to write their own micro-posts. To do so, you will need to distinguish between different users in a secure way. That's where Auth0 comes in handy.

Auth0, a global leader in Identity-as-a-Service (IDaaS) that provides thousands of enterprise customers with modern identity solutions, allows you to handle authentication in your app with features like Social Login, Multifactor Authentication, Passwordless Login, and much more with just a few clicks.

Besides bringing all these features to the table, you will see that one of the greatest benefits of using Auth0 to handle authentication is that you will be ready in just a few minutes. More, Auth0 offers all these benefits based on open standards.

So, to start taking advantage of all the benefits provided by Auth0, you can sign up for a free Auth0 account here. After signing up to your account, you will move to the Applications section of your Auth0 dashboard and click on the Create Application button. When you click this button, Auth0 will ask you for two things:

  1. A name for your application: As this is just a description that will allow you to distinguish this application from others that you might end up creating, you can add something like "JSF and Java EE App" to this field (or something more meaningful if you prefer).
  2. An application type: As this is a traditional web app that is controlled by a server (that is, an app that is rendered by a backend), you will choose Regular Web Application for its type.

After filling up this form and clicking on the Create button, Auth0 will redirect you to the Quick Starts tab of your new application. From there, you will have to move to the Settings tab, add http://localhost:8080/callback to the Allowed Callback URLs field, and hit the Save Changes button.

Note: As a security measure, Auth0 will only redirect users to URLs registered in this field. As such, you will need to specify an URL in your app that will be in charge of getting users back and initiating a session based on the message produced by Auth0. To learn more about how Auth0 really works underneath the surface, you can check this resource.

Now, you can focus on developing the code necessary to secure your Java EE application with Auth0. For that, you will create seven different classes, each one with its own purpose:

  • Auth0AuthenticationConfig: This class will work as an environment variable container. That is, whenever you need to reference variables that change in different environments (development, qa, production, etc), you will use this class.
  • Auth0Principal: This class will extend the CallerPrincipal class (principals are instances that represent the identity of users) provided by Java EE to make two users' properties easily available to your app: their id and their picture (CallerPrincipal also makes name available by default).
  • Auth0JwtCredential: This class will extend the Credential Java EE class and will hold an instance of Auth0Principal related to a particular user.
  • Auth0IdentityStore: This class will extend the IdentityStore Java EE class to enable the framework to use the Auth0JwtCredential extension.
  • UserSession: For each user authenticated (i.e., for each session), your app will create an instance of this class to facilitate the process of getting the user details (id, name, and picture) and to enable users to sign out.
  • CallbackServlet: This class will be responsible for intercepting HTTP requests to /callback to redirect them to a specific view.
  • Auth0LoginAuthenticationMechanism: This class will extend the HttpAuthenticationMechanism Java EE class to allow (or deny) users' requests based on whether they are trying to access a public view or not. If the view is not public, then the app will allow access only to users previously authenticated. Also, this class will be responsible for handling the authentication workflow for users returning from Auth0.

Before you can jump into coding these classes, there are other three files that you will have to create and one that you will have to update. For starters, you will have to open your pom.xml file and add the dependencies needed to secure your Java EE application:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <!-- ... leave the rest untouched ... -->

  <dependencies>
    <!-- ... other depedencies ... -->
    <dependency>
      <groupId>org.glassfish.soteria</groupId>
      <artifactId>javax.security.enterprise</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>mvc-auth-commons</artifactId>
      <version>1.0.0</version>
    </dependency>
  </dependencies>
  <!-- ... build ... -->
</project>

The first dependency, javax.security.enterprise, is the reference implementation of the Java EE Security (JSR 375) specification. The second one, mvc-auth-commons, is an open-source library that Auth0 provides to facilitate the integration of your apps with its services.

After adding these dependencies, you will have to create a new file called jboss-web.xml inside the src/main/webapp/WEB-INF directory with the following code:

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

Without this file, Thorntail won't enable the JASPIC (Java Authentication Service Provider Interface for Containers) service for your application (that is, no security context will be available in your app).

With this file in place, the next thing you will have to do is to create a new directory called resources inside the src/main directory. Then, inside this directory, you will create a file called project-defaults.yml and add the following code to it:

auth0:
  domain: <YOUR-AUTH0-DOMAIN>
  clientId: <YOUR-AUTH0-APPLICATION-CLIENT-ID>
  clientSecret: <YOUR-AUTH0-APPLICATION-CLIENT-SECRET>
  scope: openid profile email
  callbackUri: /callback

This file configures the environment variable that the Auth0AuthenticationConfig class will encapsulate. Note that you will have to replace all the placeholders above with your own Auth0 values:

  • <YOUR-AUTH0-DOMAIN>: For this placeholder, you will have to use the domain that you chose while creating your Auth0 account (or while creating a new Auth0 tenant for this project).
  • <YOUR-AUTH0-APPLICATION-CLIENT-ID>: For this placeholder, you will have to use the value available in the Client ID field of the Auth0 Application you created above.
  • <YOUR-AUTH0-APPLICATION-CLIENT-SECRET>: For this placeholder, you will have to use the value available in the Client Secret field of the same Auth0 Application.

You can find all the values needed to replace these placeholders in the Settings tab of the Auth0 Application you created before, as shown in the following screenshot:

Securing JavaServer Faces and JavaEE with Auth0.

When you finish creating this file, the next thing you will do is to create a view with restricted access to authenticated users only. You don't need to implement any specific feature in this view yet. Your goal now is to learn how to secure JSF routes with Auth0. Therefore, you can create a file called dashboard.xhtml in the src/main/webapp directory and define a simple view that shows nothing else than a title:

<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

<body>

<ui:composition template="./WEB-INF/template.xhtml">

  <ui:define name="content">
    <h2>Secured Dashboard</h2>
  </ui:define>

</ui:composition>

</body>
</html>

Now, to start developing the Java code needed to secure your app with Auth0, you will create a package called identity in the com.auth0.microblog package and then you will create the Auth0AuthenticationConfig class inside this package with the following code:

package com.auth0.microblog.identity;

import org.wildfly.swarm.spi.runtime.annotations.ConfigurationValue;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@ApplicationScoped
public class Auth0AuthenticationConfig {

  @Inject
  @ConfigurationValue("auth0.domain")
  private String domain;

  @Inject
  @ConfigurationValue("auth0.clientId")
  private String clientId;

  @Inject
  @ConfigurationValue("auth0.clientSecret")
  private String clientSecret;

  @Inject
  @ConfigurationValue("auth0.scope")
  private String scope;

  @Inject
  @ConfigurationValue("auth0.callbackUri")
  private String callbackUri;

  public String getDomain() {
    return domain;
  }

  public String getClientId() {
    return clientId;
  }

  public String getClientSecret() {
    return clientSecret;
  }

  public String getScope() {
    return scope;
  }

  public String getCallbackUri() {
    return callbackUri;
  }
}

As mentioned, you will use this class only to transport values from the project-defaults.yml file to any other class that might need them.

After creating this class, you will create the Auth0Principal class (also inside the com.auth0.microblog.identity package) and will add the following code to it:

package com.auth0.microblog.identity;

import javax.security.enterprise.CallerPrincipal;

public class Auth0Principal extends CallerPrincipal {
  private String id;
  private String picture;

  Auth0Principal(String id, String name, String picture) {
    super(name);
    this.id = id;
    this.picture = picture;
  }

  public String getId() {
    return id;
  }

  public String getPicture() {
    return picture;
  }
}

Then, you will create the IdentityStore specialization needed to validate JWT credentials. For that, you will create the Auth0IdentityStore class inside the com.auth0.microblog.identity and add the following code to it:

package com.auth0.microblog.identity;

import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.credential.Credential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;

@ApplicationScoped
public class Auth0IdentityStore implements IdentityStore {

  @Override
  public CredentialValidationResult validate(final Credential credential) {
    CredentialValidationResult result = CredentialValidationResult.NOT_VALIDATED_RESULT;
    if (credential instanceof Auth0JwtCredential) {
      Auth0JwtCredential jwtCredential = (Auth0JwtCredential) credential;
      result = new CredentialValidationResult(jwtCredential.getAuth0Principal());
    }
    return result;
  }
}

Now, you will create the Auth0JwtCredential class to define the Credential specialization that integrates with Auth0. You will also create this class inside the com.auth0.microblog.identity package and you will add the following code to it:

package com.auth0.microblog.identity;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;

import javax.security.enterprise.credential.Credential;

class Auth0JwtCredential implements Credential {
  private final Auth0Principal auth0Principal;

  Auth0JwtCredential(final String token) {
    DecodedJWT jwt = JWT.decode(token);
    String userId = jwt.getClaim("sub").asString();
    String name = jwt.getClaim("name").asString();
    String picture = jwt.getClaim("picture").asString();
    this.auth0Principal = new Auth0Principal(userId, name, picture);
  }

  Auth0Principal getAuth0Principal() {
    return auth0Principal;
  }
}

Next, you will create the UserSession class inside the same identity package and will add the following code to it:

package com.auth0.microblog.identity;

import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.security.enterprise.SecurityContext;
import java.io.Serializable;

@Named
@SessionScoped
public class UserSession implements Serializable {
  @Inject
  SecurityContext securityContext;

  public boolean isLoggedIn() {
    return securityContext.getCallerPrincipal() != null;
  }

  public String getId() {
    return getPrincipal().getId();
  }

  public String getName() {
    return getPrincipal().getName();
  }

  public String getPicture() {
    return getPrincipal().getPicture();
  }

  public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    return "/index.xhtml?faces-redirect=true";
  }

  private Auth0Principal getPrincipal() {
    return (Auth0Principal) securityContext.getCallerPrincipal();
  }
}

As already explained, the goal of this class is to facilitate access to the current user profile. This is achieved by injecting the securityContext to make the Principal instance of the current user available in the class and by defining some facades to this principal's methods (i.e., to getId, getName, and getPicture).

Note that the logout method enables users to sign out with the help of the FacesContext class that JSF makes available to apps. Also notice that, if you fail to add ?faces-redirect=true to the string returned by this method, JSF will end up rendering the contents of index.xhtml without changing the route that the user sees when they click on log out.

The next thing you will do now is to create the Auth0LoginAuthenticationMechanism class (still inside the identity package) with the following code:

package com.auth0.microblog.identity;

import com.auth0.AuthenticationController;
import com.auth0.IdentityVerificationException;
import com.auth0.Tokens;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.security.enterprise.AuthenticationStatus;
import javax.security.enterprise.authentication.mechanism.http.AutoApplySession;
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStoreHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@ApplicationScoped
@AutoApplySession
public class Auth0LoginAuthenticationMechanism implements HttpAuthenticationMechanism {

  private Auth0AuthenticationConfig config;
  private IdentityStoreHandler identityStoreHandler;
  private AuthenticationController authenticationController;

  @Inject
  public Auth0LoginAuthenticationMechanism(Auth0AuthenticationConfig config, IdentityStoreHandler identityStoreHandler) {
    this.config = config;
    this.identityStoreHandler = identityStoreHandler;
    this.authenticationController = AuthenticationController
      .newBuilder(config.getDomain(), config.getClientId(), config.getClientSecret())
      .build();
  }

  @Override
  public AuthenticationStatus validateRequest(HttpServletRequest req, HttpServletResponse res,
                                              HttpMessageContext context) {
    if (isCallbackRequest(req)) {
      CredentialValidationResult result = CredentialValidationResult.NOT_VALIDATED_RESULT;

      try {
        Tokens tokens = authenticationController.handle(req);
        Auth0JwtCredential credential = new Auth0JwtCredential(tokens.getIdToken());
        result = identityStoreHandler.validate(credential);
      } catch (IdentityVerificationException e) {
        e.printStackTrace(); //TODO: Add proper logging
      }

      return context.notifyContainerAboutLogin(result);
    }

    if (req.getRequestURL().toString().endsWith("/index.xhtml") ||
      req.getRequestURL().toString().contains("javax.faces.resource")) {
      return context.doNothing();
    }

    if (context.getCallerPrincipal() == null) {
      return context.redirect(createLoginUrl(req));
    } else {
      return context.doNothing();
    }
  }

  private boolean isCallbackRequest(HttpServletRequest request) {
    return (request.getRequestURI().equals(config.getCallbackUri()) && request.getParameter("code") != null);
  }

  private String createLoginUrl(final HttpServletRequest req) {
    String redirectUri =
      req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + config.getCallbackUri();
    return this.authenticationController.buildAuthorizeUrl(req, redirectUri)
      .withAudience(String.format("https://%s/userinfo", config.getDomain()))
      .withScope(config.getScope())
      .build();
  }
}

This class introduces four methods:

  • a constructor (Auth0LoginAuthenticationMechanism()): You define this constructor to create an instance of AuthenticationController, which is used by the createLoginUrl method to create the authentication URL.
  • validateRequest: Since this class implements the HttpAuthenticationMechanism interface and that you are adding @AutoApplySession to it, Java EE calls this method to validate if a particular request is valid or not. Your definition of this method does three things: if the request is asking for the /callback route, then this class tries to validate the user that is returning from Auth0; if the request is trying to access index.xhtml or any resource that contains javax.faces.resource (this prefix is added by JSF to static resources like images, CSS files, and JS files), then this method simply allows (context.doNothing()) the request; lastly, if the request is not originated from an authenticated user, then this method redirects the user to Auth0 so they can authenticate.
  • isCallbackRequest: This method simply encapsulates the logic needed to identify if a request is related to the callback URL that Auth0 calls after users authenticate.
  • createLoginUrl: This method creates the URL that unauthenticated users must visit to authenticate.

With this class in place, the last thing you will do is to create the CallbackServlet class to define the view that Auth0 will call after your users authenticate. So, create this class inside the com.auth0.microblog.identity package and add the following code to it:

package com.auth0.microblog.identity;

import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = {"/callback"})
public class CallbackServlet extends HttpServlet {

  @Override
  protected void doGet(final HttpServletRequest req, final HttpServletResponse res) throws IOException {
    res.sendRedirect("/dashboard.xhtml");
  }
}

As you can see, when Auth0 redirects your users back to your app, the CallbackServlet class will move your users to the dashboard.xhtml file. As dashboard.xhtml does not reference a public resource (that is, it does not endsWith("/index.xhtml") and it does not contains("javax.faces.resource")), the Auth0LoginAuthenticationMechanism class will check if the user is authenticated (context.getCallerPrincipal() == null). If they are, your app will render the page requested.

After you create all these Java classes and these configuration files, you can now rebuild your project and run it again:

# rebuild the project
./mvnw clean package

# run it again
java -jar target/jsf-javaee-microblog-thorntail.jar

Then, if you open the public page of your app (http://localhost:8080/index.xhtml) on a browser, you will still be able to consume its contents without signing in. However, if you try to open http://localhost:8080/dashboard.xhtml, your app will redirect you to Auth0. After signing in, Auth0 will redirect you back to http://localhost:8080/callback where your session will be created and, after that, your app will move you to the http://localhost:8080/dashboard.xhtml page. Not hard, right?

"Securing Java EE applications with Auth0 is easy."

Wrapping Up

Now that you have secured your JSF application, you can focus on developing its lasts features. For starters, you will redefine the template.xhtml file as follows:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">

<h:head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>Thorntail Facelet</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"/>
  <style>
    body {
      padding-top: 80px;
    }

    ul.navbar-nav {
      flex-direction: row;
    }

    ul.navbar-nav li {
      margin-left: 15px;
    }

    img.picture {
      margin-right: 10px;
      border-radius: 50%;
      max-width: 45px;
    }

    div.ui-growl.ui-widget {
      z-index: 2000 !important;
    }
  </style>
</h:head>

<h:body>
  <h:form id="messages">
    <p:growl id="growl" sticky="true"/>
  </h:form>

  <div class="container">
    <nav class="navbar fixed-top bg-secondary">
      <a class="navbar-brand text-white" href="/index.xhtml">MicroPosts</a>
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link text-white" href="/dashboard.xhtml">Dashboard</a>
        </li>
        <li class="nav-item">
          <h:form>
            <h:commandLink styleClass="nav-link text-white"
                           rendered="#{userSession.loggedIn}"
                           action="#{userSession.logout()}">
              Logout
            </h:commandLink>
          </h:form>
        </li>
      </ul>
    </nav>
    <ui:insert name="content"/>
  </div>
</h:body>

</html>

On the new version of your template, you are defining some CSS styles to make your UI more appealing. Then, you are adding a Growl PrimeFaces component (p:growl) so you can show nice pop-ups with messages like "Micro post added successfully". After that, before the content that each specific route produces (i.e., before ui:insert name="content"), you are adding a nice Bootstrap navigation header where your users will be able to:

  • navigate to the main, public page (href="/index.xhtml");
  • navigate to the dashboard (/dashboard.xhtml);
  • and logout (action="#{userSession.logout()}), if they are logged in first, of course.

Note that, if your users are not logged in, they won't see the Logout option and, if they click on the Dashboard one, your app will redirect them to Auth0 so they can authenticate first.

After redefining your template, you will make your dashboard more useful. That is, you will enable users to share their thoughts through a text field and these thoughts will become publicly available on the home page of your app. So, create a class called DashboardController inside the com.auth0.microblog package and add the following code to it:

package com.auth0.microblog;

import com.auth0.microblog.identity.UserSession;

import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@Named
@RequestScoped
public class DashboardController {
  @Inject
  UserSession userSession;

  @Inject
  private MicroPostsService microPostsService;

  private String content;

  public String getUserName() {
    return userSession.getName();
  }

  public String getPicture() {
    return userSession.getPicture();
  }

  public void addMicroPost() {
    microPostsService.addMicroPost(
      new MicroPost(userSession.getId(), userSession.getName(), userSession.getPicture(), content)
    );
    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Micro post added successfully.", null);
    FacesContext.getCurrentInstance().addMessage(null, message);
    content = "";
  }

  public List<MicroPost> getMicroPosts() {
    return microPostsService.getMicroPosts(userSession.getId());
  }

  public String getContent() {
    return content;
  }

  public void setContent(String content) {
    this.content = content;
  }
}

As you can see, this class has the following characteristics:

  • a @Named annotation that makes it consumable by other components and views;
  • a @RequestScoped annotation that informs your Java EE container that your app will need one instance of this class per request;
  • an UserSession property that is injected (@Inject) by the container so this class can get details about the logged-in user;
  • a MicroPostsService instance so this class can query and insert micro-posts on your dummy database;
  • a String property called content that will hold the text inserted by users in the "Share your thoughts" field;
  • two methods (getUserName and getPicture) that will allow your new dashboard route to show details about who is logged in;
  • two methods (addMicroPost and getMicroPosts) that will allow your new dashboard route to consume and insert micro-posts in your fake database;
  • and getters and setters methods (getContent and setContent) for the content property so your new dashboard can manipulate its value.

With this class in place, the last thing you will have to do is to redefine the dashboard.xhtml file to make it interact with the DashboardController you just created. So, open this file and replace its contents with this:

<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">

<body>

<ui:composition template="./WEB-INF/template.xhtml">

  <ui:define name="content">
    <div class="row">
      <div class="col-12">
        <p>
          <h:graphicImage styleClass="picture" value="#{dashboardController.picture}" />
          <h:outputText value="#{dashboardController.userName}"/>
        </p>
        <p:panel header="Share your thoughts" style="margin-bottom:20px">
          <h:form>
            <div class="form-group">
              <p:inputText placeholder="C'mon, express your mind."
                           styleClass="form-control"
                           id="content"
                           required="true"
                           value="#{dashboardController.content}"/>
            </div>
            <p:commandButton value="Add Micro Post" id="ajax" update=":messages:growl :microPosts content"
                             actionListener="#{dashboardController.addMicroPost}"
                             styleClass="ui-priority-primary"/>
          </h:form>
        </p:panel>
      </div>
    </div>
    <div class="row">
      <div class="col-12">
        <p:dataTable var="microPost" id="microPosts" value="#{dashboardController.microPosts}">
          <p:column headerText="Name">
            <h:outputText value="#{microPost.content}" />
          </p:column>
        </p:dataTable>
      </div>
    </div>
  </ui:define>

</ui:composition>

</body>
</html>

The new version of your dashboard route is now divided into two sections. The first section defines a panel (p:panel) with a form (h:form) inside where users will be able to share their thoughts. The second section defines a table (p:dataTable) where your users will be able to see what micro-posts they have created so far.

Now, to see the whole application up and running, you can issue the following commands:

# rebuild the application
./mvnw clean package

# run the app
java -jar target/jsf-javaee-microblog-thorntail.jar

Then, if you open your app in a browser (http://localhost:8080/index.xhtml) you will see that it now contains a nice navigation bar and, if you authenticate yourself, and navigate to the dashboard route, you will be able to create new micro-posts.

Developing Robust Apps with Java EE and JavaServer Faces

"I just built a secure Java EE and JavaServer Faces microblog engine."

Conclusion

Throughout this article, you learned how to develop a microblog engine with Java EE and JavaServer Faces. You started from scratch (i.e., with an empty directory), then you used Maven to scaffold your application. After that, you started creating some Java classes and defined a public route (index.xhtml) and, in the end, you learned how to secure your Java EE application with Auth0.

With this knowledge, you will be able to start developing your next Java EE projects in no time. Cool, isn't it?