Re: Welcome to the new JSR-375 mailing list


Arjan Tijms
 

Hi,

On Mon, May 15, 2017 at 7:36 AM, Will Hopkins <will.hopkins@...> wrote:

Can you explain more about that? What does Custom Form do that's not already available (apart from leveraging IdentityStore), and what benefits does the alignment bring for apps?

With the existing form authentication mechanism, an application can start the authentication dialog by calling HttpServletRequest#authenticate, but it can't continue it in this way after having collected the credentials.

Custom form provides the full form algorithm / process such as specified by the Servlet spec, with the small but essential difference that  the "command" to continue the authentication dialog after the mechanism calls out to the application can be done programmatically (via Java code) instead of posting back to the virtual URL /j_security_check with post parameters j_username and j_password.

This is important because in modern Java EE applications you often want to bind those credentials to a CDI bean, then use existing bean validation to validate some basics (password not null, password long enough, etc) and render an error message via JSF if anything was wrong.

There's a test/example of that in the RI:

First we have a view with credentials bound via EL to a CDI bean, and the submit action bound to a login method on a CDI bean:

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


Then the CDI bean has bean validation annotations applied:

@Named
@RequestScoped
public class LoginBacking {

    @NotNull
    @Size(min = 3, max = 15, message="Username must be between 3 and 15 characters")
    private String username;
    
    @NotNull
    @Size(min = 5, max = 50, message="Password must be between 5 and 50 characters")
    private String password;

    // ...
}

And then the login method can continue the authentication dialog:

public void login() {
         
        Credential credential = new UsernamePasswordCredential(username, new Password(password));
        
        AuthenticationStatus status = securityContext.authenticate(
            getRequest(context),
            getResponse(context), 
            withParams()
                .credential(credential));
        
        if (status.equals(IN_PROGRESS)) {
            context.responseComplete();
        } else if (status.equals(FAILURE)) {
            addError(context, "Authentication failed");
        }

}

This modern approach to Java EE programming is not possible with the existing form mechanism, where posting back to /j_security_check is often seen as old and clunky and certainly not capable of easily leveraging existing code in CDI, EL, BeanValidation and JSF.

 
The regular form and basic authentication mechanisms are present not only for consistency reasons, but also make it both trivial and guaranteed to use the JSR 375 identity store with them, which wouldn't hold for the existing Servlet authentication mechanisms.

Support for IdentityStores could be achieved more simply and directly (from a container implementer's POV), by simply mandating that FORM and BASIC auth methods (perhaps CERT as well) use application-provided IdentityStores when present.

This is certainly an option, and the reverse has actually been proposed as well (but not acted upon) that the HAM will be able to use a container specific IdentityStore in some way (a bridge IdentityStore would be one way to achieve this).

That however doesn't help with the consistency and CDI things.

You could see it the other way around though; a full container does not necessarily have to implement those two mechanisms twice. From the point of the spec the JSR 375 provided FORM is NOT required to be implemented fully, it just says this:

/**
 * Annotation used to define a container {@link AuthenticationMechanism} that implements
 * FORM authentication mechanism as defined by the Servlet spec and make that implementation
 * available as an enabled CDI bean.
 *
 */
@Retention(RUNTIME)
@Target(TYPE)
public @interface FormAuthenticationMechanismDefinition {
 
    @Nonbinding
    LoginToContinue loginToContinue();
    
}

The RI for convenience implements it from scratch, but full containers are only required to look at this annotation for the configuration of the existing (servlet) mechanism and expose a CDI bean that wraps or delegates to it.

Your concern is valid, but we took this into account earlier. I think we had discussions with Alex and perhaps David about this way in the beginning. That's why it says "as defined by the Servlet spec". A lot of thought went onto that seemingly simple fragment of text.

 
Any given app is likely to have one or the other, not both, and custom schemes are, by definition, custom.

Here I meant from a practical EE product testing and implementation perspective. If a lot of tests that the implementors do for BASIC and FORM directly go to the Servlet mechanism, they don't test the CDI "wrapper/delegation" code paths. A separate custom mechanism would have to be added to the test suite and only that tests those paths.

As I've seen with JASPIC, this rarely happens, resulting in 4 long years of reporting and fixing various bugs.

Applications that use FORM via the CDI wrapper would also implicitly test that CDI wrapper, so when it comes time for someone to use a custom authentication mechanism, the chance that these paths have already been tested via the supplied mechanisms is a lot higher. 

 
The servlet container already provides default implementations of FORM and BASIC, just not ones that leverage HAM. Does JSF provide multiple implementations of each default UIComponent?

You mean if JSF provides a component already present in another spec?

In that regards, it's a full yes.

JSP and JSTL define a c:out tag, which is used to render output.

JSF defines an h:outputText tag, which does the same thing but more aligned with how the other tags (and associated components) work in JSF. Here too, especially when used on JSP, an implementation would not necessarily have to code up the same output code twice but could have the h:outputText tag as a front for the code in c:out.

 
I have some architectural concerns about the general approach of implementing HAMs for BASIC and FORM auth, and with RememberMe in particular.

It's possible that I don't fully understand the intent of RememberMe, but it looks to me like what it's really doing is session management, not authentication.

This is not entirely correct. What it does is effectively exchange credentials for a token, and the token is used to authenticate the caller at every request when no (valid) HTTP session is present.

This is currently defined in the spec as follows:

 * For the remember me function the credentials provided by the caller are exchanged for a token
 * which is send to the user as the value of a cookie, in a similar way to how the HTTP session ID is send.
 * It should be realized that this token effectively becomes the credential to establish the caller's
 * identity within the application and care should be taken to handle and store the token securely. E.g.
 * by using this feature with a secure transport (SSL/https), storing a strong hash instead of the actual
 * token, and implementing an expiration policy. 

 
The RememberMeIdentityStore isn't really an identity store at all, it's a cache of authenticated users.

Not necessarily. A simple implementation of the interface could be just a cache indeed, but a "real" IdentityStore could also just be effectively a cache.

How a RememberMeIdentityStore is implemented is up to the user. An implementation could say go to a local filesystem based DB if the token is not older than a certain amount of time, and force revalidation at a remote origin authentication server if it's older. Tokens could be automatically expired and/or revoked explicitly.
 
If we really wanted to specify session management, we could do so, but the servlet spec already says how servlet containers should do session management. Shouldn't we integrate with that existing capability, rather than building a redundant, and less capable, feature on top of the existing feature?

The Servlet spec does not have any specification for this kind of session management. The HTTP session is a very broad type of session that brings with it a lot of other things, but mostly memory consumption. That's why the HTTP session has to expire after a relatively short amount of time.

The remember me token is longer lived (by definition, basically) and has no associated concept of consuming server resources. As mentioned above, an application can authenticate users using the token by going to a database or any other store.

The entire point of the token is to be able to live longer than the http session. If it would directly use the http session it would not even be needed (this is basically the key insight).

A typical pipeline as defined by priorities in a CDI interceptor chain in JSR 375 is to do the following in order:

1. Check the HTTP session if the caller is authenticated, if so use this identity
2. If no details in the HTTP session, check if a remember me token is present, if so use this to authenticate the user
3. If no remember me token is present, start the lowest level authentication mechanism (e.g. FORM).

So with both HTTP sessions and RememberMe activated, the very first request would hit 3). The second and following requests within that HTTP session would hit 1).

