Skip to main content

TOTP-based Two-Factor Authentication Library for Python

Project description

auth2fa

A flexible and easy-to-use Python library for implementing TOTP-based two-factor authentication with support for multiple storage backends.


Features

  • ✅ TOTP-based 2FA (Time-based One-Time Password)
  • ✅ QR code generation for easy setup
  • ✅ Recovery codes (8 codes, single-use)
  • ✅ Multiple storage backends (JSON file / SQL database)
  • ✅ Storage interface for custom backends
  • ✅ sqloader integration (optional)
  • ✅ No async dependencies - pure synchronous implementation
  • ✅ Compatible with Google Authenticator, Authy, etc.

Installation

Basic Installation (JSON storage only)

pip install auth2fa

With SQL Storage Support

pip install auth2fa[sql]

Quick Start

JSON Mode (Default)

from auth2fa import TwoFactorAuth

# Initialize with JSON storage (default)
tfa = TwoFactorAuth(issuer="MyApp")

# Setup 2FA for a user
result = tfa.setup("user123", username="john@example.com")
print(result["qr_uri"])          # otpauth:// URI for QR code
print(result["qr_image"])        # Base64 encoded PNG image
print(result["recovery_codes"])  # ['A3F8K2M1', 'B7D2N4P9', ...]

# Activate 2FA (user enters code from authenticator app)
if tfa.activate("user123", "482901"):
    print("2FA activated!")

# Verify during login
if tfa.verify("user123", "173829"):
    print("Login successful!")

SQL Mode (with sqloader)

from auth2fa import TwoFactorAuth
from sqloader import SQLite3  # or MySQL

# Initialize database
sq = SQLite3("database.db", sql_path="./auth2fa/sql")

# Initialize with SQL storage
tfa = TwoFactorAuth(sq=sq, issuer="FileForge")

# Rest of the API is the same
result = tfa.setup("user123", username="shin")
tfa.activate("user123", "482901")
tfa.verify("user123", "173829")

Custom Storage Path

# Use custom JSON file path
tfa = TwoFactorAuth(storage_path="./data/2fa.json", issuer="MyApp")

API Reference

TwoFactorAuth

__init__(issuer="MyApp", storage_path=None, sq=None)

Initialize TwoFactorAuth instance.

Parameters:

  • issuer (str): Application name displayed in authenticator apps
  • storage_path (str, optional): Path to JSON storage file
  • sq (optional): sqloader instance for SQL storage

Storage Priority:

  1. If sq is provided → SQL storage
  2. If storage_path is provided → JSON storage with custom path
  3. Otherwise → JSON storage at ./totp_data.json

setup(user_id, username="")

Set up TOTP for a user.

Returns: dict

{
    "secret": "BASE32SECRET",
    "qr_uri": "otpauth://totp/...",
    "qr_image": "base64encodedPNG...",
    "recovery_codes": ["A3F8K2M1", ...]
}

activate(user_id, code)

Activate TOTP after user verifies the code from their authenticator app.

Returns: bool (True if successful)

verify(user_id, code)

Verify TOTP code during login.

Behavior:

  • Unconfigured users → returns True (pass through)
  • Verifies TOTP code first
  • If TOTP fails, automatically tries recovery code
  • Recovery codes are single-use (automatically removed)

Returns: bool

disable(user_id)

Completely remove TOTP configuration for a user.

is_enabled(user_id)

Check if TOTP is enabled for a user.

Returns: bool

regenerate_recovery_codes(user_id)

Generate new recovery codes (invalidates old ones).

Returns: list

Integration Example

Login Flow with 2FA

from auth2fa import TwoFactorAuth

tfa = TwoFactorAuth(issuer="MyApp")

@router.post("/login")
async def login(credentials):
    user = authenticate(credentials)

    if tfa.is_enabled(str(user.id)):
        return {"status": "totp_required", "temp_token": create_temp_token(user)}

    return {"access_token": create_jwt(user)}

