Customization Guide

Nova2FA is designed to be completely customizable. This guide shows you how to adapt it to your needs.

Template Customization

Understanding Template Structure

Nova2FA provides unstyled templates with minimal CSS. All templates extend from a base template that you can override.

Template hierarchy:

nova2fa/base_2fa.html (Base template)
    ├── nova2fa/settings.html (2FA settings page)
    ├── nova2fa/setup.html (Method selection)
    ├── nova2fa/setup_totp.html (TOTP setup)
    ├── nova2fa/verify.html (Verification page)
    ├── nova2fa/verify_email_otp.html (Email verification during setup)
    ├── nova2fa/disable.html (Disable 2FA)
    ├── nova2fa/backup_codes.html (View backup codes)
    └── nova2fa/change_method.html (Change method)

Method 1: Override Base Template

The simplest way to customize is to override the base template with your own.

Create templates/nova2fa/base_2fa.html in your project:

{% extends "base.html" %}

{% block content %}
<div class="container my-5">
    {% 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 %}

Now all Nova2FA pages will use your base template and styling!

Method 2: Override Individual Templates

You can override any specific template by creating it in your project's template directory.

Example: Custom settings page

Create templates/nova2fa/settings.html:

{% extends "base.html" %}
{% load static %}

{% block title %}Security Settings{% endblock %}

{% block content %}
<div class="container">
    <div class="row">
        <div class="col-md-8 offset-md-2">
            <h1 class="mb-4">Two-Factor Authentication</h1>

            {% if two_factor_settings.is_enabled %}
            <div class="card border-success">
                <div class="card-body">
                    <h5 class="card-title">
                        <i class="bi bi-shield-check"></i> 2FA Enabled
                    </h5>
                    <p class="card-text">
                        <strong>Method:</strong> {{ two_factor_settings.get_method_display }}
                    </p>
                    <p class="card-text">
                        <strong>Last Verified:</strong> 
                        {{ two_factor_settings.last_verified|date:"F d, Y" }}
                    </p>

                    <div class="btn-group" role="group">
                        <a href="{% url 'nova2fa:change_method' %}" 
                           class="btn btn-outline-primary">
                            Change Method
                        </a>
                        <a href="{% url 'nova2fa:view_backup_codes' %}" 
                           class="btn btn-outline-secondary">
                            Backup Codes
                        </a>
                        <a href="{% url 'nova2fa:disable' %}" 
                           class="btn btn-outline-danger">
                            Disable
                        </a>
                    </div>
                </div>
            </div>
            {% else %}
            <div class="card border-warning">
                <div class="card-body">
                    <h5 class="card-title">
                        <i class="bi bi-shield-exclamation"></i> 2FA Not Enabled
                    </h5>
                    <p class="card-text">
                        Protect your account with two-factor authentication.
                    </p>
                    <a href="{% url 'nova2fa:setup' %}" 
                       class="btn btn-primary">
                        Enable Two-Factor Authentication
                    </a>
                </div>
            </div>
            {% endif %}
        </div>
    </div>
</div>
{% endblock %}

Method 3: Add Custom CSS Classes

Nova2FA templates use prefixed CSS classes (nova2fa-*) that you can style:

/* static/css/nova2fa-custom.css */

.nova2fa-card {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 10px;
    box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}

.nova2fa-button {
    background: #667eea;
    border: none;
    padding: 12px 30px;
    border-radius: 5px;
    transition: all 0.3s;
}

.nova2fa-button:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}

.nova2fa-input {
    border: 2px solid #e2e8f0;
    border-radius: 5px;
    padding: 10px;
    font-size: 16px;
}

.nova2fa-input:focus {
    border-color: #667eea;
    outline: none;
}

Include it in your base template:

{% load static %}
<link rel="stylesheet" href="{% static 'css/nova2fa-custom.css' %}">

Framework Integration Examples

Bootstrap 5

