How to Implement 2FA in Python

Learn how to enhance security with Python by implementing Two-Factor Authentication (2FA) using TOTP and HOTP algorithms. This comprehensive guide covers everything from generating OTPs with pyotp to integrating with authenticator apps.
  · · 8 min read · Updated mar 2024 · Ethical Hacking · Cryptography

Unlock the secrets of your code with our AI-powered Code Explainer. Take a look!

Introduction

In this tutorial, you’ll learn how to implement 2FA (Two-Factor Authentication) using the very able Python. 2FA is a security mechanism that requires users to provide two different authentication factors to verify their identity. This adds an extra layer of security beyond the traditional username and password combination. 

To ensure secure access to a system, you can be authenticated using three major factors:

  1. Something you know, such as a password or PIN.
  2. Something you have, such as a physical token, a smart card, or a mobile device.
  3. Something you are, such as biometrics (fingerprint, retina scan, etc.).

So, using any two of these three factors is 2FA. And yes, using all three ensures more security. Using all three is known as MFA (Multi-Factor Authentication).

Table of contents:

Getting Started

In this tutorial, you will learn how to add OTPs as an extra layer of security to whatever authentication a system may be using. To achieve this in Python, we'll use the pyotp library. We’ll also use the qrcode library to generate QR Codes for our OTP keys.

Let's get started by installing these packages:

$ pip install pyotp qrcode

pyotp is a Python library that provides support for generating and validating OTPs (One-Time Passwords) based on the TOTP (Time-based One-Time Password) and HOTP (HMAC-based One-Time Password) algorithms. 

TOTP generates one-time passwords based on the current time, while HOTP generates them based on a counter value. TOTP is more secure as it nullifies an OTP once its time frame (typically 30 seconds) has passed. Also, HOTP is vulnerable to brute force attacks due to its static nature. We'll see how to implement both.

While the tutorial here leverages the pyotp library for generating OTPs based on TOTP and HOTP algorithms, it's important to recognize that in the broader ecosystem of two-factor authentication, OTPs delivered via SMS or email predominantly follow the TOTP model due to their expiration characteristics. However, system architectures can vary, and some may implement a more HOTP-like mechanism, especially for email-based OTPs, where the sequence or event, rather than strictly time, dictates the OTP's validity.

Related: How to Generate and Read QR Code in Python.

TOTP (Time-based One-Time Password)

Starting with TOTP, open up a new file and name it meaningfully, like totp.py, and include the following code:

import pyotp

# Generate a random key. You can also set to a variable e.g key = "CodingFleet"
key = pyotp.random_base32()
# Make Time based OTPs from the key.
totp = pyotp.TOTP(key)
# Print current key.
print(totp.now())
# Enter OTP for verification
input_code = input("Enter your OTP:")
# Verify OTP
print(totp.verify(input_code))

In this very basic code, which is intended to show you how TOTP works, we are basically generating an OTP with a key and displaying it. After 30 seconds, the key becomes useless. Feel free to try it out:

$ python totp.py

That's my result after entering the OTP within the time frame. If I had entered it after 30 seconds, I would have gotten False as my output.

Learn also: How to Make a Password Generator in Python.

HOTP (HMAC-based One-Time Password)

Moving on with HOTP, open up a new Python file and name it hotp.py:

import pyotp

# Set the key. A variable this time.
key = 'Muhammad'
# Make a HMAC-based OTP
hotp = pyotp.HOTP(key)
# Print results
print(hotp.at(0))
print(hotp.at(1))
print(hotp.at(2))
print(hotp.at(3))
# Set counter
counter = 0
for otp in range(4):
   print(hotp.verify(input("Enter Code: "), counter))
   counter += 1

In this code, we generate and verify HMAC-based one-time passwords (HOTP) with a predefined key, incrementing the counter for each generated OTP and prompting the user to enter the OTP for verification. Also, notice that now our key wasn’t randomly generated; it was defined. You can go either way!

The major difference is that with HOTP, as long as the key is the same, you’ll get the same OTP - It is not time-based. So, there are no prizes for guessing that you must use a strong, secret password. Let's run it:

$ python hotp.py

