2 Factor Authentification

Questions and answers on designing your Servoy solutions, database modelling and other 'how do I do this' that don't fit in any of the other categories

2 Factor Authentification

Postby nam.le » Sat Nov 05, 2022 7:40 pm

Dear all,
any of you have experience creating an solution with 2FA ?
Best regards
Nam
nam.le
 
Posts: 22
Joined: Thu Jan 21, 2021 1:32 pm

Re: 2 Factor Authentification

Postby steve1376656734 » Sat Nov 05, 2022 9:16 pm

Hi Nam,

We have created a solution that uses a javascript module to calculate a TOTP that is compatible with both Google and Microsoft Authenticator apps. We also have a custom NG component that we use to display a QR code that a user can scan with their App to set up the account.

Creating the javascript module is not difficult and the algorithm to be used is widely available on the web. The custom QR code component we developed is based on the OpenSource project found at https://github.com/jeromeetienne/jquery-qrcode.

Once you have these elements then you will need to add some forms to your solution to allow the user to enable the 2FA and choose their preferred authentication method (App, eMail or SMS) and then hook up each of those services accordingly.

The final part of the puzzle is to update your login process to check if 2FA is enabled for the user logging in and if so then prompt for the current code and then validate it before continuing the login.

Steve
Steve
SAN Developer
There are 10 types of people in the world - those that understand binary and those that don't
steve1376656734
 
Posts: 327
Joined: Fri Aug 16, 2013 2:38 pm
Location: Ashford, UK

Re: 2 Factor Authentification

Postby mboegem » Sun Nov 06, 2022 2:35 pm

Hi Nam,

There are different ways for 2FA, I guess everyone knows text-message tokens or google authenticator tokens.

text-message tokens can be done using an external service like MessageBird: https://messagebird.com

google authenticator can be done the way Steve posted.
Below there's another way using 2 java libs, which you just drop in the application_server/plugins folder

Advantage of using text-message is that probably everyone has ever used these, but a service like MessageBird is not free of charge, so depending on the number of requested tokens this can be an expensive operation.
Another big advantage is that MessageBird will be handling the token-verification completely from their API, meaning that even you as a developer won't have a clue what tokens are used.
They also offer extra service like phone-number validations (ie. is it really a mobile phone number that was entered)

The Google authenticator as posted below, is completely free of charge, but depending on the way you store the secrets (ie. will this be the same server/database) it is less secure.


Libs:
https://repo1.maven.org/maven2/com/goog ... -3.4.1.jar
https://repo1.maven.org/maven2/com/warr ... -1.5.0.jar

The googleAuth library is used to generate the otp-code and otp-url:
Code: Select all
function generateVerifcationQRq(_sEmail) {
   var _oResult = {result:false, secret:null, qr_image:null, uri:null};
   
   var _authenticator = new Packages.com.warrenstrange.googleauth.GoogleAuthenticator();
   
   if(_authenticator) {
      var _credentials = _authenticator.createCredentials();
      
      if(_credentials) {
         var _sKey = _credentials.getKey();
         
         if(_sKey) {
            var _sURI = Packages.com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL('<DOMAIN_OF_YOUR_APP>', _sEmail, _credentials);
            
            var _aQR = getQrCode(_sURI); // this is the 2nd function as posted here
            
            if(_aQR) {
               _oResult.result = true;
               _oResult.secret = _sKey;
               _oResult.qr_image = _aQR;
               _oResult.uri = _sURI;
            }
         }
      }
   }
   
   return _oResult;
}



The zxing library is used to generate a byteArray that represents the actual QR Code:
Code: Select all
function getQrCode (_sURL) {
   var _sQrCodeText = java.lang.String(_sURL)
   var _nSize = 500;
   
   var _oHashMap = new Packages.java.util.Hashtable();
   _oHashMap.put(Packages.com.google.zxing.EncodeHintType.ERROR_CORRECTION, Packages.com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.L)
      
   var _oQrCodeWriter = new Packages.com.google.zxing.qrcode.QRCodeWriter()
   var _oByteMatrix = _oQrCodeWriter.encode(_sQrCodeText, Packages.com.google.zxing.BarcodeFormat.QR_CODE, _nSize, _nSize, _oHashMap);
   var _nMatrixWidth = _oByteMatrix.getWidth();
      
   var _oImage = new java.awt.image.BufferedImage(_nMatrixWidth, _nMatrixWidth, java.awt.image.BufferedImage.TYPE_INT_RGB);
   _oImage.createGraphics();
   var _oGraphics = _oImage.getGraphics();
   _oGraphics.setColor(java.awt.Color.WHITE);
   _oGraphics.fillRect(0, 0, _nMatrixWidth, _nMatrixWidth);
   _oGraphics.setColor(java.awt.Color.BLACK);
      
   for (var i = 0; i < _nMatrixWidth; i++) {
      for (var j = 0; j < _nMatrixWidth; j++) {
         if (_oByteMatrix.get(i, j)) {
            _oGraphics.fillRect(i, j, 1, 1);
         }
      }
   }
      
   var _oOutputStream = new java.io.ByteArrayOutputStream
   Packages.javax.imageio.ImageIO.write(_oImage, "png", _oOutputStream);
   return _oOutputStream.toByteArray();
}


The byteArray can be displayed easily using the imageMedia component (bootstrap components)

The missing link is verifying the token as entered by the user against the stored secret and current time
You can retrieve the current token with the function below, this can be used to compare
Code: Select all
function getCurrentToken(_sSecret) {
   var _sToken = null;
   
   var _authenticator = new Packages.com.warrenstrange.googleauth.GoogleAuthenticator();
   
   if(_authenticator) {
      // calc current time token
      _sToken = _authenticator.getTotpPassword(_sSecret, new Date().valueOf());
   }
   
   return _sToken
}