{% extends "base.html" %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card shadow">
                <div class="card-body">
                    {% if messages %}
                    {% for message in messages %}
                    <div class="alert alert-{{ message.tags }} alert-dismissible fade show">
                        {{ message }}
                        <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                    </div>
                    {% endfor %}
                    {% endif %}

                    {% block nova2fa_content %}{% endblock %}
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

Tailwind CSS

{% extends "base.html" %}

{% block content %}
<div class="container mx-auto px-4 py-8">
    <div class="max-w-2xl mx-auto">
        <div class="bg-white rounded-lg shadow-lg p-6">
            {% if messages %}
            <div class="mb-4">
                {% for message in messages %}
                <div class="rounded-md p-4 mb-2 
                    {% if message.tags == 'success' %}bg-green-50 text-green-800{% endif %}
                    {% if message.tags == 'error' %}bg-red-50 text-red-800{% endif %}
                    {% if message.tags == 'warning' %}bg-yellow-50 text-yellow-800{% endif %}
                    {% if message.tags == 'info' %}bg-blue-50 text-blue-800{% endif %}">
                    {{ message }}
                </div>
                {% endfor %}
            </div>
            {% endif %}

            {% block nova2fa_content %}{% endblock %}
        </div>
    </div>
</div>
{% endblock %}

Email Template Customization

Customize the OTP email by overriding the template:

Create templates/nova2fa/emails/otp_email.html:

<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            padding: 0;
        }
        .container {
            max-width: 600px;
            margin: 40px auto;
            background: white;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 30px;
            text-align: center;
            color: white;
        }
        .content {
            padding: 40px;
        }
        .code {
            background: #f8f9fa;
            border: 2px dashed #667eea;
            padding: 20px;
            text-align: center;
            font-size: 32px;
            font-weight: bold;
            letter-spacing: 5px;
            margin: 30px 0;
            border-radius: 5px;
        }
        .footer {
            background: #f8f9fa;
            padding: 20px;
            text-align: center;
            color: #6c757d;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Your Verification Code</h1>
        </div>
        <div class="content">
            <p>Hello {{ user.get_full_name|default:user.username }},</p>
            <p>Your two-factor authentication code is:</p>

            <div class="code">{{ otp }}</div>

            <p>This code will expire in <strong>{{ expiry_minutes }} minutes</strong>.</p>

            <p style="margin-top: 30px;">
                <strong>Security Notice:</strong> If you didn't request this code, 
                please ignore this email and ensure your account is secure.
            </p>
        </div>
        <div class="footer">
            <p>This is an automated message. Please do not reply to this email.</p>
        </div>
    </div>
</body>
</html>

Form Customization

Override forms to add custom widgets or validation:

# myapp/forms.py
from nova2fa.forms import TOTPVerificationForm

class CustomTOTPForm(TOTPVerificationForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Add Bootstrap classes
        self.fields['code'].widget.attrs.update({
            'class': 'form-control form-control-lg text-center',
            'placeholder': '000000',
            'autofocus': True,
        })

Then use it in your custom view or override the template to use it.

URL Customization

You can change the URL prefix from /2fa/ to anything:

# urls.py
urlpatterns = [
    path('security/', include('nova2fa.urls')),  # Now it's /security/
    path('mfa/', include('nova2fa.urls')),       # Or /mfa/
]

Adding Custom Pages

Add your own pages that integrate with Nova2FA:

# myapp/views.py
from django.contrib.auth.decorators import login_required
from nova2fa.models import UserTwoFactorSettings

@login_required
def security_dashboard(request):
    try:
        two_factor = UserTwoFactorSettings.objects.get(user=request.user)
        has_2fa = two_factor.is_enabled
    except UserTwoFactorSettings.DoesNotExist:
        has_2fa = False

    context = {
        'has_2fa': has_2fa,
        # Add more security metrics
    }
    return render(request, 'myapp/security_dashboard.html', context)

Translation/Internationalization

Nova2FA is translation-ready. Add translations in your project:

# settings.py
LANGUAGE_CODE = 'es'  # Spanish

USE_I18N = True

Create translation files:

django-admin makemessages -l es
# Edit locale/es/LC_MESSAGES/django.po
django-admin compilemessages

JavaScript Enhancement

Add JavaScript for better UX:

<!-- templates/nova2fa/setup_totp.html -->
{% extends "nova2fa/base_2fa.html" %}

{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
    const codeInput = document.querySelector('[name="code"]');

    // Auto-submit when 6 digits entered
    codeInput.addEventListener('input', function(e) {
        if (this.value.length === 6) {
            this.form.submit();
        }
    });

    // Auto-focus on page load
    codeInput.focus();
});
</script>
{% endblock %}

Next Steps