Custom implementation of CubaAuthProvider

I am trying to implement my own CubaAuthProvider.
It is not some standard protocol like LDAP, OAuth, etc, so I can use only doFilter method to authenticate users.
I know how to check if user valid, but I can’t see what to do to authenticate user inside CUBA:


public class CustomAuthProvider implements CubaAuthProvider {

    ...

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
           if (isAuthenticationRequest(httpRequest)){
                // WHAT TO WRITE HERE FOR CUBA TO UNDERSTANDS THAT USER IS AUTHENTICATED NOW?
            }
    }

    private boolean isAuthenticationRequest(ServletRequest request) {
        // Some logic here. Don't important for quastion.
    }

}

Hi,

I recommend that use examine com.haulmont.cuba.web.auth.IdpAuthProvider implementation where we authenticate user in doFilter and if a request is authenticated wrap HttpServletRequest into IdpServletRequestWrapper with Principal.

Cuba uses this principal to perform login on page opening: com.haulmont.cuba.web.DefaultApp#loginOnStart

So, in your case in // WHAT TO WRITE HERE section you have to call chain.doFilter with wrapped request that has Principal. Principal’s name will be used as Cuba user login.

Ok, I did what you suggested. From

doFilter

method i call:


private void setCustomUserAuthenticated(CustomUser customUser, HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest authenticatedRequest = new CustomAuthServletRequestWrapper(request, new CustomAuthPrincipal(customUser));

        chain.doFilter(authenticatedRequest, response);
    }

After that I redirect to the root of my app…

But on the next request in

doFilter

method I can not get security context [AppContext.getSecurityContext() is equals to null].

So possibly something goes wrong.

Details.
CustomAuthServletRequestWrapper:


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.security.Principal;
import java.util.Locale;

public class CustomAuthServletRequestWrapper extends HttpServletRequestWrapper {

    private final CustomAuthPrincipal principal;

    public CustomAuthServletRequestWrapper(HttpServletRequest request, CustomAuthPrincipal customAuthPrincipal) {
        super(request);
        this.principal =customAuthPrincipal;
    }

    @Override
    public Principal getUserPrincipal() {
        return principal;
    }

    @Override
    public Locale getLocale() {
        if (principal.getLocale() != null) {
            return principal.getLocale();
        }

        return super.getLocale();
    }
}

CustomAuthPrincipal:


import com.company.cuba.entity.CustomUser;

import javax.annotation.Nullable;
import java.security.Principal;
import java.util.Locale;

public class CustomAuthPrincipal implements Principal {

    private final CustomUser customUser;

    public CustomAuthPrincipal(CustomUser customUser) {
        this.customUser = customUser;
    }

    @Override
    public String getName() {
        return customUser.getLogin();
    }

    @Nullable
    public Locale getLocale() {
        return Locale.US;
    }
}

CustomUser:


import com.haulmont.cuba.core.entity.annotation.Extends;
import com.haulmont.cuba.security.entity.User;

import javax.persistence.Column;
import javax.persistence.Entity;

@Entity(name = "custom$User")
@Extends(User.class)
public class CustomUser extends User {

    @Column(name = "CUSTOM_ID", length = 100)
    private String customId;

    public String getCustomId() {
        return customId;
    }

    public void setCustomId(String customId) {
        this.customId = customId;
    }
}

Classes

  • CustomAuthServletRequestWrapper
  • CustomAuthPrincipal
  • CustomUser

are in

app-web

.

| I can not get security context [AppContext.getSecurityContext() is equals to null].

It is a normal situation. You have to set (and clear, performed automatically if withSecurityContext is called) SecurityContext each time you want to access middleware from HttpFilter.

Ok. I can save isAuthenticated=true in session bean. But then How do I know if authentication is expired? Or if user signed out to set isAuthenticated=false there?

See CubaAuthProvider#logout. You can override this method to perform session clean up.

I do not fully understand your auth flow so cannot say how to handle session expiration.

IdpAuthProvider#doFilter does the same logic you have described. It redirects user to an external login page and if user is already logged in it just call doFilter with authenticatedRequest. In our case when external IDP session is expired IDP server sends request to our server with session Id and we simply kill user session on middleware, it is enough to show special NoUserSession dialog to user on the next request to middleware from UI, we do not check session life time on each request in IdpAuthProvider.

Also, If you want to use NTLMv2 (Active Directory) external auth I recommend that try Jespa library (paid) integration described here: https://doc.cuba-platform.com/manual-6.4/jespa.html

Ok… lets try to clarify… In

doFilter

method I need to do two things:

  1. [if user not authenticated] Do some manipulations and redirections. After them I have request from user browser with cookie. By cookie I get
com.haulmont.cuba.security.entity.User

and want to proceed Authentication for this user in cuba app. As if he authenticated by login/password from login form. Then redirect user to root page.

  1. [if user authenticated]. I would prefer to do nothing. Just let user do what he can do as if he was authenticated by login/password from login form. But as you mentioned earlier I can not do nothing and have to set securityContext by hands.

Ideally, as analogy from SpringSecurity… I want to have the same effect as returning

Authentication != null

from

AuthenticationProvider

. And do the “manipulations and redirections” from implementation of

AbstractAuthenticationProcessingFilter

. I don’t want to change standard

SecurityContextRepository

and think about holding security context by sessions.

But probably I Understand your way to do things…

I am trying to go with custom SessionHolder. How can I get JSESSIONID in CubaAuthProvider#logout?.

Possibly real question is different. What request [url+method] used on logout. Then I’ll add logout logic to doFilter. Is this URL always the same, or could change in future / by configuration?

There is no logout URL. You can only override logout method in your provider, since this method is invoked from Vaadin UI code.

See example in IdpAuthProvider#logout:


RequestContext requestContext = RequestContext.get();
if (requestContext != null) {
    requestContext.getSession().removeAttribute(IDP_SESSION_ATTRIBUTE);
}

Using RequestContext you can access HTTP session and request / response.