users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] Re: FORM authentication mechanism implemented

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Fri, 8 Apr 2016 17:34:27 +0200

Hi,

On Fri, Apr 8, 2016 at 5:23 PM, Ivar Grimstad <ivar.grimstad_at_gmail.com>
wrote:

> Hope I have understood correctly... with 2 phase multi-mechanism, you mean
> support for e.g. 2 factor authentication?
> +1 to that!
>

It's something else still ;)

2 factor is more done by a single mechanism that goes into a dialog with
the user and asks for multiple credentials. See e.g.
https://github.com/arjantijms/two-factor-sam/blob/master/src/main/java/net/eisele/glassfish/twofactorsam/TwoFactorServerAuthModule.java

2 phase means that you have multiple authentication mechanisms installed
and activated, e.g. HEADER and FORM. First they all get the opportunity to
authenticate right away (typically if pre-emptive authentication is used,
e.g. the client already sends credentials without the server having asked
for it).

In the first phase no mechanism is allowed to write to the response.

If no mechanism could authenticate right away, the second phase is entered.
Here mechanisms may write to the response.

The purpose of writing to the response is engaging into a dialog with the
user (i.e. challenging the user). Of course only one mechanism can actually
write to the response. To facilitate this Darran introduced the "responder"
concept, which is a piece of code (a lambda possibly) that the
container/runtime will call when a mechanism has been selected to be
allowed to write to the response.

Hope I explained it correctly ;)

Kind regards,
Arjan Tijms




>
>
>>
>>
>>> Should also add this combination of mechanisms is not just something we
>>> have thought we can do - it is based on real demand from the end users of
>>> our application servers.
>>
>>
>> This is great feedback, thanks!
>>
>>
>>
>>> I will have a look at this example in some more detail, the biggest
>>> worry if mechanisms wasting resources generating redundant responses -
>>> although with a small API change this is not far off how we have it for
>>> Elytron.
>>>
>>
>> It's indeed not the idea that mechanisms are going to waste responses.
>> The code sketched below was just a sketch, but it should work exactly like
>> you mentioned.
>>
>> Kind regards,
>> Arjan Tijms
>>
>>
>>
>>
>>
>>
>>>
>>> Instead of the mechanism populating a response the mechanism registers a
>>> responder during the evaluateRequest phase - if no mechanism successfully
>>> authenticates then the responders are called.
>>>
>>> Something like:
>>>>
>>>> public class MultiAuthenticationMechanism implements
>>>> HttpAuthenticationMechanism {
>>>> public AuthStatus validateRequest(HttpServletRequest request,
>>>> HttpServletResponse response, HttpMessageContext httpMsgContext) {
>>>> List<HttpServerAuthenticationMechanism> mechanisms =
>>>> getFromSomewhere();
>>>> List<HttpServerRequestImpl> requests = new ArrayList();
>>>>
>>>> // First phase
>>>> for (HttpServerAuthenticationMechanism mechanism : mechanisms)
>>>> {
>>>> HttpServerRequestImpl httpServerRequest = new
>>>> HttpServerRequestImpl (request);
>>>> requests.add(httpServerRequest);
>>>> mechanism.evaluateRequest(httpServerRequest);
>>>>
>>>> if (httpServerRequest.authenticationComplete() {
>>>> SecurityIdentity identity =
>>>> httpServerRequest.getIdentity();
>>>> return httpMsgContext.notifyContainerAboutLogin(
>>>> identity.getPrincipal(),
>>>> toList(identity.getRoles()));
>>>>
>>>> }
>>>> }
>>>>
>>>> // Second phase
>>>> for (HttpServerRequestImpl httpServerRequest : requests) {
>>>> httpServerRequest.getResponder().sendResponse(response);
>>>> if (httpServerRequest.authenticationInProgress()) {
>>>> return SEND_CONTINUE;
>>>> }
>>>> }
>>>> }
>>>>
>>>> I don't know yet how your API exactly works, so the above is just a
>>>> rough sketch ;)
>>>>
>>>> Kind regards,
>>>> Arjan Tijms
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On 21/03/16 18:31, arjan tijms 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
>>>> <mailto:darran.lofthouse_at_redhat.com>
>>>> <mailto:darran.lofthouse_at_redhat.com
>>>>
>>>> <mailto: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
>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> Darran Lofthouse - Principal Software Engineer
>>>>
>>>> Registered in England and Wales under Company Registration No.
>>>> 03798903
>>>> Directors: Michael Cunningham (US), Michael O'Neill(Ireland), Paul
>>>> Argiry (US)
>>>>
>>>>
>>>>
>>> --
>>> Darran Lofthouse - Principal Software Engineer
>>>
>>> Registered in England and Wales under Company Registration No. 03798903
>>> Directors: Michael Cunningham (US), Michael O'Neill(Ireland), Paul
>>> Argiry (US)
>>>
>>