Quick Start Tutorial
This tutorial will guide you through setting up Nova2FA in a Django project and enabling your first 2FA authentication.
Prerequisites
Before starting, ensure you have:
- Completed the Installation Guide
- A Django project with user authentication working
- At least one user account to test with
Step 1: Create a Link to 2FA Settings
Add a link to the Nova2FA settings page in your base template or user profile:
<!-- base.html or profile.html -->
{% if user.is_authenticated %}
<nav>
<a href="{% url 'nova2fa:settings' %}">Security Settings</a>
</nav>
{% endif %}
Step 2: Configure Nova2FA
Add configuration to your settings.py:
# Nova2FA Configuration
NOVA2FA_ENABLED_METHODS = ['email', 'totp'] # Enable both methods
NOVA2FA_TOTP_ISSUER = 'MyApp' # Your app name (shown in authenticator apps)
NOVA2FA_VERIFICATION_WINDOW_DAYS = 14 # User stays verified for 14 days
NOVA2FA_EMAIL_OTP_EXPIRY_MINUTES = 10 # Email codes expire after 10 minutes
# Security (v1.1.0+)
NOVA2FA_SECRET_KEY = 'your-secret-encryption-key' # Required for TOTP encryption
NOVA2FA_MAX_ATTEMPTS = 5 # Account lockout after 5 failed attempts
NOVA2FA_LOCKOUT_DURATION_MINUTES = 15 # 15 minute lockout duration
!!! tip "Generate Encryption Key"
Generate a secure key with:
python
from django.core.management.utils import get_random_secret_key
print(get_random_secret_key())
Step 3: Test the Flow
3.1 Enable 2FA
- Start your development server:
python manage.py runserver - Log in to your Django application
- Navigate to
/2fa/settings/ - Click "Enable Two-Factor Authentication"
3.2 Choose Authentication Method
Option A: Email OTP
- Select "Email OTP"
- Click "Continue"
- Check your email (or console if using console backend)
- Enter the 6-digit code
- Click "Verify and Enable"
- Save your backup codes! (v1.1.0: shown only once)
Option B: Authenticator App (TOTP)
- Select "Authenticator App"
- Click "Continue"
- Scan the QR code with Google Authenticator or similar app
- Or manually enter the secret key shown
- Enter the 6-digit code from your app
- Click "Verify and Enable"
- Save your backup codes! (v1.1.0: shown only once)
!!! warning "Save Backup Codes (v1.1.0+)" Backup codes are displayed only once for security. Save them in a secure location (password manager, printed copy in safe, etc.). You won't be able to view them again!
3.3 Test Verification
- Log out of your application
- Log back in with your username and password
- You'll be redirected to
/2fa/verify/ - Enter your 2FA code
- You'll be redirected to your intended destination
Step 4: Understand What Happened
When you enabled 2FA:
# Nova2FA created a UserTwoFactorSettings record
user_settings = UserTwoFactorSettings.objects.get(user=request.user)
print(user_settings.is_enabled) # True
print(user_settings.method) # 'email' or 'totp'
print(user_settings.backup_codes) # List of 8 backup codes
When you verified 2FA:
# Nova2FA set a session variable
request.session['nova2fa_verified_at'] = '2025-01-15T10:30:00'
# And updated the database
user_settings.last_verified = timezone.now()
user_settings.save()
The middleware now checks this on every request:
# Simplified middleware logic
if user.nova2fa_settings.is_enabled:
if not is_verified_in_session(request):
redirect_to_verification()
Step 5: Customize the Experience
5.1 Override Templates
Create templates/nova2fa/settings.html in your project:
{% extends "base.html" %} {% block content %}
<div class="container">
<h1>Security Settings</h1>
{% if two_factor_settings.is_enabled %}
<div class="alert alert-success">Two-Factor Authentication is enabled</div>
<p>Method: {{ two_factor_settings.get_method_display }}</p>
<a href="{% url 'nova2fa:disable' %}" class="btn btn-danger"> Disable 2FA </a>
{% else %}
<div class="alert alert-warning">
Two-Factor Authentication is not enabled
</div>
<a href="{% url 'nova2fa:setup' %}" class="btn btn-primary"> Enable 2FA </a>
{% endif %}
</div>
{% endblock %}
5.2 Add Custom Styling
Create a custom base template at templates/nova2fa/base_2fa.html:
{% extends "base.html" %} {% block content %}
<div class="nova2fa-container">
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %} {% block nova2fa_content %}{% endblock %}
</div>
{% endblock %}
Step 6: Configure Path Protection
By default, Nova2FA protects all authenticated pages. Customize this:
# Protect specific paths only
NOVA2FA_PROTECTED_PATHS = [
'/dashboard/',
'/profile/',
'/settings/',
]
# Exempt certain paths
NOVA2FA_EXEMPT_PATHS = [
'/api/public/',
'/healthcheck/',
]
# Exempt superusers (useful for emergency access)
NOVA2FA_EXEMPT_SUPERUSERS = True
Step 7: Test Backup Codes
- Enable 2FA (if not already enabled)
- Go to
/2fa/settings/ - Click "View Backup Codes"
- Save these codes somewhere safe
- Log out and log back in
- When prompted for 2FA, click "Use a backup code instead"
- Enter one of your backup codes
- You'll be logged in (and that code is now marked as used)
Common Scenarios
Scenario 1: User Lost Their Phone
- User logs in with username/password
- User clicks "Use a backup code instead"
- User enters a backup code
- User is logged in
- User can go to settings and switch to Email OTP
- User generates new backup codes
Scenario 2: User Wants to Switch Methods
- User goes to
/2fa/settings/ - User clicks "Change Method"
- User selects new method
- User completes setup for new method
- Old method is replaced
Scenario 3: User Wants to Disable 2FA
- User goes to
/2fa/settings/ - User clicks "Disable 2FA"
- User confirms by checking the box
- 2FA is disabled (can be re-enabled anytime)
Next Steps
- Read the Configuration Reference for all available options
- Check the Customization Guide to style templates
- Learn about Custom Methods to add SMS, etc.
- Review Security Best Practices
Troubleshooting
2FA Not Triggering
Check your middleware order:
MIDDLEWARE = [
# ...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'nova2fa.middleware.Nova2FAMiddleware', # Must be after auth
# ...
]
Stuck in Verification Loop
Clear your session and try again:
python manage.py shell
>>> from django.contrib.sessions.models import Session
>>> Session.objects.all().delete()
QR Code Not Showing
Ensure Pillow is installed:
pip install Pillow
Email Not Sending
Check Django's email configuration and test manually:
python manage.py shell
>>> from django.core.mail import send_mail
>>> send_mail('Test', 'Message', 'from@example.com', ['to@example.com'])