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

[jsr375-experts] Re: FORM authentication mechanism implemented

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Mon, 21 Mar 2016 22:00:14 +0100

Hi,

On Mon, Mar 21, 2016 at 8:53 PM, Ivar Grimstad <ivar.grimstad_at_gmail.com>
wrote:

> A possible use case for multiple authentication mechanisms may be that you
> have an application that expose a REST API and an administration UI.
> In that case it would be nice to be able to have one authentication
> mechanism for the API and another for the UI. For example certificate or
> oauth for the API and FORM for the admin UI.
>

That's indeed a very important use case, even one that's mentioned in the
JSR.

I think however that this is not what Darran primarily means with "multiple
mechanisms". Your example concerns a single mechanism for separate URLs or
URL patterns, but Darran meant multiple mechanisms for a single URL or URL
pattern.

In the latter case there has to be some way to try multiple mechanisms and
select one. In Darran's earlier example a key architectural point was to
split the request processing in two phases. First mechanisms are all given
the opportunity to inspect the request and authenticate right away. If none
succeeds then they are all given the opportunity to send a challenge.

We thus actually have 3 cases of "multiple mechanisms"

1. Multiple mechanisms in the app, one per URL pattern
2. Multiple mechanisms per URL pattern automatically attempted one by one
from list, and consulted in phases
3. Multiple mechanisms in the app, the user selects one (e.g. "login with
Facebook", "login with email")

Kind regards,
Arjan Tijms










> Ivar
>
> On Mon, Mar 21, 2016 at 7:31 PM arjan tijms <arjan.tijms_at_gmail.com> wrote:
>
>> True, but remember the mechanism as it's defined now is only EDR1 status.
>> Much can still be changed.
>>
>> Time as always is a bit of the issue. I only have so much hours I can
>> spend on JSR 375, and there's quite an amount of other things that also
>> would be really nice to have. For instance, we also don't have the multiple
>> identity stores done yet.
>>
>> As for the multiple mechanisms though, is there any other server or
>> security framework that you know of that has this? Or is Undertow/Elytron
>> currently the only one?
>>
>> To get the multiple authentication mechanisms story going, perhaps best
>> to start with creating an issue at the JSR 375 tracker and coding up a
>> proposal for the
>> https://github.com/javaee-security-spec/javaee-security-proposals repo?
>> Do you think the multiple mechanisms could be implemented for the proposal
>> using the current mechanism as a base? E.g. a single
>> HttpAuthenticationMechanism implementation that does the 2-phased
>> "try-authenticate" as you explained before?
>>
>> Additionally, I wonder if any of the other EG members have a particular
>> opinion, idea or use case for the multiple mechanisms story. Would be great
>> to collect the ideas around this.
>>
>> Kind regards,
>> Arjan Tijms
>>
>>
>>
>>
>>
>> On Mon, Mar 21, 2016 at 6:53 PM, Darran Lofthouse <
>> darran.lofthouse_at_redhat.com> wrote:
>>
>>> I still think there is big gap here in that multiple mechanisms will not
>>> be able to operate concurrently.
>>>
>>>
>>> On 13/03/16 23:58, arjan tijms wrote:
>>>
>>>> Hi,
>>>>
>>>> I just implemented a clone of Servlet's FORM authentication mechanism
>>>> using the proposed JSR 375 API. A demo app showing it can be found at
>>>>
>>>> https://github.com/javaee-security-spec/soteria/tree/master/test/app-mem-form
>>>>
>>>> The mechanism itself looks like this:
>>>>
>>>> @AutoApplySession
>>>> @LoginToContinue
>>>> @Typed(FormAuthenticationMechanism.class)
>>>> public class FormAuthenticationMechanism implements
>>>> HttpAuthenticationMechanism, LoginToContinueHolder {
>>>> private LoginToContinue loginToContinue;
>>>>
>>>> @Inject
>>>> private IdentityStore identityStore;
>>>> @Override
>>>> public AuthStatus validateRequest(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMessageContext httpMessageContext)
>>>> throws AuthException {
>>>> if ("POST".equals(request.getMethod()) &&
>>>> request.getRequestURI().endsWith("/j_security_check")) {
>>>> if (notNull(request.getParameter("j_username"),
>>>> request.getParameter("j_password"))) {
>>>>
>>>> CredentialValidationResult result = identityStore.validate(
>>>> new UsernamePasswordCredential(
>>>> request.getParameter("j_username"),
>>>> new
>>>> Password(request.getParameter("j_password"))));
>>>>
>>>> if (result.getStatus() == VALID) {
>>>> return httpMessageContext.notifyContainerAboutLogin(
>>>> result.getCallerPrincipal(),
>>>> result.getCallerGroups());
>>>> } else {
>>>> throw new AuthException("Login failed");
>>>> }
>>>> }
>>>> }
>>>> return httpMessageContext.doNothing();
>>>> }
>>>>
>>>> See
>>>>
>>>> https://github.com/javaee-security-spec/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/mechanisms/FormAuthenticationMechanism.java
>>>>
>>>>
>>>> This leans heavily on the @AutoApplySession and @LoginToContinue
>>>> interceptors, which can both be re-used by other mechanisms,
>>>>
>>>> The heart of the @LoginToContinue interceptor contains this code:
>>>>
>>>> private AuthStatus validateRequest(InvocationContext invocationContext,
>>>> HttpServletRequest request, HttpServletResponse response,
>>>> HttpMessageContext httpMessageContext) throws Exception {
>>>>
>>>> if (isOnProtectedURLWithStaleData(httpMessageContext)) {
>>>> removeSavedRequest(request);
>>>> }
>>>> if (isOnInitialProtectedURL(httpMessageContext)) {
>>>> saveRequest(request);
>>>> return httpMessageContext.forward(
>>>>
>>>> getLoginToContinueAnnotation(invocationContext).loginPage());
>>>> }
>>>> if (isOnLoginPostback(request)) {
>>>> AuthStatus authstatus = null;
>>>> try {
>>>> authstatus = (AuthStatus) invocationContext.proceed();
>>>> } catch (AuthException e) {
>>>> authstatus = FAILURE;
>>>> }
>>>> if (authstatus == SUCCESS) {
>>>> if (httpMessageContext.getCallerPrincipal() == null) {
>>>> return SUCCESS;
>>>> }
>>>> RequestData savedRequest = getSavedRequest(request);
>>>> if (!savedRequest.matchesRequest(request)) {
>>>> saveAuthentication(request, new
>>>> CredentialValidationResult(
>>>> VALID,
>>>> httpMessageContext.getCallerPrincipal(),
>>>> httpMessageContext.getGroups()));
>>>> return
>>>> httpMessageContext.redirect(savedRequest.getFullRequestURL());
>>>> } // else return success
>>>> } else {
>>>> return httpMessageContext.redirect( // TODO: or
>>>> forward?
>>>> getBaseURL(request) +
>>>>
>>>> getLoginToContinueAnnotation(invocationContext).errorPage());
>>>> }
>>>> }
>>>>
>>>> if (isOnOriginalURLAfterAuthenticate(request)) {
>>>> return httpMessageContext
>>>> .withRequest(new HttpServletRequestDelegator(request,
>>>> requestData))
>>>> .notifyContainerAboutLogin(
>>>> result.getCallerPrincipal(),
>>>> result.getCallerGroups());
>>>> }
>>>> return httpMessageContext.doNothing();
>>>> }
>>>>
>>>> See:
>>>>
>>>> https://github.com/javaee-security-spec/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/cdi/LoginToContinueInterceptor.java
>>>>
>>>>
>>>> FORM is a rather nasty mechanism to implement, and going for the
>>>> re-usable parts took a little extra time, but it does show that the
>>>> proposed mechanism API is capable of implementing a quite demanding set
>>>> of requirements (FORM is really surprisingly demanding for such an old
>>>> mechanism).
>>>>
>>>> Do note that both design and implementation are rather rough at the
>>>> moment and could do with some refinements still. But I think this could
>>>> work for an early draft.
>>>>
>>>> Kind regards,
>>>> Arjan Tijms
>>>>
>>>>
>>>>
>>