Skip to content

How to implement your own QR Code provider

Rob Janssen edited this page Mar 19, 2019 · 5 revisions

This library comes with three 'built-in' QR-code providers. Some TwoFactorAuth constructor overloads accept an IQrCodeProvider argument which lets you specify a built-in or custom QR-code provider. All three built-in providers do a simple HTTP request to retrieve an image using a WebClient and implement the IQrCodeProvider interface which is all you need to implement to write your own QR-code provider.

The default provider is the ImageChartsQrCodeProvider which uses the image-charts.com drop-in replacement for Google Chart Tools to render QR-codes. Then we have the QrServerQrCodeProvider which uses the goqr.me API and finally we have the QRicketQrCodeProvider which uses the QRickit API. All three inherit from a common (abstract) baseclass BaseHttpQrCodeProvider because all three share the same functionality: retrieve an image from a 3rd party over HTTP. All three classes have constructors that allow you to tweak some settings and most, if not all, arguments should speak for themselves. If you're not sure which values are supported, click the links in this paragraph for documentation on the API's that are utilized by these classes.

If you don't like any of the built-in classes, because you don't want to, or can't, rely on external resources for example, or because you're paranoid about sending the TOTP secret to these 3rd parties (which is useless to them since they miss at least one other factor in the MFA process), feel tree to implement your own. The IQrCodeProvider interface couldn't be any simpler. All you need to do is implement 2 methods:

byte[] GetQrCodeImage(string text, int size);
string GetMimeType();

The GetMimeType() method should return the MIME type of the image that is returned by our implementation of GetQrCodeImage(string text, int size). In this example it's simply image/png. The GetQrCodeImage() method is passed two arguments: text and size. The latter, size, is simply the width/height in pixels of the image desired by the caller. The first, text is the text that should be encoded in the QR code. An example of such a text would be:

otpauth://totp/LABEL:alice@google.com?secret=XANIK3POC23RCRYN&issuer=ISSUER

All you need to do is return the QR code as binary image data and you're done. All parts of the text have been escaped for you (but note: you may need to escape the entire text just once more when passing the data to another server as GET-parameter).

Demonstration

Let's try implementing our own!

  1. Create a class MyQRProvider.cs
using System;

namespace MyNameSpace
{
    public class MyQRProvider
    {
    }
}
  1. Implement the IQrCodeProvider interface:
using System;
using TwoFactorAuthNet.Providers.Qr;

namespace MyNameSpace
{
    public class MyQRProvider : IQrCodeProvider
    {
    }
}
  1. To install QrCode.Net, run the following command in the Package Manager Console
PM> Install-Package QrCode.Net

NOTE: Explaining the actual process of generating a QR code is far beyond the scope of this document, so for that we'll use QrCode.Net in this example. However, it is up to you to find a library that suits your needs and demands (or write your own QR code generator if you're up for it!) if you don't like QrCode.Net.

  1. Implement the IQrCodeProvider interface members:
using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Render;
using System.Drawing.Imaging;
using System.IO;
using TwoFactorAuthNet.Providers.Qr;

namespace MyNameSpace
{
    public class MyQRProvider : IQrCodeProvider
    {
        public string GetMimeType()
        {
            return "image/png";
        }

        public byte[] GetQrCodeImage(string text, int size)
        {
            var encoder = new QrEncoder();
            var qrCode = encoder.Encode(text);

            var renderer = new GraphicsRenderer(new FixedCodeSize(size, QuietZoneModules.Two));
            byte[] result;
            using (var stream = new MemoryStream())
            {
                renderer.WriteToStream(qrCode.Matrix, ImageFormat.Png, stream);
                result = stream.ToArray();
            }

            return result;
        }
    }
}

Note that this library does not require an external 3rd party and the QR code generation is handled by the QrCode.Net library. This way we don't need (outgoing) internet access and the shared secret is only known by you. No more external dependencies, no more unnecessary latencies.

  1. Pass your newly created QR code provider to a TwoFactorAuth constructor overload that accepts an IQrCodeProvider argument.
var tfa = new TwoFactorAuth("MyCompany", new MyQRProvider());

From here on the tfa instance will use your QR code provider.

See also:

Clone this wiki locally