Several months ago Eric Peterson published the totp
module, a cfml implementation of Time-based One-time Password. More on TOTP in a future post, but to summarize: TOTP is used for 2-factor authentication, using some secret your app can work together with an external authenticator on a smartphone such as Google Authenticator, MS Authenticator, LastPass authenticator and many other authenticators. Your TOTP app should be able to generate some special url, and a QR code representing this URL, so configuring your authenticator can be as simple as scanning this QR code. The URL code and QR look like this:
So for our qr code, the TOTP module needs some code to generate the bar code. And that’s exactly where Lucee failed.
Too be honest, I was wondering if I should use the TOTP module. I already created my own codebase for totp in another project which did everything I needed. The only thing missing was QR code generation, because in this project the VueJS frontend generated QR codes. But a more recent project was cfml only, so I decided to give the totp
module a try.
Unfortunately I immediately discovered a bug in the module: the bar codes where not generated correctly. I contacted the author on Slack, and we soon discovered, this was only a problem on Lucee, and apparently it was caused by the ImageNew()
function. As I am developing API’s most of the time, I never used this function before, and I guess it is not used by many other people.
So let me show you some code which should generate a QR code image.
In the code snippet below I generate TWO different totp configs, both with a QR code image in the config.qrcode
. Next we try to display the images in two divs.
<cfscript>
//generate a totp config including barcode ( in prc.totpConfig.qrcode )
prc.FirstTotpConfig = getInstance("totp@totp").generate( "testUser", "First Test" );
prc.SecondTotpConfig = getInstance("totp@totp").generate( "testUser", "Second Test" );
dummy=tobase64( prc.SecondTotpConfig.qrcode )
</cfscript>
<cfoutput>
<div class="input-group m-b-20">
<figure class="flex flex-col mb-4 ">
<figcaption class="text-gray-400 text-sm">FIRST config Secret:#prc.FirstTotpConfig.secret#</figcaption>
<img class="mx-auto w-64 h-64" src="data:image/png;base64,#toBase64(prc.FirstTotpConfig.qrCode)#" alt="#prc.FirstTotpConfig.url#" />
</figure>
</div>
<div class="input-group m-b-20">
<figure class="flex flex-col mb-4 ">
<figcaption class="text-gray-400 text-sm">SECOND config Secret:#prc.SecondTotpConfig.secret#/figcaption>
<img class="mx-auto w-64 h-64" src="data:image/png;base64,#toBase64(prc.SecondTotpConfig.qrCode)#" alt="#prc.SecondTotpConfig.url#" />
</figure>
</div>
</cfoutput>
So here are the surprising results.
My first totp qr code is not displaying properly, the second one is ok. The only difference: I am accessing my qrcode CFImage object in my second example before I try to display it. So actually my dummy=tobase64...
call is changing the cfimage object!. This needed some further investigation
What’s happening? The totp
module is using a CFzxing
module which is a wrapper around the java ZXing library for bar code generation. We call the totp
generate method, which does several things, but ultimately calls getBarcodeImage
in the barcode library CFzxing. This module generates a QR code in a java image buffer and this is used to create some CFImage, e.g
/**
* Generates a barcode image with the given contents encoded in it.
*
* @contents The contents to encode.
* @type The barcode type to generate.
* @width The width of the barcode.
* @height The height of the barcode.
*
* @returns A CFML Image variable of the barcode.
*/
public any function getBarcodeImage(
required string contents,
required string type,
required numeric width,
required numeric height
) {
// .....some java calls for bar code generation
local.imageBuffer = local.MatrixToImageWriter.toBufferedImage( local.theMatrix );
return imageNew( local.imageBuffer );
}
This imageNew
() is actually returned as the offending qrcode.
The first time the object is accessed, something is still changing. I am not sure what happens, but I don’t care. It shouldn’t happen at all. If I call a tobase64 three times it should give the same result three times, but the first call is always different. If I apply some other function on my CFImage: same story. The first result is always incorrect. I guess this is a Lucee bug (ACF does not show this behaviour), so here it is https://luceeserver.atlassian.net/browse/LDEV-3964.
The only thing i don’t understand: why was this not discovered before? I think every user of the ImageNew function is Lucee should encounter the same problem. I am probably not the first user of the CFzxing module on lucee…
Although I was quite annoyed the TOTP module didn’t work as expected initially I am very happy with the help Eric Peterson provided!
For now I created a simple workaround for totp
which will show up in the next release (just access the qrcode Image before first use). One of my next blog posts will discuss the actual use of the totp
module for two factor authentication.
Leave a Reply