Published: September 13, 2022

Implementing TOTP Authentication in Python using PyOTP

Two-factor authentication (2FA) has become essential for securing user accounts in modern applications. Among the various 2FA methods available, Time-Based One-Time Passwords (TOTP) offer an excellent balance of security and user experience.

In this guide, I'll walk you through implementing TOTP authentication in Python using PyOTP, a powerful library that seamlessly integrates with popular authenticator apps like Google Authenticator and Authy. We'll build a complete authentication system that generates temporary passwords expiring every 30-240 seconds.

Understanding TOTP Authentication

PyOTP is a Python library specifically designed for generating and verifying one-time passwords. It enables you to implement robust two-factor or multi-factor authentication methods in web applications and other systems requiring secure user login.

The beauty of TOTP lies in its time-based nature - passwords are generated using a combination of a shared secret key and the current timestamp, ensuring each code is unique and expires quickly.

Building the Authentication Class

Let's create a comprehensive auth_2FA class that handles all our TOTP operations:

auth_2FA.py
import pyotp
import qrcode
 
class auth_2FA:
    """Class for verifying one-time passwords (TOTP: Time-Based One-Time Password Algorithm).
    For use with Google Authenticator and other OTP apps."""
 
    @staticmethod
    def secret_key_gen():
        """Generate a 32-character base32 secret, compatible with Google Authenticator
        and other OTP apps."""
        return pyotp.random_base32()
 
    def gen_qr(self, secret_key: str):
        """Generate provisioning URIs for use with the QR Code scanner built into MFA
        client apps - works with Google Authenticator iPhone and Android app."""
        provisioning_uri = pyotp.totp.TOTP(secret_key).provisioning_uri(
            name='user@example.com',
            issuer_name='YourApp'
        )
        img = qrcode.make(provisioning_uri)
        img.save("./totp_qr.png")
 
    def totp_gen(self, secret_key: str):
        """Generate the Time-Based One-Time Password. Used for development testing."""
        totp = pyotp.TOTP(secret_key)
        current_otp = totp.now()
        print("Current OTP:", current_otp)
        return current_otp
 
    def validate_otp(self, code_to_verify: str, secret_key: str):
        """Verify TOTP for current time. Returns True or False."""
        totp = pyotp.TOTP(secret_key)
        return totp.verify(code_to_verify)

Authentication Flow Implementation

Now that we have our core methods, let's understand the complete authentication workflow:

User Registration Process

When a user registers for an account on your application, you'll store their hashed password securely in the database, just as you would with any regular password-based authentication system. Additionally, generate a Base32 secret using secret_key_gen() to enable TOTP for the user, and save this in your users table.

QR Code Generation

PyOTP works seamlessly with Google Authenticator, Authy, and other OTP apps. The library includes the ability to generate provisioning URIs for use with QR code scanners built into these MFA client apps. The gen_qr() method handles this by using the qrcode module to generate a scannable QR code that users can add to their authenticator app.

TOTP Algorithm

The TOTP algorithm uses HMAC-SHA1 (Hash-based Message Authentication Code with SHA-1) to generate one-time passwords. The algorithm takes the secret key and the current timestamp as input, producing a unique, time-based OTP that changes every 30 seconds.

Validation Process

When users attempt to log in, they provide both their regular password and the current one-time password from their authenticator app. The server uses the stored secret key associated with the user's account and the current time to calculate the expected OTP, then compares it with the entered OTP for authentication.

Handling Time Synchronization

To account for slight variations in time between the server and the user's device, a small grace period is typically allowed during OTP verification. If the current OTP is not accepted, the server may check the OTPs for the previous and next time steps as well, accommodating minor time differences.

Best Practices and Security Considerations

As a developer, you should implement a backup mechanism for users to regain access if they lose their TOTP device. This could include:

  • Backup codes: Pre-generated single-use codes
  • Alternative 2FA methods: SMS or email verification
  • Account recovery process: Secure identity verification procedure

These backup options ensure users aren't permanently locked out of their accounts while maintaining security standards.