users@javaee-security-spec.java.net

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

From: Werner Keil <werner.keil_at_gmail.com>
Date: Mon, 14 Mar 2016 18:43:22 +0100

If this isn't already defined somewhere else, maybe we could offer a
collection of constants. Similar to e.g. JSR 362 and prior Portlet standard
versions do.

Regards,
Werner



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

> All,
>
> Regarding the names j_security_check, etc; they seems to be standardized.
> see https://docs.oracle.com/javaee/6/tutorial/doc/gkbaa.html#bncbq
>
> For authentication to proceed appropriately, the action of the login form
> must always be j_security_check. This restriction is made so that the
> login form will work no matter which resource it is for and to avoid
> requiring the server to specify the action field of the outbound form. The
> following code snippet shows how the form should be coded into the HTML
> page:
>
> <form method="POST" action="j_security_check">
> <input type="text" name="j_username">
> <input type="password" name="j_password">
> </form>
>
> 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.
>
> 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
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>