Dear all,
any of you have experience creating an solution with 2FA ?
Best regards
Nam
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;
}
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();
}
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
}
/**
* 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;
}
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.
mboegem wrote: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
Return to Programming with Servoy
Users browsing this forum: No registered users and 19 guests