When testing, enter all OTPs sequentially. Notice I got False when I entered a wrong OTP.

Integrating with Authenticator Apps

Now that we know what OTPs are, and how to use them in Python, let’s build a simple script that integrates with Google Authenticator - We'll be able to view our OTPs using Google Authenticator. However, feel free to test with any Authenticator that lets you scan a QR code!

We’re going to be writing two programs. The first one will generate our key and QR code for us, while the other will do the verification.

So for the first, name it meaningfully like otp_qrcode_and_key.py and insert the following code:

# Program 1: Generate and Save TOTP Key and QR Code
import pyotp
import qrcode

def generate_otp_key():
   # Generate a random key for TOTP authentication.
   return pyotp.random_base32()

def generate_qr_code(key, account_name, issuer_name):
   # Generate a QR code for TOTP authentication.
   uri = pyotp.totp.TOTP(key).provisioning_uri(name=account_name, issuer_name=issuer_name)
   img = qrcode.make(uri)
   img.save('totp_qr.png')
   print("QR Code generated and saved as 'totp_qr.png'.")

# Main code.
# Generate user key.
user_key = generate_otp_key()
print("Your Two-Factor Authentication Key:", user_key)
# Save key to a file for reference purposes
with open('2fa.txt', 'w') as f:
   f.write(user_key)
# Generate QR Code.
generate_qr_code(user_key, 'Muhammad', 'CodingFleet.com')

In this code, we generate a random TOTP key, display it to the user, save it to a file for reference, and generate a QR code for TOTP setup with the account name "Muhammad" and issuer name "CodingFleet.com," saving the QR code as totp_qr.png.

After running this code, you should see a 2fa.txt file containing your key and a totp_qr.png. which is your QR code in your directory.

Please use your Google Authenticator or whatever authentication app you use to scan the QR code. After scanning, your OTPs will now be available on the authenticator app, like so:

This means that to verify my login to my CodingFleet account, I need to enter that OTP.

Now, for the verification program, open another file and name it otp_verification.py:

# Program 2: Verify TOTP Code with Google Authenticator
import pyotp

def simulate_authentication(key):
   # Simulate the process of authenticating with a TOTP code.
   totp = pyotp.TOTP(key)
   print("Enter the code from your Google Authenticator app to complete authentication.")
   user_input = input("Enter Code: ")
   if totp.verify(user_input):
       print("Authentication successful!")
   else:
       print("Authentication failed. Please try again with the right key.")

# Main Code
# The key should be the same one generated and used to create the QR code in Program 1
user_key = open("2fa.txt").read()  # Reading the key from the file generated in Program 1 (otp_qrcode_and_key.py)
simulate_authentication(user_key)

This code simulates the authentication process using the TOTP key (generated in Program 1). It prompts the user to enter a code from their Google Authenticator app, verifies its correctness, and displays the authentication result. We're reading from the 2fa.txt file generated in the first program, let's run it:

$ python otp_verification.py

Notice that I tried using the key in the image of the Authenticator App above, which failed. That’s because that key had already expired (After 30 seconds). So I tried another one, which was still in its validity period, and it worked.

Conclusion

The OTP (One-Time Password) mechanism, when integrated with Google Authenticator (or any authenticator app), works on the principle of TOTP. Even after a significant amount of time has passed, the OTPs generated by the authenticator app remain in sync with the code (or potentially, your server) because both the server and the app use the same secret key and the current time as inputs to generate OTPs. The TOTP algorithm generates new codes at fixed intervals (usually every 30 seconds), ensuring that the code on the user's app matches the server's expected code within the same time frame. This method maintains synchronization between the server and the app, ensuring a secure and dynamic method of authentication that is challenging to compromise.

There you have it. That’s how you can ensure 2FA using OTP in your applications. You can get all the code snippets for this tutorial here.

Also, if you want to learn more about cybersecurity and cryptography, check out our Cryptography with Python eBook.

Happy coding ♥

Let our Code Converter simplify your multi-language projects. It's like having a coding translator at your fingertips. Don't miss out!

View Full Code Auto-Generate My Code
Sharing is caring!



Read Also



Comment panel

    Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!