@router.post("/login/verify-totp")
async def verify_totp(temp_token, code):
    user = validate_temp_token(temp_token)

    if tfa.verify(str(user.id), code):
        return {"access_token": create_jwt(user)}

    return {"error": "invalid_code"}

@router.post("/user/setup-2fa")
async def setup_2fa(user):
    result = tfa.setup(str(user.id), username=user.email)

    return {
        "qr_image": result["qr_image"],  # Display as <img src="data:image/png;base64,..." />
        "recovery_codes": result["recovery_codes"]  # Show to user (print/save)
    }

@router.post("/user/activate-2fa")
async def activate_2fa(user, code):
    if tfa.activate(str(user.id), code):
        return {"success": True}
    return {"error": "invalid_code"}

@router.post("/user/disable-2fa")
async def disable_2fa(user):
    tfa.disable(str(user.id))
    return {"success": True}

Storage Backends

JSON Storage (Default)

  • File-based storage with file locking
  • Default path: ./totp_data.json
  • Thread-safe with lock file mechanism
  • Auto-converts user_id to string

SQL Storage (with sqloader)

Requires auth2fa[sql] installation.

Supported Databases:

  • SQLite3
  • MySQL
  • PostgreSQL (via sqloader)

Table Schema:

CREATE TABLE totp_auth (
    user_id VARCHAR(64) PRIMARY KEY,
    secret VARCHAR(64) NOT NULL,
    enabled BOOLEAN DEFAULT FALSE,
    recovery_codes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Custom Storage Backend

Implement BaseStorage interface:

from auth2fa.storage.base import BaseStorage

class CustomStorage(BaseStorage):
    def save(self, user_id: str, data: dict) -> None:
        # Your implementation
        pass

    def get(self, user_id: str) -> dict | None:
        # Your implementation
        pass

    def delete(self, user_id: str) -> None:
        # Your implementation
        pass

    def exists(self, user_id: str) -> bool:
        # Your implementation
        pass

# Use custom storage
from auth2fa import TwoFactorAuth
tfa = TwoFactorAuth.__new__(TwoFactorAuth)
tfa.issuer = "MyApp"
tfa.storage = CustomStorage()

Data Structure

TOTP data stored for each user:

{
    "secret": "BASE32SECRET",           # TOTP secret key
    "enabled": False,                   # Activation status
    "recovery_codes": [                 # Single-use backup codes
        "A3F8K2M1",
        "B7D2N4P9",
        # ... 8 codes total
    ],
    "created_at": "2026-02-11T18:00:00"  # ISO format timestamp
}

Security Notes

  • Recovery codes are alphanumeric (excluding 0, O, I, 1 for clarity)
  • TOTP uses 30-second time window with ±1 window tolerance
  • Recovery codes are case-insensitive
  • Each recovery code can only be used once
  • File locking prevents race conditions in JSON storage

Requirements

  • Python 3.10+
  • pyotp
  • qrcode
  • Pillow

Optional:

  • sqloader (for SQL storage)

License

MIT License

Links

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

auth2fa-0.1.1.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

auth2fa-0.1.1-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file auth2fa-0.1.1.tar.gz.

File metadata

  • Download URL: auth2fa-0.1.1.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for auth2fa-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9be60783716b5ade4964e7248cc6cff0b1a51ab851f7bc13a15e84ca96cefbf8
MD5 cd21d4ff26a44173a207c23e40f44760
BLAKE2b-256 8a327aba9af0fb41dc701c59e867696d6bfa128dca20cb739d2c71592a43e32f

See more details on using hashes here.

File details

Details for the file auth2fa-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: auth2fa-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for auth2fa-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a98d3cb2083a442b24a8c6936bd689b09a28829b56b06012c6cbb23eb016eb4e
MD5 881b417164664eea6d73d445657f62f2
BLAKE2b-256 71eb83e6b86e559c3b8560ac3e0a1898b9878864bf027adfeeeb6032d1f0f659

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page