users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] CDI Authentication Events

From: David Blevins <dblevins_at_tomitribe.com>
Date: Thu, 12 Mar 2015 15:32:08 -0700

[ markdown version https://gist.github.com/dblevins/211a45b893999748c7c4 ]

Looking to crack the ice. Hopefully inspire some brainstorming.

The following is a set of events Jean-Louis Monteiro created in our impl
for using CDI to extend login events to the application.

The impetus of this one came from our attempt to kill a particularly
inflexible interface in Tomcat called `Realm`. It started out hard-coded
to a specific login approach (basic auth) and has grown awkwardly since.


    java.security.Principal authenticate(java.lang.String username,
java.lang.String password);
    java.security.Principal authenticate(String username, String digest,
                                  String nonce, String nc, String cnonce,
                                  String qop, String realm,
                                  String md5a2);
    java.security.Principal authenticate(org.ietf.jgss.GSSContext
gssContext, boolean storeCreds);
    java.security.Principal authenticate(java.security.cert.X509Certificate[]
x509Certificates);

As you can see, this has obvious downsides.

Cons:

 - not extendable. each new auth scheme requires the interface to be
updated.
 - each existing implementation has to implement the new auth scheme. (at
least pre-java8)
 - caller and callee are still ultimately tied together

Clearly this is a false interface and separates very little. The calling
side and the implementing side are doing a specific dance -- they are tied
together. This is perhaps unavoidable. More on that.

Possible Pros:

 - strongly typed
 - self documenting
 - extremely simple to implement a scheme supported by the interface.

If you look at JAAS in comparison, there is effectively a single method
that counts. This is from CallbackHandler, effectively the thing that
really logs people in.

    void handle(Callback[] callbacks)
    throws java.io.IOException, UnsupportedCallbackException;

The observation on this one is it is effectively the same in function to
the hard-coded Tomcat Realm API if it looked like this:

    java.security.Principal[] authenticate(Object[] arguments);

Very little is actually being specified. If it is, then perhaps one could
argue that `public static void main(String[])` is also a security API :)

The Principal objects come in a slightly round-a-bout way, but you get the
idea. It is the polar opposite of the Tomcat Realm API.

Pros:

 - extendable

Cons:

 - too loosely typed
 - not self-descriptive
 - very hard to implement an auth scheme
 - caller and callee are still ultimately tied together

I'll note both approaches have the same downside; the caller and callee are
still ultimately tied together.

When the data you pass back and forth is essentially `Object[]` there is an
incredible amount of casting involved. You aren't relying on the
interface, but your foreknowledge of the code calling you. You haven't
separated anything ultimately and only accomplish obfuscating that simple
fact. It makes a good show of looking like it accomplishes something by
having the arguments be a specific interface, but as mentioned that
interface may as well be Object.

The calling side may as well just use reflection to find an "authenticate"
method in the callee that will accept the data it is capable of passing in.

The idea Jean-Louis crafted up, which I thought was quite genius, use CDI
Events. A callback handler is basically an event handler. Sounds like we
have several genius' on the list and this is at the forefront of everyone's
minds. That's more than exciting. :)

Here's an idea of what those look like. Putting them here not because I
think their already perfect, but so we have something to start tearing up.
Let's get creative. Some comments from me inline.

    public abstract class BaseAuthenticationEvent {
        private Principal principal;

        public Principal getPrincipal() {
            return principal;
        }

        public void setPrincipal(final Principal principal) {
            this.principal = principal;
        }
    }

Other options might include making principle a collection or this to a
"Context" class of some sort and avoiding inheritance.

    public class DigestAuthenticationEvent extends BaseAuthenticationEvent {

        private final String username;
        private final String digest;
        private final String nonce;
        private final String nc;
        private final String cnonce;
        private final String qop;
        private final String realm;
        private final String md5a2;

        // getters, setters and constructor stripped
    }

Clearly for Digest auth. I'd expect any required scheme to have a standard
event type. New ones can be provided, but there should be concrete classes
for the minimum. The event object need only simple and dumb approach to
passing arguments.

    public class GssAuthenticationEvent extends BaseAuthenticationEvent {

        private final org.ietf.jgss.GSSContext gssContext;
        private final boolean storeCreds;

        // getters, setters and constructor stripped
    }

    public class SslAuthenticationEvent extends BaseAuthenticationEvent {

        private final java.security.cert.X509Certificate[] certs;

        // getters, setters and constructor stripped
    }

    public class UserPasswordAuthenticationEvent extends
BaseAuthenticationEvent {

        private final String username;
        private final String credential;

        // getters, setters and constructor stripped
    }

I'm not a fan of the inheritance, but I love the basic idea.

The obvious use is that authentication could be implemented with a simple
observer such as:

    public void authenticate(@Observes UserPasswordAuthenticationEvent
event)

The inheritance clearly has CDI benefit in that one could observe
`BaseAuthenticationEvent` such as:

    public void authenticate(@Observes BaseAuthenticationEvent event)


This of course raises tons of question, and that of course is the idea :)

 - Who is allowed to implement this?
 - How should one reject login?
 - Do we let both the app and the container @Observes the event?
 - Do we require @Observes to use a qualifier that explicitly states the
scheme? @Scheme("BASIC")

There is another event for adding roles (more Principle instances), but
let's see what kind of ideas this fosters.

I'm sort of curious if there isn't a producing side to this as well:

    @Produces
    public UserPasswordAuthenticationEvent filter(@Authorization("Basic")
String headerValue)

Maybe a bit too low level, but interesting idea to treat the HTTP
`Authorization` header itself as some kind of event or parameter to a
producer.

Theoretically the container could sign up to handle 100% of the parts in
the common case where the app does not want to handle anything, but we
might theoretically have the container NOT add built in producers and
observers when it is determined the app has them.

Again, all brainstorming.