jsr375-experts@javaee-security-spec.java.net

[jsr375-experts] Re: CDI Authentication Events

From: Jean-Louis Monteiro <jlmonteiro_at_tomitribe.com>
Date: Fri, 13 Mar 2015 10:03:54 +0100

Definitely agree.
Login covers probably the biggest part of the use cases, but it's really
painful when you need to deal with MFA, tokens, etc which is very common
nowadays.

--
Jean-Louis Monteiro
http://twitter.com/jlouismonteiro
http://www.tomitribe.com
On Fri, Mar 13, 2015 at 9:24 AM, arjan tijms <arjan.tijms_at_gmail.com> wrote:
> Hi,
>
> On Fri, Mar 13, 2015 at 9:12 AM, Rudy De Busscher <rdebusscher_at_gmail.com>
> wrote:
> > Can we use generics here in some way so that the casting isn't in the
> code
> > the developer write?
>
> Probably not. The issue is that there are many different kinds of
> credentials that the user can provide. username/password is one
> example and here it's most often String/String or String/char[], but
> there are many other possibilities. Both the number of data items as
> well as the type can differ.
>
> This is a bit why HttpServletRequest#login
> (
> http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String
> ,
> java.lang.String)) is not that useful, since it assumes one particular
> set of credentials (username/password again). It's therefor not
> compatible when the user provides a token as credential.
>
> Kind regards,
> Arjan Tijms
>
>
> >
> > Rudy
> >
> >
> > On 12 March 2015 at 07:49, David Blevins <dblevins_at_tomitribe.com> wrote:
> >>
> >> [ 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.
> >>
> >>
> >> -David
> >>
> >
>