When we decided that we were going to add two-factor authentication to GoSquared, we knew it was a big task to get it right. There are near infinite ways that it can be set up with any combination of SMS, Google Authenticator, backup codes, HOTP, TOTP, backup phones and much more. Deciding what functionality and features to include took a lot of inspiration – we knew we weren’t reinventing the wheel but wanted to keep our login process beautiful and easy whilst improving security.
In this post, we’re going to analyse how we built our two-factor authentication system and the decisions we made to give our users the best experience possible while maintaining great security.
How does 2FA actually work?
Each user who activates 2FA has a cryptographically strong key (shared secret) generated for them. This is then securely stored for future use as the key to a SHA1 HMAC. To generate codes, we also need a counter – in some cases this is the current timestamp rounded down to an interval (known as TOTP), in others it’s a incrementing counter and sometimes it’s a random number. We update the HMAC with this counter and convert it to a 4-byte integer. To verify an authentication code, we generate it again using the same counter and check that the two codes are the same.
Features and Functionality
From our own personal experiences, we knew that using a third-party service wasn’t good enough – nobody wants to install a new application or sign up for a new service just so they can login to your app (MailChimp are an example of this, requiring AlterEgo). Once we’d decided to build the system internally, we could choose what features to include to best suit our users, rather than moulding ourselves around another service.
Getting an SMS sent your phone with an authentication code to let you log in is the simplest form of two-factor authentication, and also the bare minimum. Your phone number can stay the same even if you replace or lose your phone, which means it’s a more reliable form of authentication than using an application.
The codes sent to your mobile are generated via HOTP using a random counter. The counter is then stored and the code is valid for a few minutes, or until it’s used, whichever is sooner.
Authenticator applications usually work by scanning a QR code with the secret key to generate authentication codes. Instead of using a random counter to generate the HOTP, they use the current timestamp rounded down to the nearest 30 seconds – known as TOTP. Each code is therefore valid for 30 seconds, regardless of whether it is used or not. Unfortunately, not everybody has their mobile device’s clock synced perfectly and we need to allow for network latency and other edge cases so we allow a drift of one code either side, meaning each code is actually valid for 1 minute 30 seconds. This does reduce the security of the codes a little, but it’s worth it for the improved experience.
Don’t lose your phone!
Neither SMS-based or app-based authentication are good enough alone. In my case at least, it’s not a matter of “if” you lose or break your phone, it’s a matter of “when”1 – so our backup access methods have to be great.
Backup codes are implemented by most companies that offer 2FA. They can allow access to your account if your mobile device has been lost or is unavailable. There are two main methods of implementing backup codes:
- Multiple backup codes: Each user has around 6-10 backup codes which can be used at any time to access their account. After a code is used, it’s invalidated so it can’t be used again.
- A single backup code: Each user has one backup code which is used to access their account AND disable 2FA at the same time.
Having multiple backup codes is great for ensuring you can access your account if your phone has simply run out of battery, or if it has no mobile reception to receive an authentication SMS, whilst a single backup code assumes that you’ve permanently lost access to your mobile device and need to set up 2FA again.
We chose the single code solution, as a lot of users don’t see much value in multiple backup codes and find them confusing. It takes seconds to set up 2FA again if needed, so the simplicity of a single code was the right option for us.
Backup codes are great, but they’re also fairly easy to lose. Who actually writes down the backup codes and keeps them in a secure place? They usually sit in the Downloads folder or maybe in BitTorrent Sync. If you do have them written down, they aren’t going to be accessible if you’re in the office and your codes are at home. This is where fallback phone comes in.
We can send an SMS to a different phone number if you’re unable to use your primary authentication method. This is especially useful if you’re using an authenticator app, which is then accidentally deleted or lost (reformatted/new phone) – the fallback phone can be set up to be your regular phone number. This has come in exceptionally useful when I’ve got a new phone with the same number – hassle-free logins can still be a thing.
The Setup Flow
2FA isn’t something anybody actually wants to set up, and therefore the process to do so must be slick and easy.
Step 1: Enter your password
A very quick and clear step to ensure the user is authorised to enable 2FA.
Step 2: Choose delivery method
As we decided to offer two methods of authentication, we needed a step to choose which one you’d like to use. This step has minimal bloat: a title, two icons for illustration and short descriptions.
Step 3: Set up your method
The next step is to actually set up your device for 2FA.
Step 3a: Authenticator app
With an authenticator app, it’s as simple as scanning the QR code (generated via our 2FA module) and then entering the code it gives you to verify the code was scanned properly. If your phone doesn’t have a working camera, or your app doesn’t support the QR code, we provide a tooltip which displays the secret key for manual entry.
Step 3b: SMS
When using SMS-based authentication, the user has to enter their phone number before we can send a verification code. We list the countries supported by Twilio (who we use to send texts) with the US and UK at the top, as a very large percentage of our users are from there.
Once the SMS has been sent, the verification code input is automatically focussed so the code can be entered with no extra clicks.
This same screen is used for setting up a backup phone.
Little big detail: if you’re in the UK, we’ll text you from a UK phone number, otherwise we’ll use our US number.
Step 4: Enabled! Success!
Excellent! Everything worked perfectly. Of course, we also have error states for if the verification code is incorrect so the user can try again/re-configure.
Step 5: Configuration and backup systems
After 2FA is enabled, users are taken to the configuration screen. This is where they can save their backup code or configure a fallback SMS number, as well as reconfiguring or disabling 2FA.
Kudos to GitHub, Stripe, Google and many others for providing inspiration for our 2FA system.