Then when the HTTP session expires and the caller does a request again, it would hit 2), and then for all following requests within the new HTTP session it would hit 1) again.

 
Do we really want two session cookies rather than one?

Yes, absolutely, since the expiration policy of the two cookies are totally different. One is longer lived and MUST NOT be removed when the browser closes, while the other is shorted lived and MUST be removed when the browser closes.
 
Have we specified it fully enough? How does RememberMe ensure that the cookies it issues are protected against session stealing attacks? Do we require that the cookie is marked secure? If so, how does it work if an authenticated user makes a request over a non-SSL connection?

These are good questions and the answer is that it's basically the same as for the JSESSIONID cookie. Servlet containers basically give the user 2 options for this as evidenced by the cookie-config:

<cookie-config>
    <http-only>true</http-only>
    <secure>true</secure>
</cookie-config>

In the JSR 375 case the spec warns for this case by suggesting that "secure" should be required "by using this feature with a secure transport (SSL/https", but it would probably be a good idea to default these two settings to be set to true. The only reason for secure not being set to true is localhost development, where you often don't have a secure connection to your dev server.

The cookie handler in the RI already sets http only to true btw:

public class CookieHandler {

public static void saveCookie(HttpServletRequest request, HttpServletResponse response, String name, String value, Integer maxAge) {
Cookie cookie = new Cookie(name, value);
if (maxAge != null) {
cookie.setMaxAge(maxAge);
}
cookie.setHttpOnly(true);
cookie.setPath(isEmpty(request.getContextPath())? "/" : request.getContextPath());

response.addCookie(cookie);
}

But I think it would be good to make the spec a bit more strict here, thanks for mentioning this concern!


 
Similarly the FORM and BASIC implementations, if left in the API, should be required to integrate with container session management, so that behavior is uniform for apps that use HAM and apps that don't. Authentication is authentication; session management is different, and mostly (though not entirely) orthogonal.

In this case it's simply a layer underneath the existing session, like layers in a memory hierarchy. So as explained above it's http session -> token store -> original store (and mechanism).

 
In part where I'm coming from is that I see a difference between a framework that's mean to run in a wide variety of platforms, some of which may be Java EE containers, and some of which may not, and a JSR, which is explicitly meant to be *part* of the Java EE platform, and fully integrated with it -- not merely layered on top. I think we should be specifying where JSR-375 needs to integrate with the servlet container, not how it can duplicate or re-implement existing functionality.

I understand your concerns, but as explained above it's not duplicating or re-implementating functionality. By its very definition a "remember me" token must live outside the http session, but it only comes into play if the http session is not there anymore. Perhaps that's where the confusion comes from. If the http session is active, the mechanisms you mention work with the http session as they normally would.
 
Those are my initial thoughts, anyway. I do see the benefits of the fact that HAMs are CDI beans, but, at a minimum, I think there's complexity here, and some questions that need to be resolved, and little or no time to do so. That's probably my fault, but we are where we are.

Thoughts?

We are where we are indeed. I hope to have answered most questions, and that we can amend the spec today where needed. Especially requiring secure and httponly to default to true would probably be a good thing to add to the spec.

Thanks for your thorough review!

Kind regards,
Arjan Tijms


 


Will

-- 
Will Hopkins | WebLogic Security Architect | +1.781.442.0310
Oracle Application Development
35 Network Drive, Burlington, MA 01803


Join javaee-security-spec@javaee.groups.io to automatically receive all group messages.