Implementing Two-Factor Authentication Is Easier Than It Seems

User and password verification with two-factor authentication isn’t as easy to use as plain old user and password, but with smartphones it comes very, very close. At the same time, the security benefits from having another secret stored on a different terminal are massive. Users’ accounts aren’t necessarily compromised if their passwords are.

In my mind, two-factor authentication is one of the least tedious things you can add that adds the most security to user accounts.

“But it’s a pain to add to your application, right?”

No! Just take a look at main() in this example that uses only the Python standard library:

#!/usr/bin/env python
import base64
import hashlib
import hmac
import os
import time
import struct

appName = "MyApp"

def newSecret():
    return base64.b32encode(os.urandom(10))

def getQRLink(name, secret):
    return "https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/{0}%20-%20{1}%3Fsecret%3D{2}".format(name, appName, secret)

def auth(secret, nstr):
    # raise if nstr contains anything but numbers
    int(nstr)
    tm = int(time.time() / 30)
    secret = base64.b32decode(secret)
    # try 30 seconds behind and ahead as well
    for ix in [-1, 0, 1]:
        # convert timestamp to raw bytes
        b = struct.pack(">q", tm + ix)
        # generate HMAC-SHA1 from timestamp based on secret key
        hm = hmac.HMAC(secret, b, hashlib.sha1).digest()
        # extract 4 bytes from digest based on LSB
        offset = ord(hm[-1]) & 0x0F
        truncatedHash = hm[offset:offset+4]
        # get the code from it
        code = struct.unpack(">L", truncatedHash)[0]
        code &= 0x7FFFFFFF;
        code %= 1000000;
        if ("%06d" % code) == nstr:
            return True
    return False

def main():
    # Setup
    name = raw_input("Hi! What's your name? ")
    pw = raw_input("What's your password? ")
    secret = newSecret() # store this with the other account information
    link = getQRLink(name, secret)
    print("Please scan this QR code with the Google Authenticator app:\n{0}\n".format(link))
    print("For installation instructions, see http://support.google.com/accounts/bin/answer.py?hl=en&answer=1066447")
    print("\n---\n")

    # Authentication
    opw = raw_input("Hi {0}! What's your password? ".format(name))
    if opw != pw:
        print("Sorry, that's not the right password.")
    else:
        code = raw_input("Please enter your authenticator code: ")
        if auth(secret, code):
            print("Successfully authenticated! Score!")
        else:
            print("Sorry, that's a fail.")

if __name__ == "__main__":
    main()

Not using Python, and don’t want to port this code? There are many TOTP libraries available that are just as easy to use!

If you don’t implement two-factor authentication in your applications, please consider supporting a federated authentication mechanism like OAuth or OpenID so users can leverage providers that do.

Code based on example by brool.