What's new in Java EE Security API 1.0?

The Java EE Security API 1.0 is a new spec for Java EE 8 that aims to bridge some of the gaps that have traditionally been left unspecified and for which vendor specific (proprietary) solutions were required. By bridging those gaps it should be possible to enable security in applications without requiring any kind of vendor specific configuration.

So what will be in the Java EE Security API 1.0?

The main project page lists several epics, which together make up the main deliverables for the 1.0 version. It should be noted that partially due to a severe shortage of available resources (other constraints on the time of all EG members) version 1.0 will primarily go for the low hanging fruit. This means that it first of all seeks to standardise what pretty much all Java EE servers already do today, although by using modern and easy to use CDI based artefacts and giving much attention to defaults in order to minimise configuration.

An important design consideration of the Java EE Security API is that it does not replace any of the existing security machinery, but provides e.g. an alternative way to define or configure identity stores and authentication mechanisms. Existing declarative security as expressed by constraints in web.xml or annotations like @RolesAllowed keep working as they have always worked.

Switching between an in-app custom identity store and a traditional vendor specific (proprietary) identity store equivalent should if all plans work out be as simple as not defining that in-app custom identity store and instead defining the vendor specific one using the traditional vendor specific mechanism (e.g. using a security domain in standalone.xml for JBoss/WildFly). Likewise, some of the proposed new functionality like the security context or the security interceptors should function identical when either Java EE Security API or traditional authentication mechanisms and identity stores are used.

The process of developing the Java EE Security API 1.0 (JSR 375) started in march 2015. It currently has an anticipated release date of h2 2017, which is aligned with the anticipated release date of Java EE 8. JSR 375 missed the deadline for the EDR, but it was successfully renewed.

Download

UPDATE: Soteria can be downloaded from https://repo1.maven.org/maven2/org/glassfish/soteria/jakarta.security.enterprise/

Features

 

Identity stores

Standard identity store interface (spec issue 18)

The identity store is responsible for providing access to a storage system where caller data and credentials are stored. E.g. when being given a valid caller name and password as input it returns a (possibly different) caller name and zero or more groups associated with the caller. As such it's roughly equivalent to a model in the MVC architecture; the identity store knows nothing about its environment and does not interact with the caller. It only performs the {credentials in, caller data out} function.

Identity stores are a well known concept in Java EE, although confusingly almost every other server uses a different name. Early on in the JSR 375 cycle it was discovered no less than 16 terms were being used for the exact same concept:

authenticator login module
authentication provider login service
authentication repository realm
authentication realm relying party
authentication store security policy domain
identity manager security domain
identity provider service provider
identity store user registry
(read How Servlet containers all implement identity stores differently for more info)

The Java EE Security API will introduce a standard interface for the identity store concept, as given below:

public interface IdentityStore {
    CredentialValidationResult validate(Credential credential);
}

The interface is designed to be as minimal as possible; only capturing the {credentials in, caller data out} requirement. A Credential will in practice be some sub-type, like e.g. UsernamePasswordCredential. The return type, CredentialValidationResult, contains a status code the caller can use to see if authentication succeeded, the (new) caller name and optionally a list of groups the caller is in.

An application can install an identity store by simply having an implementation of this interface on the class path. The following shows an example with a single hard coded user:

@RequestScoped
public class TestIdentityStore implements IdentityStore {
    
    public CredentialValidationResult validate(Credential credential) {
        if (credential instanceof UsernamePasswordCredential) {
            return validate((UsernamePasswordCredential) credential);
        }

        return NOT_VALIDATED_RESULT;
    }
    
    public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {
        
        if (usernamePasswordCredential.getCaller().equals("reza") && 
            usernamePasswordCredential.getPassword().compareTo("secret1")) {
            
            return new CredentialValidationResult(
                VALID, 
                new CallerPrincipal("reza"), 
                asList("foo", "bar")
            );
        }
        
        return INVALID_RESULT;
    }
}

