Multi-Factor Authentication for Mendix

Table of contents

As Emixa places great emphasis on creating high-quality and secure applications, we are currently actively implementing two-factor authentication (2FA) for multiple clients. Typically, this is done using available Mendix modules from the Mendix Marketplace. However, the Emixa team believed that a better and more secure solution could be developed. Therefore, the team recently created an improved standard module during one of the Emixa R&D days, making it available to the entire global Mendix community.

The module supports all variants of two-step verification: SMS, email, and the most secure method, an authenticator app. Apps such as 1Password, LastPass, Authy, Google, and Microsoft can generate a login code, which must be entered after logging in with a username and password. Our goal is for this to become the new standard for two-step verification in the Mendix world, enhancing the security of applications globally. Our CTO, Pim van der Noll, wrote a blog about it:

What is Multi-factor Authentication?

Multi-factor authentication or MFA (also known as Two-factor authentication or 2FA, along with similar terms) is an electronic authentication method, in which a user is granted access to a website or application only after successfully presenting two or more pieces of evidence (or factors) to an authentication mechanism such as:

  • Knowledge : something only the user knows.
  • Possession : something only the user has.
  • Inherence : something the user is.

MFA protects the user from an unknown person trying to access their data such as personal ID details or financial assets.

“A third-party authenticator (TPA) app enables two-factor authentication, usually by showing a randomly generated and constantly refreshing code to use for authentication.”-Wikipedia

What Problem Does Our Solution Solve?

The available multi-factor implementations in the Marketplace offer the end-user of a Mendix application during the login procedure to enter the code from a SMS, E-mail or generated by an authenticator (Google) after the actual login (and creation of the user session). See the diagram below:

https://swimlanes.io/d/zEPQlLV3T

At a successful login and prior to entering an MFA code, the user already has a valid user session and access to the user’s authorized pages, microflows etc. (but you cannot see them nor easily access them). This approach only works when dynamic role assignment and is done after a valid MFA code (which is not done by default in Mendix)

What is the Solution?

We have built an MFA module that extends the Mendix LoginAction, documented at here. 

The MFA code is validated first, and only then does the module create a user session, granting access to pages and microflows.

How Did We Ensure the Module is Secure?

We covered various scenarios, including default login, login via widgets, login for web service and REST accounts, and customized login with MFA enabled. We validated the module's security and ease of use in Mendix applications.

https://swimlanes.io/u/4o7jaAOjY

At the point in time after login in the first step:

mx.data.get({ xpath:”//System.User”, callback:function(data){console.log(data);} })

It still returns the anonymous User object as shown below:

How did we prove that this module is secure?

Scenarios to cover:

  • Default login via login.html for accounts with MFA disabled.
  • Default login via widgets for accounts with MFA disabled.
  • Default login for webservice and REST accounts.
  • Login by a customized login.html with MFA enabled (login-with-mfa.html + login-mfa.js + Authenticator app code only. Not compatible code sent by SMS or E-mail).
  • Login by default widgets but extended with the ability to enter MFA code with MFA enabled.

How easy to use is this module in your Mendix application?

Download the module from the Mendix Marketplace here or from Github.

There are a few things to configure:

After startup configuration:
1. Add ASU_MFA Microflow in your After Startup.

2. Change SUB_MFA_UserDisabledCheck to call your logic to determine if the logged-in user needs to be multi-factor authenticated. Add the new attributes HasMFAenabled(Boolean)and LastLogin2FA (DateTime) to the Account entity.

3. Add your method(s) of multifactor authentication in SUB_ValidateMFAforUser:

Three samples available:

Google Authenticator connector

SMS

Email verification with code to verify

  • Change SUB_InformMFACodeForUser to send the e-mail
  • Use default Emailtemplate module or Sendgrid API for example to compose this e-mail to your user.

4. Add snippet SN_MFA_LoginPage to your login page

5. If applicable move the login-with-mfa.html and js/login-mfa.js from the resources directory to your theme directory to support login actions with MFA from these pages.

6. Set the constant EnabledMFA to true to get started!

Keep in mind when upgrading the module from the Appstore in the future:

It will break the login mechanism, but you will be notified because by default an exception will raise when not (correctly) configured the module with your own MFA logic. Like this “An error has occurred while handling the request: java.lang.Exception: Create code not yet implemented”.

What we've learned

We preferred a non-persistent approach to keep track of the anonymous user login steps (first with login, second with MFA code). We found out when the first attempt was successfully validated, in the second step when passing the MFA code, the context/relation to the previous anonymous session was deleted. Therefore we could not correlate this to the first step. When we use a persistent MFA entity (and commit the MFA object after the first login step) it works.

Advanced: java challenges:

When extending the Login Action class and trying to set parameters from this class in our extended class, we found out this was not possible in combination with the super.execute() method. We decided to use createSession. We have already validated the username and password in the first step and the MFA object can’t be modified/created by the anonymous user (and is also checked twice).

We also wanted the module to be compatible via a login.html variant and the custom login-with-mfa.html. Therefore, it is necessary to send the MFA code together with your username and password. We need to pass this MFA code through the header because the payload is stripped by the Core LoginAction functionality.

Login-with-mfa.html:

Login-mfa.js:

MultiFactorAuthLoginAction.java:

Want to know more?

There are plenty of opportunities to drive forward digital transformation in the tech industry. Would you like to improve the digital state of your organisation? And are you looking for a partner who can help you achieve this goal? In that case, Emixa is the right fit for you. We translate complex issues into simple, user-friendly IT solutions that accelerate your digital transformation and take your business to a higher level. Don’t hesitate to contact us. We would be delighted to meet you!