users@javaee-security-spec.java.net

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

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Fri, 13 Mar 2015 01:03:10 +0100

Hi,

On Thu, Mar 12, 2015 at 7:49 AM, David Blevins <dblevins_at_tomitribe.com> wrote:
> 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.

Well, in Java EE and specifically in JASPIC the JAAS login module (if
used at all), does not really do the actual login. In practice it's
something like the following:

ServerAuthModule#validateRequest(..., Subject clientSubject, ...)

    LoginContext loginContext = new LoginContext(...,
credentialsCallbackHandler);
    loginContext.login();

    Subject subject = loginContext.getSubject();

    String username = extractUsername(subject);
    String[] groups = extractGroups(subject);

    CallerPrincipalCallback callerPrincipalCallback = new
CallerPrincipalCallback(clientSubject, username);
    GroupPrincipalCallback groupPrincipalCallback = new
GroupPrincipalCallback(clientSubject, groups);

    handler.handle(new Callback[]{callerPrincipalCallback,
groupPrincipalCallback});

    return SUCCESS;
}

Here the JAAS LoginContext calls through to a JAAS LoginModule, which
then gets the credentials from the passed-in
credentialsCallbackHandler, and after commit() is called makes a
Subject available, which contains the username and roles of the
logged-in user.

However, despite the name login() on the LoginContext, nobody is
logged-in for Java EE at this moment. The login only exists in the
local LoginContext and by extension the LoginModule it contains, but
nothing in the outside world is aware of this yet.

The "auth mechanism" (the Servlet proprietary implementation of FORM
etc, or a JASPIC custom one) then extracts the username and groups
from the Subject. This is always a non-standardized process. Therefor
you always have JAAS LoginModules that are specific for JBoss,
GlassFish or a specific custom auth module (see also
http://arjan-tijms.omnifaces.org/2014/02/jaas-in-java-ee-is-not-universal.html)

Once these two data items are obtained, they are put into two
structures that -are- standard, the CallerPrincipalCallback and the
GroupPrincipalCallback, which are then passed to the container
provided CallbackHandler, after which the auth module's method returns
control to the container.

It's only after this point that the (Servlet) container reads the data
from the CallerPrincipalCallback and GroupPrincipalCallback and does
whatever it internally needs to do to log the user in (which typically
consists of setting some private fields in the HttpServletRequest
implementation and/or installing a thread local security context of
some kind).

Up until this very last point there's still theoretically something
that can go wrong, so it's only after this point that an
authentication event indicating a successful login can be published.


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

True, but correct me if I'm wrong but it looks like we're talking
about *two* kinds of events here:

1. Events indicating a well known authentication step has happened
(Logged-in, Login-Failed, Logged-out, ...)
2. Events to solve the eternal problem of how to pass credentials into
a login module, when the form of those credentials are not fixed (can
be username/password, token, ....)

Is that correct?


> public void authenticate(@Observes UserPasswordAuthenticationEvent event)

Okay, so this is clearly an example of item 2. above ;)

It's a way by which the "auth mechanism" (which handles the user
interaction and therefor knows the user has provided a
username/password) can delegate the credential check and retrieval of
a username (which may be different from the provided username) and
optionally one or more groups/roles.

Btw, we still don't really have established terms for these two
things, which is why I hoped to discuss
https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-1 first on this
list, but nobody has replied to that unfortunately. It would make
exactly this discussion much easier.

Additionally I hope that with clear terminology here also comes more
awareness of the fact that there really are two main entities that
play a role in authentication, and that both of these can be switched
and/or provided by the application. When looking at the JIRA, blog
postings etc, I get the feeling that almost everyone thinks mainly
about the "credential check and retrieval of username/roles" entity
and forgets that there's also the "mechanism" part.

I do like the general idea using events though. I'm missing a bit how
the observer of such event communicates the user/caller name and
group/roles back to the container. Also, the BaseAuthenticationEvent
in the example has a getPrincipal(), but what kind of Principal would
that exactly contain?

Here we do perhaps have another terminology issue; in general a
Principal is almost another word for an "attribute". It can be
anything, like a username, phonenumber, SSN, etc. The overarching
entity (typically representing the user) is the Subject type, which
contains a "bag of Principals". Often times it's mistakingly thought
that a Principal represents the User, and that a Principal has many
attributes like name, street address etc, but this is not entirely
correct.

An alternative CDI based approach would be to define a similar kind of
hierarchy as mentioned for these kinds of authentication events, but
as interfaces to be implemented by login modules. This is essentially
what I used in the example I posted here earlier and which is further
described at http://arjan-tijms.omnifaces.org/2014/11/header-based-stateless-token.html

In that case the auth module uses the CDI bean manager to request a
bean that implements the "TokenAuthenticator" interface, via the
following utility method:

TokenAuthenticator tokenAuthenticator =
getReferenceOrNull(TokenAuthenticator.class);

(injection would be an alternative)

An implementation of the TokenAuthenticator is then defined as follows
(simplified)

@RequestScoped
public class APITokenAuthModule implements TokenAuthenticator {

    @Override
    public boolean authenticate(String token) {
      // ...
    }

    @Override
    public String getUserName() {
         // ...
    }

    @Override
    public List<String> getApplicationRoles() {
        // ...
    }

}

Ultimately my gut feeling says that publishing events vs obtaining a
bean, both for a specific type of credentials that are provided are
not -that- different, but largely a matter of style.

To sum up:

1. Actual login is done by Servlet container, not by auth module or login module
2. Two types of opposite events; 1. login is requested for given
credentials, 2. Something auth related has happened
3. Two main entities play role in authentication; the interaction
mechanism and the credential check + user/role retrieval. No clear
terms for these yet.
4. Event to essentially invoke a login module, or obtain an
implementation of login module can both be done via CDI

Hope this feedback helps ;)

Kind regards,
Arjan Tijms