It should be noted that the identity store does not have a global effect on the authentication state. It's merely a data store (data repository/database) to retrieve authentication data from. Also note that as mentioned above in the current proposal just having a class with the IdentityStore interface is enough to have it registered with the container. Perhaps this is too magical though and while not technically needed it's considered to add a qualifier annotation anyway, maybe something like @SecurityIdentityStore.

Commits for this feature have been done between aug/15 and jan/16.


Standard identity store implementations (spec issue 9)

Most, if not all, Java EE servers and Servlet containers out there ship with a number of identity store implementations. The following table gives an overview for several well known servers:

Identity stores shipped with several servers
Store JBoss GlassFish Liberty WebLogic Tomcat Resin Jetty
LDAP
V
V
X
V
V
V
V (via JAAS bridge)
JDBC/Database
V
V
X
V
V
V
V
File
V (.properties)
V (.csv)
X
X
V (.xml)
V
V (.properties)
Embedded
V single user only
V
X
V
V
V
V
X.509
V
V
X
V
V
V
V
JAAS bridge
V
V
X
V
V
V
V
SPNEGO
V
V
X
V
V
V
V
SAML
V
V
X
V
V
V
V

As can be seen, LDAP support is pretty universal closely followed by some kind of database support (via a datasource stored in JDNI and/or directly via a JDBC driver). Some kind of file support is pretty universal as well. Although file formats differ, it's almost always {name, (hashed) password, zero or more groups} in some form.

Embedded is supported by less than half of the servers shown here. Embedded means that whatever location is used to say what identity store is being used has the caller data at that same location, typically as nested XML tags.

X.509 is a somewhat more special type of store. In all cases this one is backed by a keystore that is only able to check whether the presented client certificate has been issued by a trusted CA. Contrary to almost all other stores it's not capable of returning a new caller name and optionally groups. In order to get these groups this store either has to be combined with another store, or the server's proprietary principal to role mapping has to be used (meaning that you have to hardcode all possible callers (users) in some XML file, which is often a downright impossible requirement).

The Java EE Security API will standardize a number of those. The exact set is not determined yet, but it will at least include LDAP, DataBase, Embedded and the JAAS bridge.

Technically, a standard (container provided) identity store will be based on the exact same IdentityStore interface as is used for custom (user provided) identity stores, and it too will be a CDI bean. In order to select and configure a specific IdentityStore to be used an @...IdentityStoreDefinition annotation is used. This annotation is read by a CDI extension, which then programmatically adds an enabled and configured CDI bean corresponding to that store. This somewhat mimics the existing @...Definition annotations, such as @JMSDestinationDefinition.

In the current proposal @...IdentityStoreDefinition is the only way to select and configure a store, but the EG is looking at adding alternative ways such as via an XML deployment descriptor and externally from the application (but in a standard way, e.g. via a separate .rar or even .war deployment).

At the moment of writing the LDAP, Database and Embedded stores have been (partially) defined and implemented. The following gives a short overview of these:

 

The LDAP identity store

The LDAP identity store allows authentication data to be stored in an external LDAP server. It's selected and configured via the @LdapIdentityStoreDefinition annotation.

The RI specific implementation is done by org.glassfish.soteria.identitystores.LDapIdentityStore.

It should be noted that the LDAP identity store is an early version still. There's a very wide variety of ways to model users and groups in LDAP. The current version is only capable of reading a limited amount of these. The following shows an minimal example relying on the defaults for most attributes:

@LdapIdentityStoreDefinition(
    url = "ldap://localhost:33389/",
    callerBaseDn = "ou=caller,dc=jsr375,dc=net",
    groupBaseDn  = "ou=group,dc=jsr375,dc=net"
)

The RI project contains a demo application with an embedded LDAP server demonstrating this in practice.

 

The Database identity store

The Database identity store allows authentication data to be stored in an external SQL Database, that's accessed via a DataSource bound to JNDI. It's selected and configured via the @DataBaseIdentityStoreDefinition annotation.

