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 16:44:49 +0100

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?


> 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...

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
>>
>>
>>
>>
>>
>>