Hope this helps
Marc Boegem
Solutiative / JBS Group, Partner
• Servoy Certified Developer
• Servoy Valued Professional
• Freelance Developer

Image

Partner of Tower - The most powerful Git client for Mac and Windows
User avatar
mboegem
 
Posts: 1742
Joined: Sun Oct 14, 2007 1:34 pm
Location: Amsterdam

Re: 2 Factor Authentification

Postby nam.le » Mon Nov 07, 2022 2:05 pm

Dear Steve, dear Marc,
many thanks for your support.
I will try the Marc approach. Hopefully it will work.
Many thanks again
Best regards
Nam
nam.le
 
Posts: 22
Joined: Thu Jan 21, 2021 1:32 pm

Re: 2 Factor Authentification

Postby nam.le » Mon Nov 07, 2022 3:49 pm

Hi Marc,
i tried the code you posted.
However, when excecuting the line
"var _authenticator = new Packages.com.warrenstrange.googleauth.GoogleAuthenticator();"
I got the error:
ERROR com.servoy.j2db.util.Debug - TypeError: [JavaPackage com.warrenstrange.googleauth.GoogleAuthenticator] is not a function, it is object.
Do you know, what is the problem here.
Best regards
Nam
nam.le
 
Posts: 22
Joined: Thu Jan 21, 2021 1:32 pm

Re: 2 Factor Authentification

Postby mboegem » Mon Nov 07, 2022 4:48 pm

This looks like missing libraries, so:

Did you actually download the 2 jar files and put them in the application_server/plugins folder?
If so, you probably didn't restart after adding the jars, which is essential for Servoy to load them.
Marc Boegem
Solutiative / JBS Group, Partner
• Servoy Certified Developer
• Servoy Valued Professional
• Freelance Developer

Image

Partner of Tower - The most powerful Git client for Mac and Windows
User avatar
mboegem
 
Posts: 1742
Joined: Sun Oct 14, 2007 1:34 pm
Location: Amsterdam

Re: 2 Factor Authentification

Postby nam.le » Mon Nov 07, 2022 7:24 pm

Hi Marc,
i found my mistake.
I forgot to export the new files in the plugins folder when creating the war export for the solution.
Best regards and many thanks for your quick help
Nam
nam.le
 
Posts: 22
Joined: Thu Jan 21, 2021 1:32 pm

Re: 2 Factor Authentification

Postby nam.le » Mon Nov 07, 2022 9:29 pm

Hi Marc,
now I have everything working.
Your function generateVerifcationQRq() is so convient to use as the byte array be used directly to show the QR code in the web-component Image.
It really a great help for me !!
Many thanks for your help, I really appreciate it.
Best regards
Nam
nam.le
 
Posts: 22
Joined: Thu Jan 21, 2021 1:32 pm

Re: 2 Factor Authentification

Postby steve1376656734 » Tue Nov 08, 2022 12:34 pm

Hi Nam,

One other enhancement you might want to consider is validating the user entered token against the previous token as well as the current one. This handles the edge case where the token changes between the user entering it and you validating it and provides a better user experience. To do this you could either create a getPreviousToken(_sSecret) method or return an object from getCurrentToken(_sSecret) that includes both the current and previous token. A sample using the object approach would be (not tested but should work):
Code: Select all
/**
* Return both the current and previous tokens for validation
* @param {String} _sSecret
*
* @return {{currentToken: Number, previousToken: Number}}
* /
function getCurrentToken(_sSecret) {
   /** @type {{currentToken: Number, previousToken: Number}} */
   var _oResult = {};
   _oResult.currentToken = null;
   _oResult.previousTken = null;

   var _authenticator = new Packages.com.warrenstrange.googleauth.GoogleAuthenticator();
   
   if(_authenticator) {
      // calc current and previous time token
      _oResult.currentToken = _authenticator.getTotpPassword(_sSecret, new Date().valueOf());
     _oResult.previousToken = _authenticator.getTotpPassword(_sSecret, new Date().valueOf() - 30000); // Subtract 30 seconds to get previous token
   }
   
   return _oResult;
}


@mboegem - great tip on the QR code generator - thanks.

Steve
Steve
SAN Developer
There are 10 types of people in the world - those that understand binary and those that don't
steve1376656734
 
Posts: 327
Joined: Fri Aug 16, 2013 2:38 pm
Location: Ashford, UK

Re: 2 Factor Authentification

Postby mboegem » Tue Nov 08, 2022 1:54 pm

steve1376656734 wrote:One other enhancement you might want to consider is validating the user entered token against the previous token as well as the current one.


Hi Steve, great tip indeed!

In my final code I am calculating 3 tokens:
1) token for current time
2) token for current time minus 10 sec.
3) token for current time plus 10 sec.

The result of this is 3 tokens of which at least 2 are always the same.
This way the validity of the 'current token' that the user will find on his device will be extended to 40 seconds instead of the normal 30 seconds.
In real life this turns out to be enough to rule out any slight time differences between the client and server.
Marc Boegem
Solutiative / JBS Group, Partner
• Servoy Certified Developer
• Servoy Valued Professional
• Freelance Developer

Image

Partner of Tower - The most powerful Git client for Mac and Windows
User avatar
mboegem
 
Posts: 1742
Joined: Sun Oct 14, 2007 1:34 pm
Location: Amsterdam


Return to Programming with Servoy

Who is online

Users browsing this forum: No registered users and 12 guests