JBoss.orgCommunity Documentation

Chapter 13. Errai Security

13.1. Basic Model
13.2. Getting Started
13.2.1. Making Users
13.2.2. Authentication from the Client
13.3. RestrictedAccess
13.3.1. RPC Services
13.3.2. Page Navigation
13.3.3. Hiding UI Elements
13.4. Using an Alternative to PicketLink
13.4.1. Form Based Login

Errai Security provides a lightweight security API for declaring RPC services and client-side UI elements which require authentication or authorization.

Plugin Tip

Use the Errai Forge Addon Add Errai Features command and select Errai Security to follow along with this section.

Manual Setup

Checkout the Manual Setup Section for instructions on how to manually add Errai Security to your project.

Errai Security provides two main concepts:

By default the server-side Errai Security module uses PicketLink for authentication. Later on we will explain how to use an alternative backend.

The simplest way to begin experimenting with Errai Security is to add Users and Roles to PicketLink programmatically. Here is some sample server-side code from the Errai Security Demo.

@Singleton

@Startup
public class PicketLinkDefaultUsers {
  @Inject
  private PartitionManager partitionManager; 1
  /**
   * <p>Loads some users during the first construction.</p>
   */
  @PostConstruct
  public void create() {
    final IdentityManager identityManager = partitionManager.createIdentityManager();
    final RelationshipManager relationshipManager = partitionManager.createRelationshipManager();
    User john = new User("john");
    john.setEmail("john@doe.com");
    john.setFirstName("John");
    john.setLastName("Doe");
    User hacker = new User("hacker");
    hacker.setEmail("hacker@illegal.ru");
    hacker.setFirstName("Hacker");
    hacker.setLastName("anonymous");
    identityManager.add(john); 
2
    identityManager.add(hacker);
    final Password defaultPassword = new Password("123");
    identityManager.updateCredential(john, defaultPassword);
    identityManager.updateCredential(hacker, defaultPassword);
    Role roleDeveloper = new Role("simple");
    Role roleAdmin = new Role("admin");
    identityManager.add(roleDeveloper);
    identityManager.add(roleAdmin);
    relationshipManager.add(new Grant(john, roleDeveloper)); 
3
    relationshipManager.add(new Grant(john, roleAdmin));
  }
}

Here are the important things that are happening here:

1

PicketLink uses the concept of partitions, which are sections that can contain different users and roles. What we really need to make users and roles are the IdentityManager and RelationshipManager, but these objects are @RequestScoped so in order to access them when the application starts we must @Inject the PartitionManager.

2

Here we add are new users to the IdentityManager. It is also used below to give passwords to the new users, and to add the simple and admin roles.

3

The RelationshipManager defines relationships between entities. In this case, it is used to specify that a user belongs to a role.

Once you’ve created some users and roles, you’re ready to write some client-side code. Authentication is performed with the org.jboss.errai.security.shared.service.AuthenticationService via Errai RPC.

Here is some sample code involving the user john from the previous Security Demo excerpt.

The annotation @RestrictedAccess is the only annotation necessary to secure a resource or UI element. In general, @RestrictedAccess blocks a resource from users who are not logged in; if an array of roles are passed in, users without the declared roles are prevented access to the annotated resource. Below we will explain how different resources are blocked from unauthorized users.

To secure an Errai RPC service, simply annotate the RPC interface (either the entire type or just a method) with one of the security annotations.

For example:

When access to a secured RPC service is denied an UnauthenticatedException or UnauthorizedException is thrown. This error is then transmitted back to the client, where it can be caught with an ErrorCallback (provided when the RPC is invoked).

Here is how we would invoke the previous MixedService example with error handling:

MessageBuilder.createCall(new RemoteCallback<Void>() {


    @Override
    public void callback(Void response) {
      // ...
    }
  }, new ErrorCallback<Message>() { 
1
    @Override
    public boolean error(Message message, Throwable t) {
      if (instanceof UnauthenticatedException) {
        // User is not logged in.
        return false;
      }
      else if (instanceof UnauthorizedException) {
        // User is logged in but lacked sufficient roles.
        return false;
      }
      else {
        // Some other error has happened. Let it propogate.
        return true;
      }
    }
  }, MixedService.class).adminService();

1

This ErrorCallback is parameterized with the type Message because it is an Errai Bus RPC. In the next section we will demonstrate the use of a JAX-RS RPC.

JAX-RS RPCs are secured exactly as bus RPCs. Here is the first example from the previous section, but converted to use JAX-RS instead of the Errai Bus.

@Path("/rest-endpoint")

@RestrictedAccess
public interface UserOnlyStuff {
  @Path("/some-method")
  @GET
  public void someMethod();
  @Path("/other-method")
  @GET
  public void otherMethod();
}

There are two important differences when calling a secured JAX-RS RPC (in contrast to an Errai Bus RPC):

Because there is no global error-handling, you should always pass a RestErrorCallback when using a JAX-RS RPC. Errai provides the DefaultRestSecurityErrorCallback that provides the same default behaviour as the DefaultBusSecurityErrorCallback mentioned above. It can also optionally wrap a provided callback as demonstrated below:

Any class annotated with @Page can also be marked with @RestrictedAccess. By doing so, users will be prevented from navigating to the given page if they are not logged in or lack authorization.

Here are two simple examples:

Security checks performed before page navigation do not use any RPC calls, but are instead performed from a cached (in-memory) instance of the org.jboss.errai.security.shared.api.identity.User. This prevents the possibility of lengthy delays between page navigation while waiting for RPC return values.

But the drawback is that any attempts to navigate to a secured @Page before the cache is populated will result in redirection to the LoginPage — even if the user is in fact logged in.

In practice, this is only likely to happen if a user starts an Errai app with a URL to a secure page while still logged in on the server from a previous session.

One option offered by Errai is to persist the org.jboss.errai.security.shared.api.identity.User object in a cookie. This can be done by adding the following to ErraiApp.properties:

errai.security.user_cookie_enabled=true

With this option enabled the User will be persisted in a browser cookie, which is loaded quickly enough to avoid the described navigation issue. This feature can also be used to allow an application to work offline, or allow the server to log in a user on an initial page request.

If you do not wish to use this feature you will likely want to handle this case in the @PageShowing method of your LoginPage. Here is an outline of what you might want to do:

@Page(role = LoginPage.class)

@Templated
public class ExampleLoginPage extends Composite {
  @Inject
  private SecurityContext securityContext;
  @Inject
  private Caller<AuthenticationService> authService;
  @Inject
  @DataField
  private Label status;
  @PageShowing
  public void checkForPendingCache() {
    // Check if cache is invalid.
    if (!securityContext.isUserCacheValid()) {
      // Update the status.
      status.setText("loading...");
      // Force cache to update by calling getUser
      authService.call(new RemoteCallback<User> {
        @Override
        public void callback(User user) {
          /* An interceptor will have updated the cache by now.
             So check if we are logged in and redirect if necessary.
          */
          if (!user.equals(User.ANONYMOUS)) {
            /* This is a special transition that takes us back to
               a secure page from which we were redirected. */
            securityContext.navigateBackOrHome();
          }
          else {
            status.setText("You are not logged in.");
          }
        }
      }).getUser();
    }
  }
}

All Errai Security authentication is implemented with Errai Remote Procedure Calls to the AuthenticationService. A default implementation of this interface using PicketLink is provided in the errai-security-picketlink jar. But it is possible to use a different sever-side security framework by providing your own custom implementation of AuthenticationService and annotating it with @Service. In that case your project should not depend on errai-security-picketlink.