Python One Time Password Library
PyOTP is a Python library for generating and verifying one-time passwords, supporting both Time-Based One-Time Passwords (TOTP) from RFC 6238 and HMAC-Based One-Time Passwords (HOTP) from RFC 4226. It is widely used to implement two-factor (2FA) or multi-factor (MFA) authentication in various systems, compatible with apps like Google Authenticator. The library is actively maintained, with its current version being 2.9.0, and follows a regular release cadence.
Warnings
- breaking Python 3.6 support was dropped in pyotp v2.8.0. Users on Python 3.6 or older must upgrade their Python environment or pin pyotp to a version prior to 2.8.0.
- breaking The default and minimum secret lengths were increased in versions 2.5.0 (base32 to 26 chars) and 2.6.0 (base32 to 32 chars, hex to 40 chars) to meet RFC recommendations. Versions 2.4.0 and later will raise an error if a secret is too short. Applications relying on implicitly generated or shorter secrets from older versions might encounter errors.
- gotcha To prevent replay attacks, the RFCs and `pyotp` documentation recommend storing the most recently authenticated timestamp, OTP, or a hash of the OTP in your database and rejecting any OTP that has been used before.
- gotcha For TOTP, accurate time synchronization between the server and the client (authenticator app) is crucial. Significant clock drift can lead to OTPs being incorrectly rejected.
- gotcha As of v2.8.0, OTP generation runs in constant time to mitigate timing side-channel attacks. While this is a security improvement, ensure any custom OTP generation or verification logic in your application also considers constant-time operations if sensitive to such attacks.
Install
-
pip install pyotp
Imports
- TOTP
from pyotp import TOTP
- HOTP
from pyotp import HOTP
- random_base32
import pyotp secret = pyotp.random_base32()
- random_hex
import pyotp secret = pyotp.random_hex()
- parse_uri
import pyotp otp_object = pyotp.parse_uri(uri_string)
Quickstart
import pyotp
import time
# Generate a random base32 secret key
secret = pyotp.random_base32()
print(f"Generated Secret: {secret}")
# Create a TOTP object
totp = pyotp.TOTP(secret)
# Generate a provisioning URI for Google Authenticator (or similar)
# In a real app, 'alice@example.com' would be the user's email
# 'SecureApp' would be the name of your application
uri = totp.provisioning_uri(name="alice@example.com", issuer_name="SecureApp")
print(f"Provisioning URI: {uri}")
# In a real application, you'd render this URI as a QR code for the user to scan.
# For demonstration, we'll manually get a code.
# Simulate getting an OTP code from the user (e.g., from their authenticator app)
current_otp = totp.now()
print(f"Current OTP (will change every 30s): {current_otp}")
# Verify the OTP code
# You might wait a few seconds to demonstrate validity windows
# user_input_otp = input("Enter the OTP from your authenticator app: ")
user_input_otp = current_otp # For demonstration, assume correct input
if totp.verify(user_input_otp):
print("OTP verified successfully!")
else:
print("Invalid OTP.")
# For HOTP (counter-based):
hotp = pyotp.HOTP(secret)
initial_count = 0
first_hotp = hotp.at(initial_count)
print(f"HOTP for count {initial_count}: {first_hotp}")
# Verify HOTP
# In a real app, you'd store and increment the counter after each successful verification
if hotp.verify(first_hotp, initial_count):
print("HOTP verified successfully!")