Python

Add 2FA to Your Python App

A complete guide to integrating Google Authenticator using AuthenticatorAPI.com

Also available in: PHP C# JavaScript Java

This guide covers adding Google Authenticator-compatible TOTP two-factor authentication to Python applications — whether you are using Flask, Django, FastAPI, or a plain script. The only dependency you need is the standard requests library.

👉 Prerequisites: Python 3.7+, requests library (pip install requests). A database (SQLite, PostgreSQL, etc.) to store per-user secrets.
Step-by-Step Integration
1

Generate a Secret Code

Use Python's secrets module (cryptographically secure) to generate a random Base32 secret for each user at the point they enable 2FA.

import secrets
import string

def generate_totp_secret(length: int = 32) -> str:
    """Generate a cryptographically random Base32 secret."""
    # Base32 alphabet: A-Z and 2-7
    alphabet = string.ascii_uppercase + '234567'
    return ''.join(secrets.choice(alphabet) for _ in range(length))

# Example usage
secret = generate_totp_secret()
# e.g. 'JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PX'

# Store encrypted — never store plaintext secrets
from cryptography.fernet import Fernet
cipher = Fernet(ENCRYPTION_KEY)  # key from environment variable
encrypted_secret = cipher.encrypt(secret.encode()).decode()
# Save encrypted_secret to the database for this user
2

Fetch and Display the QR Code

Call the Pair endpoint to get a QR code image, then embed it in your setup page so the user can scan it.

import requests
from urllib.parse import urlencode

def get_qr_code_html(app_name: str, user_email: str, secret: str) -> str:
    """Returns an HTML img tag containing the QR code for pairing."""
    params = {
        'AppName':    app_name,
        'AppInfo':    user_email,
        'SecretCode': secret,
    }
    base_url = 'https://www.authenticatorApi.com/pair.aspx'
    response = requests.get(base_url, params=params, timeout=10)
    response.raise_for_status()
    return response.text  # Returns an <img> tag

# Flask route example
from flask import session, render_template

# @app.route('/settings/2fa/setup')
def setup_2fa():
    secret = generate_totp_secret()
    session['pending_2fa_secret'] = secret  # store until confirmed
    qr_html = get_qr_code_html('MyApp', session['user_email'], secret)
    return render_template('2fa_setup.html', qr=qr_html, secret=secret)
⚠ Only save the secret to the database after the user successfully verifies their first code. This prevents orphaned secrets from abandoned setups.
3

Validate the PIN at Login

Once the user has set up 2FA, validate every login by calling the Validate endpoint.

def validate_totp_pin(pin: str, secret: str) -> bool:
    """Returns True if the 6-digit pin is valid for the given secret."""
    if not pin.isdigit() or len(pin) != 6:
        return False

    params = {'Pin': pin, 'SecretCode': secret}
    response = requests.get(
        'https://www.authenticatorApi.com/Validate.aspx',
        params=params,
        timeout=10
    )
    response.raise_for_status()
    return response.text.strip().lower() == 'true'

# Flask login route example
from flask import request, redirect, url_for, flash

# @app.route('/login/verify', methods=['POST'])
def verify_2fa():
    pin = request.form.get('pin', '')
    secret = get_user_secret(session['user_id'])  # decrypt from DB

    try:
        valid = validate_totp_pin(pin, secret)
    except requests.RequestException:
        flash('Could not reach authentication service. Try again.', 'error')
        return redirect(url_for('verify_2fa_form'))

    if valid:
        session['2fa_verified'] = True
        return redirect(url_for('dashboard'))
    else:
        flash('Invalid code. Please try again.', 'error')
        return redirect(url_for('verify_2fa_form'))
4

Protect Routes with a Decorator

In Flask, create a decorator that enforces 2FA verification before allowing access to protected views.

from functools import wraps
from flask import session, redirect, url_for

def require_2fa(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('user_id'):
            return redirect(url_for('login'))
        if user_has_2fa(session['user_id']) and not session.get('2fa_verified'):
            return redirect(url_for('verify_2fa_form'))
        return f(*args, **kwargs)
    return decorated_function

# Usage:
@app.route('/dashboard')
@require_2fa
def dashboard():
    return render_template('dashboard.html')
Django Integration

For Django, the same logic applies but using Django's ORM and middleware patterns.

# views.py
import requests
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.contrib import messages
from .models import UserProfile

@login_required
def verify_2fa(request):
    if request.method == 'POST':
        pin    = request.POST.get('pin', '')
        profile = UserProfile.objects.get(user=request.user)
        secret  = profile.get_decrypted_totp_secret()

        resp = requests.get(
            'https://www.authenticatorApi.com/Validate.aspx',
            params={'Pin': pin, 'SecretCode': secret},
            timeout=10
        )

        if resp.text.strip().lower() == 'true':
            request.session['2fa_verified'] = True
            return redirect('dashboard')
        else:
            messages.error(request, 'Invalid code. Please try again.')

    return render(request, 'auth/verify_2fa.html')

# middleware.py — enforce 2FA on all protected views
class TwoFactorMiddleware:
    EXEMPT_PATHS = ['/login/', '/login/verify/', '/logout/']

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if (request.user.is_authenticated
                and request.path not in self.EXEMPT_PATHS
                and user_has_2fa(request.user)
                and not request.session.get('2fa_verified')):
            return redirect('/login/verify/')
        return self.get_response(request)
FastAPI Integration
from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import OAuth2PasswordBearer
import httpx

app = FastAPI()

async def validate_totp(pin: str, secret: str) -> bool:
    async with httpx.AsyncClient() as client:
        response = await client.get(
            'https://www.authenticatorApi.com/Validate.aspx',
            params={'Pin': pin, 'SecretCode': secret},
            timeout=10.0
        )
    return response.text.strip().lower() == 'true'

@app.post('/auth/verify-2fa')
async def verify_2fa(pin: str, current_user = Depends(get_current_user)):
    secret = await get_user_secret(current_user.id)
    if not await validate_totp(pin, secret):
        raise HTTPException(status_code=401, detail='Invalid 2FA code')
    return {'status': 'verified'}
Security Checklist for Python