The RI specific implementation is done by org.glassfish.soteria.identitystores.DataBaseIdentityStore.

The following shows an example:

@DataBaseIdentityStoreDefinition(
    dataSourceLookup="java:global/MyDS", 
    callerQuery="select password from caller where name = ?",
    groupsQuery="select group_name from caller_groups where caller_name = ?"
)

The RI project contains a demo application with an embedded H2 database demonstrating this in practice.

 

The embedded identity store

The embedded identity store allows authentication data to be stored directly in the annotation. This type of store is typically used exclusively for testing purposes. It's selected and configured via the @EmbeddedIdentityStoreDefinition annotation.

The RI specific implementation is done by org.glassfish.soteria.identitystores.EmbeddedIdentityStore.

The following shows an example:

@EmbeddedIdentityStoreDefinition({ 
    @Credentials(callerName = "reza", password = "secret1", groups = { "foo", "bar" }),
    @Credentials(callerName = "alex", password = "secret2", groups = { "foo", "kaz" }),
    @Credentials(callerName = "arjan", password = "secret3", groups = { "foo" }) })

The RI project contains a demo application demonstrating this in practice.

 

Commits for this feature have been done between aug/15 and jan/16.


 

Authentication mechanisms

Simplified authentication mechanism interface (spec issue 32)

Besides the identity store that was discussed above, there's another major artefact in (Java EE) security; the authentication mechanism. One of the big challenges in security discussions, next to the terminology confusion, is that this particular artefact is often overlooked. Discussions almost naturally gravitate to the identity store and to API to actually set the authenticated identity. We'll therefor first discuss what this artefact is about in the sections below.

The authentication mechanism is responsible for interacting with the caller and the environment. E.g. it causes a UI to be rendered that asks for details such as a caller name and a password, and after a postback retrieves these from the request. As such it's roughly equivalent to a controller in the MVC architecture.

