Tag Archives: 2FA

TOTP and Authenticator Apps for 2FA in Go

Back in 2019, I wrote “Using Time-Based One-Time Passwords for Two-Factor Authentication“, explaining how to allow a server written in C# to generate and verify codes in sync with authenticator apps. In this article, I’ll show how to do the same thing in Go.

How It Works

Logins that use Two-Factor Authentication (2FA) typically rely on the user’s password as the first factor, as well as a second factor that could be anything from an email/SMS to something biometric. Among all these options, the Time-Based One-Time Password (TOTP) protocol is a simple way to provide the benefits of 2FA without the disadvantages of other options (such as SMS gateways being unreliable and expensive, and biometrics being complex to implement).

The way TOTP works is that the client and server each generate a code, and these have to match. The codes are generated based on a shared secret and the current time, so as long as the client and server are using the same secret and generate the codes at roughly the same time, they will match.

The client usually takes the form of an authenticator app – such as Google Authenticator, Microsoft Authenticator, or Authy – on a mobile device. Once it acquires the shared secret from the server – typically by scanning a QR code – it then generates codes every 30 seconds. Usually the server will accept that code during these 30 seconds and also for an additional 30 seconds while the next code is displayed.

This might all sound a little abstract, but it will become clear shortly once we go through implementing TOTP on the server side and testing it out.

TOTP in Go – Getting Started

Before looking at TOTP, let’s first do the usual steps to create a simple program in Go:

  • Create a new folder, e.g. go-otp
  • Create a new file in it, e.g. main.go
  • Run go mod init main
  • Add a basic program stub:
package main

func main() {

}

In this article, we’ll be using the excellent otp library by Paul Querna. Install it as follows:

go get github.com/pquerna/otp

Now we can start adding code in that main() function.

Generating a Shared Secret (Server Side)

To generate a shared secret, you call totp.Generate(), passing in a totp.GenerateOpts object. The Issuer and AccountName are required, and you can set other options if you need to.

	// generate shared secret

	options := totp.GenerateOpts{
		Issuer:      "My Web Application",
		AccountName: "me@example.com",
	}

	key, err := totp.Generate(options)
	if err != nil {
		fmt.Println("Failed to generate shared secret: ", err)
		return
	}

In order to do something with that key object, you have to convert it to base32, and you do that by simply calling its Secret() method:

	secret := key.Secret()
	fmt.Println(secret)

The output will change every time you run this, but the following is an example of what it looks like. In a real implementation, this would be saved in a database for each user.

QS4MXEE5AKT5NAIS74Z2AT3JPAGNDW7V

Generating QR Codes (Server Side)

The next step is to transmit the shared secret to the user. Normally, at some point during the registration process, the server generates the shared secret for the user, and sends it to the frontend, which displays it as a QR code. The user then scans the QR code with their authenticator app.

Although the server has no need to generate QR codes, it’s useful to do this for testing purposes, and it’s certainly faster than typing in the shared secret manually (which apps usually allow as a fallback option).

The otp library’s example code shows how to generate a QR code from the shared secret. Adapting this example a little, we get:

	// generate QR code

	var buf bytes.Buffer
	img, err := key.Image(200, 200)
	if err != nil {
		fmt.Println("Failed to save QR code: ", err)
		return
	}
	png.Encode(&buf, img)
	os.WriteFile("qr-code.png", buf.Bytes(), 0644)

Running this code generates a neat little QR code:

QR code for the shared secret.

Acquiring the Shared Secret (Client Side)

We can now pass the QR-encoded secret to the client. For this, we’ll need an authenticator app. Google Authenticator, Microsoft Authenticator and Authy should all work, but I personally prefer the latter because of its cloud backup feature, which is handy if your phone dies or goes missing.

Once the app is installed, locate the option to add a new account, after which you’ll be able to add a shared secret either by scanning a QR code or typing it manually. Excuse the quality of the next two pictures; none of these authenticator apps allow screenshots to be taken, so the closest thing I could do was take a photo with another phone.

Adding an account in Authy.

Generating Authenticator Codes (Client Side)

Once your app has acquired the shared secret, it will automatically start generating 2FA codes, typically six digits long at 30-second intervals:

Authy generating 2FA codes.

Alternatively, for testing purposes or if you need to develop a client with similar functionality, you can generate similar 2FA codes using the otp library itself. To do this, simply call totp.GenerateCode(), passing in the shared secret you generated earlier, and the current time:

	// generate 2FA code

	secondFactorCode, err := totp.GenerateCode(secret, time.Now())
	if err != nil {
		fmt.Println("Failed to generate 2FA code: ", err)
		return
	}
	fmt.Println(secondFactorCode)

The output is the same kind of six-digit 2FA code that you’d get with an authenticator app, for instance:

605157

In fact, if you run this while you also have the authenticator app set up, you’ll see the exact same codes.

Validating 2FA Codes (Server Side)

