Hi,
> 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, ....)
I added
https://java.net/jira/browse/JAVAEE_SECURITY_SPEC-20 to cover
this case. Maybe we can standardize something around this common problem.
Thanks,
Alex
On 3/12/15 8:03 PM, arjan tijms wrote:
> 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