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: Mon, 14 Mar 2016 23:35:37 +0100

Hi,

On Mon, Mar 14, 2016 at 6:32 PM, Rudy De Busscher <rdebusscher_at_gmail.com>
wrote:

> Regarding the names j_security_check, etc; they seems to be standardized.
> see https://docs.oracle.com/javaee/6/tutorial/doc/gkbaa.html#bncbq
>

Indeed, those names are standardised specifically for the FORM
authentication mechanism in the Servlet spec. For JSR 375 we don't have to
strictly adhere to the Servlet spec of course and can provide extended
versions if needed. Compare this to e.g. JTA that provides via
@Transactional an extended version of the equivalent EJB feature.

For now I'm mainly copying the Servlet mechanisms, both as a proof of
concept to see if the proposed API can provide at least this functionality,
as well as for a starting point to extend later.



> Of course, a version where you can define the Form yourself (Like JSF) and
> use the *request.authenticate()* is very usefull. But the developer can
> always define it like this because it is part of the 'custom way of
> implementation'? I guess.
>

Indeed, with a custom mechanism a developer can also do this directly
without using a mechanism provided by us.

Kind regards,
Arjan Tijms




>
> regards
>
> Rudy
>
>
>
> On 14 March 2016 at 17:05, Ivar Grimstad <ivar.grimstad_at_gmail.com> wrote:
>
>>
>>
>> On Mon, Mar 14, 2016 at 4:45 PM arjan tijms <arjan.tijms_at_gmail.com>
>> wrote:
>>
>>> Hi,
>>>
>>> On Mon, Mar 14, 2016 at 4:15 PM, Ivar Grimstad <ivar.grimstad_at_gmail.com>
>>> wrote:
>>>
>>>> Hi Arjan,
>>>>
>>>> It looks great! Thanks!
>>>>
>>>
>>> Thanks ;)
>>>
>>>
>>>> Should the form name and parameter names be configurable with defaults
>>>> j_security_check, j_username and j_password? Or are this names so common to
>>>> use that they can be considered standard? WDYT?
>>>>
>>>
>>> For the Servlet FORM mechanism they are hardcoded for now indeed, but
>>> the two interceptors used, @AutoApplySession and @LoginToContinue are
>>> designed to be re-used for other mechanisms, which can behave largely
>>> differently.
>>>
>>> So two options here ;) Make them configurable, or have a second
>>> "extended form" mechanism.
>>>
>>
>>>
>> At any length I wanted to code up a second mechanism that is largely what
>>> FORM does, only depending on request.authenticate() instead of a POST to
>>> j_security_check. With that you can create your own form and have JSF (or
>>> whatever) validations, but still have the automatic remember and forward to
>>> a login page whenever you try to access a protected page without being
>>> logged-in.
>>>
>>>
>>> I ported your servlet example to MVC 1.0. It works as a charm :)
>>>> Since there are no Servlet, the
>>>> @ServletSecurity(@HttpConstraint(rolesAllowed = "foo")) does not kick in
>>>> and this currently needs to be configured in web.xml, but I guess this will
>>>> be solved when we start looking at the Role/Permission Assignment epic.
>>>>
>>>
>>> In the case of MVC, it's mainly the @RolesAllowed (or variant on that)
>>> CDI interceptor, right? Or did you had something else in mind here?
>>>
>>
>> Yes, that is what I meant.
>>
>>
>>>
>>>
>>>> My sample code is here:
>>>> https://github.com/ivargrimstad/security-samples/tree/master/form-embedded-mvc
>>>>
>>>
>>> Nice one ;)
>>>
>>> I was doubting a little btw about this construct now:
>>>
>>> @FormAuthenticationMechanismDefinition(
>>> loginToContinue = @LoginToContinue(
>>> loginPage = "/ui/login",
>>> errorPage = "/ui/login?auth=-1"))
>>>
>>> It now re-uses the @LoginToContinue annotation. Variants are:
>>>
>>> @FormAuthenticationMechanismDefinition(
>>> @LoginToContinue(
>>> loginPage = "/ui/login",
>>> errorPage = "/ui/login?auth=-1"))
>>>
>>> and
>>>
>>> @FormAuthenticationMechanismDefinition(
>>> loginPage = "/ui/login",
>>> errorPage = "/ui/login?auth=-1")
>>>
>>> Last one looks a bit nicer, but since the FORM mechanism re-uses
>>> @LoginToContinue you'd have to keep the two in sync. Not sure yet what is
>>> best...
>>>
>>
>> Yes, the last one looks a little better. I think developers will
>> appreciate less typing, even if the IDEs give you code completion...
>>
>>
>>>
>>> Kind regards,
>>> Arjan Tijms
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>>
>>>>
>>>> Ivar
>>>>
>>>> On Mon, Mar 14, 2016 at 12:59 AM arjan tijms <arjan.tijms_at_gmail.com>
>>>> 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
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>