To complete the second factor part of the login process, a user grabs a 2FA code from their authenticator app and enters it in the prompt in the web application’s frontend, essentially sending it over to the server. The server’s job is to ensure that the code is valid. The otp library allows us to do this validation using the totp.Validate() function, which takes the input 2FA code, and the shared secret from earlier.

In order to test how this behaves over a period of time, we can do a little infinite loop that requests 2FA codes from the user and then validates them:

	for {
		fmt.Print("Enter 2FA code: ")

		var input2faCode string
		fmt.Scanln(&input2faCode)

		codeIsValid := totp.Validate(input2faCode, secret)
		fmt.Println("Code is valid: ", codeIsValid)
	}

While playing with this, you’ll notice that:

  • Entering a code shown by the authenticator app returns true
  • Entering the same code twice returns true
  • Entering the same code still returns true within 30 seconds after it has stopped displaying in the authenticator app
  • Entering the same code after that returns false
  • Entering anything else (other than the current or previous code) returns false

In theory, you should prevent the application from accepting the same code twice, because they’re supposed to be one-time-passwords. To do this, you’ll need to add additional state and logic to your application. Or, you can accept the security tradeoff knowing that the 2FA codes are only valid for a minute, giving little opportunity for an attacker to exploit them.

Summary

My earlier article showed how easy it is to work with 2FA codes using a TOTP library in C#, and in Go it’s no different. After generating a secret from the otp library, you can either share it with your authenticator app (e.g. by generating a QR code) to generate 2FA codes, or use it to generate 2FA codes directly. The library also allows you to validate the generated 2FA codes, assuming they are valid for two 30-second windows.

In the interest of simplicity, I’ve only shown basic usage of the library. If you have more complex requirements, you can customise the generation of secrets, generation of 2FA codes, and also the validation of 2FA codes.

Finally, one thing to keep in mind is that 2FA becomes a major headache when one of the factors goes missing (e.g. phone is lost or stolen). So you’ll need to consider how to handle this situation (e.g. recovery codes, reset shared secret, etc.), and how to handle normal password resets (you may want to reset the shared secret too).

Using Time-Based One-Time Passwords for Two-Factor Authentication

Introduction

Two-factor authentication (2FA) is becoming more and more important, as its adoption is driven by a need for major software companies to secure their systems against threats, as well as due to legal requirements of strong customer authentication, such as the PSD2 directive that came in force in Europe last month.

2FA can be implemented in a number of ways. Typically, it is a combination of the usual username/password login as well as something else, often being a one-time password (OTP) that is sent via SMS or email, or generated by an algorithm.

In this article, we’ll focus entirely on generating and verifying Time-Based One-Time Passwords (TOTP) using Google Authenticator and the Otp.NET library.

Update 20th October 2019: This also works if you use Microsoft Authenticator instead of Google Authenticator. Microsoft Authenticator requires more permissions on your device, sends usage data to Microsoft by default, and is slightly more confusing because you have to choose the type of account.

Update 22nd October 2019: I discovered another mobile app called Authy, and it works just as well to acquire the TOTP secret and generate codes. It is interesting because it has a mechanism to take encrypted backups in the cloud and synchronise across devices, addressing the problem of when you lose or change your phone.

About TOTP

TOTP is an algorithm used to generate one-time passwords based on a shared secret and the current time. It is defined in RFC6238, and is a variant of the HOTP algorithm (RFC4226) which uses a counter instead of time.

The client and server use the same algorithm, the same shared secret and (roughly) the same time to generate the same code.

TOTP can be thought of as a function that takes the shared secret and current time as inputs, and generates a one-time password as output. Given that the client and server both know the same shared secret, and that their software clocks are more or less in sync without major clock skew, then they would generate the same code. This allows a code generated on a mobile device to be verified on the server side.

Generating a Shared Secret

We will use Otp.NET to perform most operations related to TOTP generation and verification. This can easily be installed in a .NET (Core) console application via NuGet:

Install-Package Otp.NET

It is then really easy to generate and output a shared secret using the following code:

var secret = KeyGeneration.GenerateRandomKey(20);
var base32Secret = Base32Encoding.ToString(secret);
Console.WriteLine(base32Secret);

The secret that we generated on the first line is an array of bytes. However, we output it in base32 encoding. This is important for the next step when we will pass the secret to the mobile device. As I learned the hard way, it does not work if the secret is an arbitrary string and not base32-encoded.

Running the above, I just got the following in the output:

6L4OH6DDC4PLNQBA5422GM67KXRDIQQP

Generating a QR Code for the Secret

Stefan Sundin made this great 2FA QR code generator. The two required fields are the Secret (where we paste the value generated above) and a Label (which is arbitrary and identifies the application — we’ll simply put “MFA Test 1” in there).

The QR code helps to synchronise the secret between the server and the mobile device.

Setting up Google Authenticator

Find Google Authenticator in your phone’s app store and install it. It requires access to your camera as we’ll see in a second.

Get Google Authenticator from your phone’s app store.

After installation and its brief in-built tutorial, you get to the point where you can set up your first TOTP code generator (they call it an “account”):

