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

[jsr375-experts] Re: CDI Authentication Events

From: Werner Keil <werner.keil_at_gmail.com>
Date: Fri, 13 Mar 2015 11:36:14 +0100

SecurityProvider, IdentityProvider or AuthenticationProvider are common
terms at least inside WebLogic Server and Portal.
I happened to back-port relevant providers for SAML from WLP 9/10 to 8 once
in a client project, so at least I am quite familiar with those terms. And
of course everyone inside Oracle or partners will be as well;-)

Cheers,
Werner



On Fri, Mar 13, 2015 at 11:14 AM, arjan tijms <arjan.tijms_at_gmail.com> wrote:

>
> Hi,
>
> On Friday, March 13, 2015, Jean-Louis Monteiro <jlmonteiro_at_tomitribe.com>
> wrote:
> >but it's really painful when you need to deal with MFA, tokens, etc which
> is very common nowadays.
>
> True, and it's for this reason that many of the existing security types
> and interfaces are so generic; they have to be able to handle anything.
>
> I think btw that the particular discussion we now find ourselves in is
> most closely related with the following existing issue;
> https://java.net/jira/browse/JAVAEE_SPEC-25
>
> Here Reza asks to essentially modernise/CDI-ify JAAS login modules
> (calling them "security providers, yet another term)
>
> David seems to be asking the same thing, but using an event per credential
> type, where Reza proposed an annotation and a different method signature
> per credential type. My own suggestion in the comments used a bean type per
> credential type.
>
> David, can you confirm that this is indeed what you wanted to discuss here?
>
> Kind regards,
> Arjan Tijms
>
>
>
>
>> --
>> 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
>>> >>
>>> >
>>>
>>
>>