Java EE has standardised 4 authentication mechanisms for a Servlet container, as well as a JASPIC API profile to provide a custom authentication mechanism for Servlet (and one for SOAP, but let's ignore that for now). Unfortunately standard custom mechanisms are only required to be supported by a full Java EE server, which means the popular web profile and standalone servlet containers are left in the dark.

Servlet vendors can adopt the standard API if they want and the Servlet spec even encourages this, but in practice few do so developers can't depend on this. (Spec text is typically quite black and white. *Must support* means it's there, anything else like *should*, *is encouraged*, *may*, etc simply means it's not there)

The following table enumerates the standard options:

  1. Basic
  2. Digest (encouraged to be supported, not required)
  3. Client-cert
  4. Form
  5. Custom/JASPIC (encouraged for standalone/web profile Servlet containers, required for full profile Servlet containers)

While there thus already exists both a standard interface for an authentication mechanism as well as various standard mechanisms, they are not optimally integrated. Or actually, they are not integrated at all.

Furthermore, the JASPIC interface for an authentication mechanisms (called ServerAuthModule, aka SAM), is a rather old, abstract and low-level design. It's Java 1.4(!) based, and very abstract in the sense that it's theoretically suitable for many different entry points of an application (like HTTP/Servlet, remote EJB and JMS). For usage in a Servlet environment JASPIC defines a Servlet Container Profile, which defines specific behavior, but doesn't introduce any specific APIs for that environment. And while a SAM itself is not that complicated, installing a SAM in a standard way requires a huge amount of verbose boilerplate code.

For the reasons outlined above, the Java EE security API will introduce a new simplified interface that's HTTP/Servlet specific and more targeted at higher level application development:

public interface HttpAuthenticationMechanism {

    AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException;
   
    default AuthStatus secureResponse(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {
        return SEND_SUCCESS;
    }
    
    default void cleanSubject(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) {
        httpMessageContext.cleanClientSubject();
    }

}

The interface mimics that of the ServerAuthModule but it doesn't require things like the getSupportedMessageTypes method (where you always have to return the same thing for the Servlet Container Profile) at all.

The only required method to be implemented is the validateRequest method, which is called before the first Filter or Servlet is called. The request and response parameters can be used pretty much just as a Filter or Servlet can use these to interact with the caller. Via the httpMessageContext the caller principal and optionally one or more groups can be set as the so-called authenticated identity, meaning that after validateRequest returns the container will consider the caller authenticated for the remainder of the request.

Just like with an identity store, an application can install an authentication mechanism by simply having an implementation of that interface on the class path. The following shows a minimal example that always sets the same hardcoded user for every request:

@RequestScoped
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Override
    public AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {

        return httpMessageContext.notifyContainerAboutLogin(
            new CallerPrincipal("reza"), 
            asList("foo", "bar"));
    }
}

While an authentication mechanism is under no obligation to delegate the credential validation and subsequent retrieval of the caller data to an identity store, it's typical good practice to do so. The following shows an example where an authentication mechanisms interacts in a minimal way with the http request and delegates to an identity store:

@RequestScoped
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Inject
    private IdentityStore identityStore;

    @Override
    public AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {

        if (isAnyNull(request.getParameter("name"), request.getParameter("password")) {
            return httpMessageContext.doNothing();
        }

        String name = request.getParameter("name");
        Password password = new Password(request.getParameter("password"));

        CredentialValidationResult result = identityStore.validate(
            new UsernamePasswordCredential(name, password));

        if (result.getStatus() != VALID) {
            throw new AuthException("Login failed");
        }

        return httpMessageContext.notifyContainerAboutLogin(
            result.getCallerPrincipal(), 
            result.getCallerGroups());
    }
}
(Note that the name and password are directly taken from the request. This is for brevity in the example only and should not be done in practice)

It should be noted that contrary to the identity store, the HttpAuthenticationMechanism *does* have an effect on the state of the application.

Both the HttpAuthenticationMechanism and HttpMessageContext interfaces are early draft proposals and are likely to be subject to a number of changes still. Specifically the AuthStatus is now directly the JASPIC type and may better be a Servlet specific type as well. The notifyContainerAboutLogin name is perhaps not ideal and it's an open question if the request and response objects should be passed in as separate parameters since they can also be obtained from the HttpMessageContext.

Commits for this feature have been done between aug/15 and jan/16.

Standard simplified authentication mechanism implementations (spec issue 19 )

The Servlet spec already defines 4 standard authentication mechanisms as mentioned above. However, these currently can not delegate to a JSR 375 identity store, nor are they CDI beans and therefore no CDI decorators can be applied to them. At the moment they are in practice also not based on the JASPIC interface.

The Java EE Security EG therefor plans to define standard implementations of at least the same authentication mechanisms that Servlet defines. It's not yet clear though if these will make it into the final release of the spec. On the one hand Java EE vendors may be able to internally delegate the JSR 375 authentication mechanism to the Servlet spec one. On the other hand, we could also ask the Servlet spec to support a special bridge that calls the JSR 375 identity store from their own internal authentication mechanism. Such bridge would then be optional for standalone Servlet containers and only required for Java EE servers.

At the moment of writing only the BASIC authentication mechanism has been defined and implemented. The following gives a short overview of this one:

 

The Basic authentication mechanism

The basic authentication mechanism allows callers to authenticate via the well known HTTP basic access authentication. It's selected and configured via the @BasicAuthenticationMechanismDefinition annotation.

The RI specific implementation is done by org.glassfish.soteria.mechanisms.BasicAuthenticationMechanism.

The following shows an example:

@BasicAuthenticationMechanismDefinition(
    realmName="test realm"
)

By contrast, the similar Servlet authentication mechanism is selected and configured in web.xml as follows:

<login-config>
    <auth-constraint>BASIC</auth-constraint>
    <realm-name>test realm</realm-name>
</login-config>

One difference with the Servlet version is that the realmName is strictly informational in the Java EE Security API version, while for the Servlet implementations it depends. For some servers it's indeed informational, for others it has to be a key that links to a named server specific identity store.

The RI project contains a demo application demonstrating this in practice.

Commits for this feature have been done between aug/15 and jan/16.


Auto apply session (spec issue 34 )

The authenticated identity set by the proposed authentication mechanism type is by default only applied to the current request. This means the mechanism will be called for every request, even when authentication already took place earlier within the same http session. It's the authentication mechanism's responsibility to re-authenticate at the start of each request.

While at first sight this may not seem logical, it's what makes the authentication mechanism naturally suitable for stateless architectures and makes pre-emptive authentication effortless.

In order to couple authentication to an HTTP session a typical approach would be to store the authenticated identity manually in the http session or in an instance field of an @SessionScoped bean implementing AuthenticationMechanism. Then as the first order of business the validateRequest method checks if the authenticated identity is available at one of those locations, and if so applies it. If not it proceeds with the regular logic.

While not particular difficult, it's a somewhat tedious pattern to implement and the Java EE Security API will therefore provide an annotation to automate this: @AutoApplySession

The RI implements this via a CDI interceptor that intercepts calls to the validateRequest method. All that's needed for the application developer is to annotate the authentication mechanism bean with this annotation.

The following shows an example of this:

@RequestScoped
@AutoApplySession
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Inject
    private IdentityStore identityStore;

    @Override
    public AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {

        if (isAnyNull(request.getParameter("name"), request.getParameter("password")) {
            return httpMessageContext.doNothing();
        }

        String name = request.getParameter("name");
        Password password = new Password(request.getParameter("password"));

        CredentialValidationResult result = identityStore.validate(
            new UsernamePasswordCredential(name, password));

        if (result.getStatus() != VALID) {
            throw new AuthException("Login failed");
        }

        return httpMessageContext.notifyContainerAboutLogin(
            result.getCallerPrincipal(), 
            result.getCallerGroups());
    }
}

The RI project contains a demo application demonstrating this in practice.

Commits for this feature have been done on 28 dec/15.


Remember me (spec issue 34 )

In authentication, "remember me" is a common use case where effectively a session with an extended duration is realised that only stores the authenticated identity data. Conceptually whatever credential and interaction (challenge/response) was used to authenticate initially is exchanged for a new credential. This new credential is typically a token that's presented via a cookie.

As such there's a strong parallel with a session ID (e.g. the well known JSESSIONID) which also is a token that may give access to an authenticated session. In practice the main difference is that a session ID is typically backed by an amount of volatile memory on the front-end (UI) server and expires relatively fast (like 30~60 minutes), while a remember me "session" ID is backed by a persistent entry in a back-end (DB) server and expires more slowly or not at all.

It should be realised that a remember me cookie is essentially the key to a user's account and should therefor be carefully guarded, i.e. used only in combination with SSL (HTTPS) and on non-shared devices, preferably ones with encrypted file systems. Although technically just as easy or difficult to hijack as a session cookie, a leaked remember me cookie can be more problematic because of it being valid for a (much) longer time. Because of these reasons "remember me" is often a choice being offered to the user instead of being applied automatically.

The Java EE Security API will provide support for "remember me" via the @RememberMe annotation and a special purpose secondary identity store called the RememberMeIdentityStore.

In order to enable remember me an existing authentication mechanism has to be annotated with the @RememberMe annotation, and an implementation of the RememberMeIdentityStore has to be present on the class path. Perhaps a decorator mechanism will be provided to enable remember me on mechanisms that are not under the control of the application developer (i.e. mechanisms provided by a 3rd party library), or perhaps the mere existence of a RememberMeIdentityStore can be enough (but this is again a balance of minimal configuration vs being too magical).

The purpose of the RememberMeIdentityStore is to vend a token in exchange for the authentication data, and to validate this token at a later moment just like a regular identity store would do.

As mentioned above, remember me is often given as a choice to the user. To facilitate this choice the @RememberMe annotation has an attribute that accepts an EL expression. This expression needs to have a boolean outcome, but can otherwise be whatever the application developer wants to put there. I.e. it can be bound to the method of some (JSF) backing bean, reference the current request, contain a conditional that checks a configuration parameter first, etc.

The variables available to the EL expression are all CDI named beans, and two special variables: "this", which refers to the bean being intercepted by the remember me interceptor, and "httpMessageContext" which is the method parameter of type HttpMessageContext that's passed into the intercepted validateRequest method.

As an implementation detail, EL support in the RI is based on the EL 3.0 ELProcessor which makes adding EL support a breeze.

The following shows an example of using the annotation and specifically the EL expression:

@RequestScoped
@RememberMe(
    cookieMaxAgeSeconds = 3600,
    isRememberMeExpression ="this.isRememberMe(httpMessageContext)"
)
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Inject
    private IdentityStore identityStore;

    @Override
    public AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {
        // ...
    }

    public Boolean isRememberMe(HttpMessageContext httpMessageContext) {
        return httpMessageContext.getRequest().getParameter("rememberme") != null;
    }
}

