Where to enable custom implementation of AuthenticationProvider or it is replaced by CubaAuthProvider on Web client

Hi there,

My application has both portal and Web client. The business logic enhanced login password by a particular pattern so that I have to customize AuthenticationProvider to massage user typed_in password, then programatically connection.login as demonstrated in Cuba for portal client:

While the user login via portal works fine, I need to do similar for Web login. By mimic the portal client approach, I created a bean in Web module:

@Service
public class WebAuthenticationProvider implements AuthenticationProvider, Serializable {
   ...
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                 log.info("Entering authenticate");
                 ...
                 Connection  connection = AppBeans.get(Connection.NAME);
                 ClientUserSession webClientSession = (ClientUserSession)connection.getSession();
                 String loginPassword = myMassgePassword((String) token.getCredentials());
                 connection.login(loginUserName,
                                  passwordEncryption.getPlainHash(loginPassword),
                                  request.getLocale());
                 ...
                 return new UsernamePasswordAuthenticationToken(webClientSession,
                                                                webClientSession.getId(), 
                                                                getRoleUserAuthorities(webClientSession));
    }
}

This bean WebAuthenticationProvider is registered in web-spring.xml as:

<bean class="cloud.pospro.posportal.web.auth.WebAuthenticationProvider"
      name="WebAuthenticator"
      scope="prototype"/>

When login, WebAuthenticationProvider is completely ignored since I did not see log message “Entering authenticate”.

I am using cuba studio 6.7.6. I also noticed “PL-9404 New Authentication subsystem”. Since my app still want to use Cuba-builtin login mechanism, just wanted to be able to transform user typed_in password, let’s say from “abc” to “xyz”, then programatically login with Cuba login, so LDAP is kind of overkilling, and I’d prefer not to use it.

Did I miss something, or Web external/customized AuthenticationProvider has to implement CubaAuthProvider instead of AuthenticationProvider, or this has to be done in a different way?

Any code example would be appreciated.

Thanks for the help,
-Mike

Hi Cuba Team,

I found this topic on the forum answered by Yuriy which sounds exactly what I need:

https://www.cuba-platform.com/discuss/t/changing-password-hashing-algorithm/2730

Blockquote
you will be able to get rid of plain-hasing passwords before sending them to middleware and implement your customized login behaviour only in single place - in LoginPasswordAuthenticationProvider.
Blockquote

Could it be possible to demonstrate “get rid of plain-hasing passwords before sending them to middleware and implement your customized login behaviour” with an sample code?

Thanks a lot,
-Mike

Hi,

We do not use Spring Security in Web Client, thus you cannot employ its classes (AuthenticationProvider) to implement custom authentication in Web Client. At the moment, there is only one way to replace standard authentication process - use custom CubaAuthProvider implementation. Since 6.8 version we are introducing new authentication mechanisms for Web Client, see https://youtrack.cuba-platform.com/issue/PL-9867

If you want to simply transform user typed in password, I’d recommend that you extend standard loginWindow using CUBA Studio and override doLogin method:

public class ExtAppLoginWindow extends AppLoginWindow {
    protected void doLogin() {
        String login = loginField.getValue();
        String password = passwordField.getValue() != null ? passwordField.getValue() : "";
        
        if (StringUtils.isEmpty(login) || StringUtils.isEmpty(password)) {
            showNotification(messages.getMainMessage("loginWindow.emptyLoginOrPassword"), NotificationType.WARNING);
            return;
        }

        // TODO Convert password here

        App app = App.getInstance();
        try {
            Connection connection = app.getConnection();

            Locale selectedLocale = localesSelect.getValue();
            app.setLocale(selectedLocale);

            if (loginByRememberMe && webConfig.getRememberMeEnabled()) {
                doLoginByRememberMe(login, password, selectedLocale);
            } else if (webAuthConfig.getExternalAuthentication()
                    && !webAuthConfig.getStandardAuthenticationUsers().contains(login)) {
                // we use resolved locale for error messages
                // try to login as externally authenticated user, fallback to regular authentication if enabled
                authenticateExternally(login, password, selectedLocale);
                login = convertLoginString(login);
                ((ExternallyAuthenticatedConnection) connection).loginAfterExternalAuthentication(login, selectedLocale);
            } else {
                doLogin(login, passwordEncryption.getPlainHash(password), selectedLocale);
            }

            // locale could be set on the server
            if (connection.getSession() != null) {
                Locale loggedInLocale = userSessionSource.getLocale();

                if (globalConfig.getLocaleSelectVisible()) {
                    app.addCookie(App.COOKIE_LOCALE, loggedInLocale.toLanguageTag());
                }
            }
        } catch (LoginException e) {
            String message = StringUtils.abbreviate(e.getMessage(), 1000);
            showLoginException(message);
        } catch (Exception e) {
            showUnhandledExceptionOnLogin(e);
        }
    }
}

It is not the clean way, but the easiest.

you will be able to get rid of plain-hashing passwords before sending them to middleware and implement your customized login behavior only in single place - in LoginPasswordAuthenticationProvider.

You can try it if you pass plain password to doLogin() method instead of hashed and replace cuba_LoginPasswordAuthenticationProvider on middleware. In your customized bean you have to hash password before check with database value. There is no demo for this approach on 6.7 version, since the process will be changed in 6.8 and all the hacks in AppLoginWindow probably will be broken.

Also, I will show all the new features and demos on 6.8 in the upcoming webinar Custom authentication and social login https://www.cuba-platform.com/webinars

Yuriy,

I will try out your doLogin method and update the post later.

I am even more excited about the upcoming webinar on Authentication and social login.

Thank you very much as always for your great help.
-Mike

Hi Yuriy,

I was just checking the webinar but could not find the custom authentication demo. If it is not been added to the webinar, could you please walk me through the steps I need to follow to implement custom authentication.

Regards,
Sanchit

Hi,

The demo project is here: https://github.com/cuba-labs/demo-68-custom-authentication

Thanks. I will have a look.