Python. Two-Factor Authentication with Google Authenticator

Two-factor authentication has become increasingly common. It certainly improves the security of applications and websites in particular. Today I’ll explain how to add an extra authentication step using TOTP. We’ll use the mobile version of Google Authenticator for Android as the client.
For this, we’ll need two files:
- “totp.py” — the implementation of the TOTP algorithm itself;
- “totp_auth.py” — a helper for generating and validating tokens.
totp.py
"""
Time-based One-time Password Algorithm
Based on the pyotp: https://github.com/nathforge/pyotp
"""
import base64
import hashlib
import hmac
import datetime
import random
import time
import sys
import urllib
def random_base32(length=16, random=random.SystemRandom(),
chars=base64._b32alphabet.values()):
return ''.join(
random.choice(chars)
for i in xrange(length)
)
class OTP(object):
def __init__(self, secret, digits=6):
self.secret = secret
def int_to_bytestring(self, int, padding=8):
"""
Turns an integer to the OATH specified
bytestring, which is fed to the HMAC
along with the secret
"""
result = []
while int != 0:
result.append(chr(int & 0xFF))
int = int >> 8
return ''.join(reversed(result)).rjust(padding, '\0')
def generate_otp(self, input):
"""
@param [Integer] input the number used seed the HMAC
Usually either the counter, or the computed integer
based on the Unix timestamp
"""
hmac_hash = hmac.new(
base64.b32decode(self.secret, casefold=True),
self.int_to_bytestring(input),
hashlib.sha1,
).digest()
offset = ord(hmac_hash[19]) & 0xf
code = ((ord(hmac_hash[offset]) & 0x7f) << 24 |
(ord(hmac_hash[offset + 1]) & 0xff) << 16 |
(ord(hmac_hash[offset + 2]) & 0xff) << 8 |
(ord(hmac_hash[offset + 3]) & 0xff))
# '6' is number of integers in the OTP
return code % 10 ** 6
class TOTP(OTP):
def __init__(self, *args, **kwargs):
"""
@option options [Integer] internval (30) the interval in seconds
This defaults to 30 which is standard.
"""
self.interval = kwargs.pop('interval', 30)
super(TOTP, self).__init__(*args, **kwargs)
def timecode(self, for_time):
i = time.mktime(for_time.timetuple())
return int(i / self.interval)
def now(self):
"""
Generate the current time OTP
@return [Integer] the OTP as an integer
"""
return self.generate_otp(self.timecode(datetime.datetime.now()))
def at(self, date):
"""
Generate the current time OTP
@return [Integer] the OTP as an integer
"""
return self.generate_otp(self.timecode(date))
def verify(self, otp, for_time=None):
"""
Verifies the OTP passed in against the current time OTP
@param [String/Integer] otp the OTP to check against
"""
if for_time is None:
for_time = datetime.datetime.now()
return otp == self.at(for_time)
def provisioning_uri(self, name):
"""
Returns the provisioning URI for the OTP
This can then be encoded in a QR Code and used
to provision the Google Authenticator app
@param [String] name of the account
@return [String] provisioning uri
"""
return 'otpauth://totp/%(name)s?secret=%(secret)s' % {
'name': urllib.quote(name, safe='@'),
'secret': self.secret,
}
if __name__ == "__main__":
secret = "AAAAAAAAAAAAAAAA"
unixtime = 0
if len(sys.argv) > 1:
unixtime = int(sys.argv[1])
if unixtime > 1:
date = datetime.datetime.fromtimestamp(unixtime)
else:
date = datetime.datetime.now()
totp = TOTP(secret)
print "TOTP token for secret '%s' at '%s' is: %s" % (
secret, date, totp.at(date))
totp_auth.py
import totp
import qrcode
import datetime
class TotpAuth(object):
def __init__(self, secret=None):
if secret is None:
secret = totp.random_base32()
self.secret = secret
self.totp = totp.TOTP(secret)
def generate_token(self):
return self.totp.now()
def valid(self, token):
try:
token = int(token)
except ValueError:
return False
now = datetime.datetime.now()
time30secsago = now + datetime.timedelta(seconds=-30)
try:
valid_now = self.totp.verify(token)
valid_past = self.totp.verify(token, for_time=time30secsago)
return valid_now or valid_past
except:
return False
def qrcode(self, username):
uri = self.totp.provisioning_uri(username)
return qrcode.make(uri, box_size=4)
Usage example
Let’s create an isolated environment and install the dependencies:
mkdir ~/work/totp && cd ~/work/totp
wget -O totp.py https://gist.githubusercontent.com/adw0rd/0a61f53dd7a61e5aa65c/raw/totp.py
wget -O totp_auth.py https://gist.githubusercontent.com/adw0rd/0a61f53dd7a61e5aa65c/raw/totp_auth.py
virtualenv --no-site-packages --distribute venv
. venv/bin/activate
pip install qrcode PIL
Let’s create a file called sample.py to demonstrate how it works, with the following contents:
sample.py
from totp_auth import TotpAuth
def main(secret=None):
if not secret:
# WHEN SIGN UP
ta = TotpAuth()
else:
# WHEN SIGN IN
ta = TotpAuth(secret)
# SHOW SECRET AND QR CODE
qr = ta.qrcode('adw0rd@adw0rd.com')
print 'SECRET: ', ta.secret
qr._img.show()
# CHECK TOKEN
while True:
token = raw_input('Please input the token from Google Authenticator: ')
if ta.valid(token):
print "SUCCESS"
else:
print "FAILED"
if __name__ == '__main__':
import sys
main(*sys.argv[1:])
Let’s walk through sample.py. First, the user enrolls in two-factor authentication and the application saves their secret key (SECRET):
- We generate a new secret key (SECRET);
- We display a QR code;
- The user scans it with Google Authenticator;
- They enter the token provided by Google Authenticator into the input form on the website;
- If the user enters the correct token, we save the secret key (SECRET) for that user in the database, so that this key will be used during authentication to validate tokens from Google Authenticator.
Let’s run it:
$ python sample.py
SECRET: UG4QTQ6F4JI4BE3Y

Please input the token from Google Authenticator: 123123
FAILED (тут специально пользователь ввел неверный токен)
Please input the token from Google Authenticator: 613257
SUCCESS (тут пользователь ввел верный токен, сохраняем SECRET в БД)
Now let’s walk through the authentication steps:
- The user visits the website and enters the correct username and password;
- If we can uniquely identify the user and they already have a secret key (SECRET) in the database, we display a QR code based on that secret key;
- They scan the QR code with Google Authenticator, or select the existing account in the app, then enter its current code on the website;
- If they enter the token within the specified time, authentication is considered successful; otherwise, they are prompted to try again.
Let’s run it with the secret key:
$ python sample.py UG4QTQ6F4JI4BE3Y
SECRET: UG4QTQ6F4JI4BE3Y

Please input the token from Google Authenticator: 123123
FAILED (тут специально пользователь ввел неверный токен)
Please input the token from Google Authenticator: 394500
SUCCESS (тут пользователь ввел верный токен, можно его авторизовывать)
We’ve now implemented two-factor authentication based on TOTP and Google Authenticator. In the near future, I’ll try to create a Django application based on this article that will store secret keys in the database and display a QR code when necessary.
Comments are closed. Read the original discussion in Russian →