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

[jsr375-experts] Re: CDI Authentication Events

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Fri, 13 Mar 2015 09:24:02 +0100

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
>>
>