Using Google Authenticator For Your Website

Google has started offering two-factor authentication for Google logins, using Google Authenticator. They have applications available for iPhone, Android, and Blackberry that give time-based passwords based on the proposed TOTP (Time-based One Time Password) draft standard.

The Google code provides a command line program that can generate secret keys as well as a PAM module, but it turns out to be very little code to authenticate a TOTP, thereby providing two-factor authentication to your website very easily.

To give the user the key, you’ll need to generate a cryptographically-secure 10 byte random key, presented to the user as a base32 16-character string. They can either enter this string directly, or you can use Google charts to provide a barcode that they can scan into the Google Authenticator application:

def get_barcode_image(username, domain, secretkey):
    url = "https://www.google.com/chart"
    url += "?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/"
    url += username + "@" + domain + "%3Fsecret%3D" + secretkey
    return url

For an example of what a code looks like, click here, or, look below:

After the user has a secret key from you and has entered it into Google Authenticator either by typing it in directly or scanning in the barcode, you have to be able to verify the key during login (for example). The code to authenticate is only a few lines in Python:

import time
import struct
import hmac
import hashlib
import base64
 
def authenticate(secretkey, code_attempt):
    tm = int(time.time() / 30)
 
    secretkey = base64.b32decode(secretkey)
 
    # 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(secretkey, 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) == str(code_attempt):
            return True
 
    return False