An example of the RememberMeIdentityStore is shown below:

@ApplicationScoped
public class TestRememberMeIdentityStore implements RememberMeIdentityStore {

    private final Map<String, CredentialValidationResult> identities = new ConcurrentHashMap<>();
 
    @Override
    public CredentialValidationResult validate(RememberMeCredential credential) {
        return identities.getOrDefault(credential.getToken(), INVALID_RESULT);
    }
 
    @Override
    public String generateLoginToken(CallerPrincipal callerPrincipal, List<String> groups) {
        return putAndReturnKey(
            identities, 
            randomUUID().toString(),
            new CredentialValidationResult(VALID, callerPrincipal, groups));
    }
 
    @Override
    public void removeLoginToken(String token) {
        identities.remove(token);
    }
 
}

Note that the RememberMeIdentityStore example shown above is just a fairly brief (but working) example of how the API is used. In a real world implementation the data would more likely be stored in a database instead of in an application scoped map, and then of course not the token itself would be stored but rather a strong hash would.

The remember me functionality can be combined with the Auto apply session feature. In that case the runtime will first check for the authentication data in the session, if it doesn't find it there it will check for the remember me token and if present try the remember me identity store, and if that doesn't work finally try the main identity store. The following shows a brief example of this:

@RequestScoped
@AutoApplySession
@RememberMe
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
   // ...
}

The RI project contains a demo application demonstrating this in practice.

Commits for this feature have been done between 12 jan/16 and 17 jan/16.


 


That's it for now! I'll periodically update this page to cover the latest Java EE Security API developments.

Arjan Tijms

Comments

  1. Can "RememberMe" feature remember user on multiple browsers/machines?

    ReplyDelete
    Replies
    1. >Can "RememberMe" feature remember user on multiple browsers/machines?

      It should be yes. The cookie you get is per browser and there is nothing in the design that prevents a backend from associating multiple tokens (coming from multiple cookies, which on their turn come from multiple browsers/machines) with a single identity. It's just a key value store really. 123 can map to john, and 456 can map to john as well.



      Delete
  2. Thanks for your post, right now i am learning Soteria and it help me a lot :)

    ReplyDelete
  3. hi Arjan Tijms.
    A have a question. Are authentication credentials distributable?
    For example. If we have 2 nodes in cluster (I mean 2 wildfly in domain) and simply put in web.xml.
    If user is loggedin node1 what will be distributed to node2?

    (Sorry for my English)

    ReplyDelete

Post a Comment

Popular posts from this blog

Jakarta EE Survey 2022

Implementing container authentication in Java EE with JASPIC

Counting the rows returned from a JPA query