To synchronise a shared secret onto your mobile device, you can scan a barcode or type in the secret directly.

This step is where you enter the shared secret into Google Authenticator. You can do that by scanning a QR code (first option), or by typing it in (second option). The latter is slow and painful, especially on a mobile device, and should be kept as a fallback in case there is some kind of problem scanning the QR code. Scanning the QR code is really just a convenience mechanism and is an encoded version of the same secret.

Scan the barcode to get the shared secret into Google Authenticator.

Once you’ve scanned the QR code, Google Authenticator has acquired the shared secret and starts generating TOTP codes every 30 seconds:

Google Authenticator is generating TOTP codes.

Since you can have more than one of these code generators in here (for different applications), they come with a label. In this case, you’ll notice that we have “MFA Test 1”, which is exactly what we entered in the Label field when generating the QR code.

Generating TOTP codes from Otp.NET

If you need to generate TOTP codes from .NET code (essentially to do what Google Authenticator is doing), then Otp.NET makes it very easy to do that:

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);
            var code = totp.ComputeTotp();

            Console.WriteLine(code);

The ComputeTotp() method takes an optional DateTime parameter as the current time to use for the code generation algorithm. If not provided, it uses DateTime.UtcNow, which is typically what you want to use.

The TOTP code generated from the C# program (top right) is identical to the one generated from Google Authenticator on my phone (bottom centre).

Since we are using Google Authenticator, we don’t actually need this at all, so this is just something to keep in mind if you ever actually need it. It also gives some assurance that we’re on the right track, because what we’re doing in C# and on the mobile device are evidently well in sync.

Verifying TOTP Codes

Like every other operation we’ve seen, verifying TOTP codes with Otp.NET is also very easy. The following code shows how to do this, although most of the code is actually handling input and output.

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);

            while (true)
            {
                Console.Write("Enter code: ");
                string inputCode = Console.ReadLine();
                bool valid = totp.VerifyTotp(inputCode, out long timeStepMatched,
                    VerificationWindow.RfcSpecifiedNetworkDelay);

                string validStr = valid ? "Valid" : "Invalid";
                var colour = valid ? ConsoleColor.Green : ConsoleColor.Red;
                Console.ForegroundColor = colour;
                Console.WriteLine(validStr);
                Console.ResetColor();
            }

Here’s what it might look like while you test it out repeatedly:

A number of tests show interesting results.

As you can see above, I did a number of things:

  1. I entered two invalid codes, and got invalid responses.
  2. I entered a valid code, and got a valid response as expected.
  3. I waited for a new code to be generated, then entered the same code as before, and it was accepted.
  4. I entered the new code that was generated, and it was validated.
  5. I entered another invalid code, and it was marked as such.

The most interesting part of the above is the third step, and it requires further explanation. Codes are generated in time windows, by default every 30 seconds. That doesn’t necessarily mean that the previous code should be rejected. The time window might have shifted just as the user was typing the code, or there could be network delays, etc. Typically, some leeway is allowed when validating these codes. The RFC recommends allowing codes from one time window in the past or future, and that’s what the value of VerificationWindow.RfcSpecifiedNetworkDelay that we passed in as the third parameter to VerifyTotp() does. If you want, you can pass in something different that is more lenient or more restrictive.

On the other hand, accepting the same code twice is wrong, considering we are supposed to be generating one time passwords. In order to make sure that a code isn’t used twice, we need to store something that we can later check to know whether a code has been used. That’s the reason for the second parameter to VerifyTotp(). It gives us back a number indicating the time step used, so we can save this whenever a code is used, and later check whether the same time step has already been used before.

Assuming a single shared secret, a very quick-and-dirty dummy implementation using a HashSet instead of real persistence could look something like this:

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);

            var usedTimeSteps = new HashSet<long>();

            while (true)
            {
                Console.Write("Enter code: ");
                string inputCode = Console.ReadLine();
                bool valid = totp.VerifyTotp(inputCode, out long timeStepMatched,
                    VerificationWindow.RfcSpecifiedNetworkDelay);

                valid &= !usedTimeSteps.Contains(timeStepMatched);
                usedTimeSteps.Add(timeStepMatched);

                string validStr = valid ? "Valid" : "Invalid";
                var colour = valid ? ConsoleColor.Green : ConsoleColor.Red;
                Console.ForegroundColor = colour;
                Console.WriteLine(validStr);
                Console.ResetColor();
            }

Like this, there’s no way you can ever have the same code be valid twice:

The same code, even within the same time window, is invalid the second time.

Conclusion

In this article we’ve seen how Time-Based One-Time Passwords can be generated and verified. We’ve focused mainly on:

  1. Generating a shared secret using Otp.NET
  2. Bringing it to a mobile device with Google Authenticator
  3. Using Google Authenticator to generate TOTP codes
  4. Using Otp.NET to validate these codes

In a two-factor authentication implementation, this is of course only one of the factors, and usually takes place after a regular username/password login.