feat: Implement TOTP-based Two-Factor Authentication
- Add django-otp and qrcode dependencies - Create comprehensive 2FA views and templates in German - Add 2FA setup, verification, and management interfaces - Implement backup token system with 10 recovery codes - Add TwoFactorMiddleware for session enforcement - Integrate 2FA controls into user navigation menu - Support QR code generation for authenticator apps - Add forms for secure 2FA operations with validation - Configure OTP settings and admin site integration Features: - Optional 2FA (users can enable/disable) - TOTP compatible with Google Authenticator, Authy, etc. - Backup codes for emergency access - German language interface - Session-based 2FA enforcement - Password confirmation for sensitive operations - Production-ready with HTTPS support
This commit is contained in:
@@ -622,6 +622,12 @@
|
||||
<li><a class="dropdown-item" href="{% url 'stiftung:user_detail' user.pk %}">
|
||||
<i class="fas fa-user me-2"></i>Mein Profil
|
||||
</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">Sicherheit</h6></li>
|
||||
<li><a class="dropdown-item" href="{% url 'stiftung:two_factor_setup' %}">
|
||||
<i class="fas fa-shield-alt me-2"></i>2FA verwalten
|
||||
</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% if perms.stiftung.manage_users %}
|
||||
<li><a class="dropdown-item" href="{% url 'stiftung:user_management' %}">
|
||||
<i class="fas fa-users me-2"></i>Benutzerverwaltung
|
||||
|
||||
157
app/templates/stiftung/auth/backup_tokens.html
Normal file
157
app/templates/stiftung/auth/backup_tokens.html
Normal file
@@ -0,0 +1,157 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-key text-success"></i>
|
||||
Backup-Codes für Zwei-Faktor-Authentifizierung
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<h5><i class="fas fa-check-circle"></i> Zwei-Faktor-Authentifizierung aktiviert!</h5>
|
||||
<p class="mb-0">
|
||||
Ihr Konto ist jetzt mit Zwei-Faktor-Authentifizierung geschützt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<h6><i class="fas fa-exclamation-triangle"></i> Wichtig: Backup-Codes sicher speichern</h6>
|
||||
<p class="mb-0">
|
||||
Diese Backup-Codes können Sie verwenden, falls Sie keinen Zugriff auf Ihre
|
||||
Authenticator-App haben. <strong>Speichern Sie diese Codes an einem sicheren Ort!</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Ihre Backup-Codes:</h5>
|
||||
<div class="bg-light p-3 rounded mb-3" id="backup-codes">
|
||||
{% for token in backup_tokens %}
|
||||
<div class="font-monospace fw-bold mb-2">{{ token }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" class="btn btn-outline-primary" onclick="copyBackupCodes()">
|
||||
<i class="fas fa-copy"></i>
|
||||
Codes kopieren
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="printBackupCodes()">
|
||||
<i class="fas fa-print"></i>
|
||||
Codes drucken
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>Verwendung der Backup-Codes:</h5>
|
||||
<ul class="text-muted">
|
||||
<li>Jeder Code kann nur <strong>einmal</strong> verwendet werden</li>
|
||||
<li>Geben Sie den Code anstelle des Authenticator-Codes ein</li>
|
||||
<li>Codes sind 8 Zeichen lang (Buchstaben und Zahlen)</li>
|
||||
<li>Bewahren Sie die Codes sicher und vertraulich auf</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-info small">
|
||||
<h6><i class="fas fa-lightbulb"></i> Empfohlene Aufbewahrung:</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li>In einem Passwort-Manager</li>
|
||||
<li>Ausgedruckt in einem Tresor</li>
|
||||
<li>Verschlüsselt auf einem sicheren Gerät</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="{% url 'stiftung:dashboard' %}" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-home"></i>
|
||||
Weiter zum Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyBackupCodes() {
|
||||
const codes = [
|
||||
{% for token in backup_tokens %}'{{ token }}'{% if not forloop.last %},{% endif %}{% endfor %}
|
||||
];
|
||||
const text = codes.join('\n');
|
||||
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
// Show success message
|
||||
const btn = event.target.closest('button');
|
||||
const originalText = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-check text-success"></i> Kopiert!';
|
||||
btn.classList.add('btn-success');
|
||||
btn.classList.remove('btn-outline-primary');
|
||||
|
||||
setTimeout(function() {
|
||||
btn.innerHTML = originalText;
|
||||
btn.classList.remove('btn-success');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
}, 2000);
|
||||
}).catch(function(err) {
|
||||
alert('Fehler beim Kopieren: ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
function printBackupCodes() {
|
||||
const codes = [
|
||||
{% for token in backup_tokens %}'{{ token }}'{% if not forloop.last %},{% endif %}{% endfor %}
|
||||
];
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Backup-Codes - Stiftung Management System</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||
h1 { color: #333; }
|
||||
.codes { background: #f8f9fa; padding: 20px; border-radius: 5px; }
|
||||
.code { font-family: monospace; font-size: 14px; margin: 10px 0; font-weight: bold; }
|
||||
.warning { color: #d63384; font-weight: bold; margin: 20px 0; }
|
||||
.date { color: #666; font-size: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Backup-Codes für Zwei-Faktor-Authentifizierung</h1>
|
||||
<p><strong>Konto:</strong> ${window.location.hostname}</p>
|
||||
<p class="date"><strong>Generiert am:</strong> ${new Date().toLocaleString('de-DE')}</p>
|
||||
|
||||
<div class="warning">
|
||||
⚠️ WICHTIG: Diese Codes sind vertraulich und können nur einmal verwendet werden!
|
||||
</div>
|
||||
|
||||
<div class="codes">
|
||||
${codes.map(code => '<div class="code">' + code + '</div>').join('')}
|
||||
</div>
|
||||
|
||||
<p><strong>Verwendung:</strong></p>
|
||||
<ul>
|
||||
<li>Verwenden Sie diese Codes, falls Sie keinen Zugriff auf Ihre Authenticator-App haben</li>
|
||||
<li>Geben Sie einen Code anstelle des Authenticator-Codes ein</li>
|
||||
<li>Jeder Code kann nur einmal verwendet werden</li>
|
||||
<li>Bewahren Sie diese Codes sicher auf</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
printWindow.document.close();
|
||||
printWindow.print();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
146
app/templates/stiftung/auth/backup_tokens_manage.html
Normal file
146
app/templates/stiftung/auth/backup_tokens_manage.html
Normal file
@@ -0,0 +1,146 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-key text-primary"></i>
|
||||
Backup-Codes verwalten
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if has_tokens %}
|
||||
<div class="alert alert-info">
|
||||
<h6><i class="fas fa-info-circle"></i> Backup-Codes Status</h6>
|
||||
<p class="mb-0">
|
||||
Sie haben derzeit <strong>{{ token_count }} Backup-Codes</strong> verfügbar.
|
||||
Aus Sicherheitsgründen werden die Codes nicht angezeigt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Neue Backup-Codes generieren</h5>
|
||||
<p class="text-muted">
|
||||
Wenn Sie neue Backup-Codes benötigen (z.B. weil Sie alle verbraucht haben
|
||||
oder sie verloren haben), können Sie neue generieren.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-warning small">
|
||||
<strong>Warnung:</strong> Das Generieren neuer Codes macht alle
|
||||
bisherigen Backup-Codes ungültig.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>Passwort eingeben</h5>
|
||||
<p class="text-muted">
|
||||
Geben Sie Ihr Passwort ein, um neue Backup-Codes zu generieren:
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="regenerate" value="1">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Passwort</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-warning">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
Neue Backup-Codes generieren
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<h6><i class="fas fa-exclamation-triangle"></i> Keine Backup-Codes vorhanden</h6>
|
||||
<p class="mb-0">
|
||||
Sie haben derzeit keine Backup-Codes. Das kann passieren, wenn:
|
||||
</p>
|
||||
<ul class="mb-0 mt-2">
|
||||
<li>Sie alle Codes bereits verwendet haben</li>
|
||||
<li>Die Zwei-Faktor-Authentifizierung neu eingerichtet wurde</li>
|
||||
<li>Die Codes aus anderen Gründen gelöscht wurden</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Neue Backup-Codes generieren</h5>
|
||||
<p class="text-muted">
|
||||
Generieren Sie neue Backup-Codes, um sicherzustellen, dass Sie
|
||||
auch ohne Authenticator-App auf Ihr Konto zugreifen können.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>Passwort eingeben</h5>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="regenerate" value="1">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Passwort</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i>
|
||||
Backup-Codes generieren
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<h6><i class="fas fa-lightbulb"></i> Über Backup-Codes:</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li><strong>Zweck:</strong> Zugriff auf Ihr Konto, wenn die Authenticator-App nicht verfügbar ist</li>
|
||||
<li><strong>Anzahl:</strong> 10 Codes werden generiert</li>
|
||||
<li><strong>Verwendung:</strong> Jeder Code kann nur einmal verwendet werden</li>
|
||||
<li><strong>Format:</strong> 8 Zeichen (Buchstaben und Zahlen)</li>
|
||||
<li><strong>Sicherheit:</strong> Codes sollten sicher aufbewahrt werden</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'stiftung:dashboard' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Zurück zum Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
84
app/templates/stiftung/auth/two_factor_disable.html
Normal file
84
app/templates/stiftung/auth/two_factor_disable.html
Normal file
@@ -0,0 +1,84 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-shield-alt text-warning"></i>
|
||||
Zwei-Faktor-Authentifizierung deaktivieren
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
<h6><i class="fas fa-exclamation-triangle"></i> Warnung</h6>
|
||||
<p class="mb-0">
|
||||
Sie sind dabei, die Zwei-Faktor-Authentifizierung zu deaktivieren.
|
||||
Dies verringert die Sicherheit Ihres Kontos erheblich.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h5>Was wird deaktiviert:</h5>
|
||||
<ul class="text-muted mb-4">
|
||||
<li>Authenticator-App Codes</li>
|
||||
<li>Alle bestehenden Backup-Codes</li>
|
||||
<li>Zusätzliche Sicherheitsebene beim Login</li>
|
||||
</ul>
|
||||
|
||||
<h5>Bestätigung erforderlich</h5>
|
||||
<p class="text-muted">
|
||||
Geben Sie Ihr Passwort ein, um die Zwei-Faktor-Authentifizierung
|
||||
zu deaktivieren:
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="password" class="form-label">Passwort</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
autofocus>
|
||||
<div class="form-text">
|
||||
Ihr aktuelles Konto-Passwort
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-warning btn-lg">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
Zwei-Faktor-Authentifizierung deaktivieren
|
||||
</button>
|
||||
<a href="{% url 'stiftung:dashboard' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Abbrechen
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-info mt-4">
|
||||
<h6><i class="fas fa-lightbulb"></i> Alternative Empfehlung</h6>
|
||||
<p class="mb-0 small">
|
||||
Statt die 2FA zu deaktivieren, können Sie auch:
|
||||
</p>
|
||||
<ul class="mb-0 small mt-1">
|
||||
<li>Neue Backup-Codes generieren, falls Sie den Zugang verloren haben</li>
|
||||
<li>Die 2FA neu einrichten, falls Probleme mit der App bestehen</li>
|
||||
<li>Den Administrator kontaktieren, wenn Sie Hilfe benötigen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
124
app/templates/stiftung/auth/two_factor_manage.html
Normal file
124
app/templates/stiftung/auth/two_factor_manage.html
Normal file
@@ -0,0 +1,124 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-shield-alt text-success"></i>
|
||||
Zwei-Faktor-Authentifizierung verwalten
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<h6><i class="fas fa-check-circle"></i> 2FA ist aktiviert</h6>
|
||||
<p class="mb-0">
|
||||
Ihr Konto ist mit Zwei-Faktor-Authentifizierung geschützt.
|
||||
Sie benötigen bei jeder Anmeldung einen Code aus Ihrer Authenticator-App.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-key"></i>
|
||||
Backup-Codes
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Backup-Codes ermöglichen den Zugriff auf Ihr Konto,
|
||||
wenn Ihre Authenticator-App nicht verfügbar ist.
|
||||
</p>
|
||||
|
||||
{% if backup_token_count > 0 %}
|
||||
<p class="mb-2">
|
||||
<strong>{{ backup_token_count }} Backup-Codes</strong> verfügbar
|
||||
</p>
|
||||
<div class="d-grid">
|
||||
<a href="{% url 'stiftung:backup_tokens' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-eye"></i>
|
||||
Codes verwalten
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning small">
|
||||
<strong>Keine Backup-Codes vorhanden!</strong><br>
|
||||
Es wird empfohlen, Backup-Codes zu generieren.
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<a href="{% url 'stiftung:backup_tokens' %}" class="btn btn-warning">
|
||||
<i class="fas fa-plus"></i>
|
||||
Backup-Codes generieren
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-cog"></i>
|
||||
Einstellungen
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Verwalten Sie Ihre 2FA-Einstellungen oder
|
||||
deaktivieren Sie die Zwei-Faktor-Authentifizierung.
|
||||
</p>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{% url 'stiftung:two_factor_disable' %}" class="btn btn-outline-danger">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
2FA deaktivieren
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<small class="text-muted">
|
||||
<strong>Gerät eingerichtet:</strong><br>
|
||||
Standard TOTP-Device
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<h6><i class="fas fa-lightbulb"></i> Tipps für 2FA:</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li><strong>Authenticator-Apps:</strong> Google Authenticator, Microsoft Authenticator, Authy</li>
|
||||
<li><strong>Backup-Codes:</strong> Bewahren Sie diese sicher auf (z.B. Passwort-Manager)</li>
|
||||
<li><strong>Sicherheit:</strong> 2FA schützt auch bei kompromittierten Passwörtern</li>
|
||||
<li><strong>Neues Gerät:</strong> Bei Gerätewechsel 2FA deaktivieren und neu einrichten</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'stiftung:dashboard' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Zurück zum Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
129
app/templates/stiftung/auth/two_factor_setup.html
Normal file
129
app/templates/stiftung/auth/two_factor_setup.html
Normal file
@@ -0,0 +1,129 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-shield-alt text-primary"></i>
|
||||
Zwei-Faktor-Authentifizierung einrichten
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Schritt 1: Authenticator App installieren</h5>
|
||||
<p class="text-muted">
|
||||
Installieren Sie eine Authenticator-App auf Ihrem Smartphone:
|
||||
</p>
|
||||
<ul class="mb-4">
|
||||
<li><strong>Google Authenticator</strong> (iOS/Android)</li>
|
||||
<li><strong>Microsoft Authenticator</strong> (iOS/Android)</li>
|
||||
<li><strong>Authy</strong> (iOS/Android/Desktop)</li>
|
||||
<li><strong>1Password</strong> (Premium)</li>
|
||||
</ul>
|
||||
|
||||
<h5>Schritt 2: QR-Code scannen</h5>
|
||||
<p class="text-muted">
|
||||
Scannen Sie den QR-Code mit Ihrer Authenticator-App:
|
||||
</p>
|
||||
<div class="text-center mb-4">
|
||||
<img src="{% url 'stiftung:two_factor_qr' %}"
|
||||
alt="QR Code für 2FA Setup"
|
||||
class="img-fluid border rounded"
|
||||
style="max-width: 200px;">
|
||||
</div>
|
||||
|
||||
<details class="mb-4">
|
||||
<summary class="text-muted small">Manueller Setup-Code anzeigen</summary>
|
||||
<div class="mt-2 p-2 bg-light rounded">
|
||||
<small class="font-monospace">{{ device.key }}</small>
|
||||
</div>
|
||||
<small class="text-muted d-block mt-1">
|
||||
Falls der QR-Code nicht funktioniert, können Sie diesen Code manuell eingeben.
|
||||
</small>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>Schritt 3: Bestätigen</h5>
|
||||
<p class="text-muted">
|
||||
Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein:
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="token" class="form-label">Bestätigungscode</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="token"
|
||||
name="token"
|
||||
placeholder="000000"
|
||||
maxlength="6"
|
||||
pattern="[0-9]{6}"
|
||||
required
|
||||
autocomplete="off">
|
||||
<div class="form-text">
|
||||
Der Code wechselt alle 30 Sekunden.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-check"></i>
|
||||
Zwei-Faktor-Authentifizierung aktivieren
|
||||
</button>
|
||||
<a href="{% url 'stiftung:dashboard' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-times"></i>
|
||||
Abbrechen
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<h6><i class="fas fa-info-circle"></i> Wichtige Hinweise:</h6>
|
||||
<ul class="mb-0 small">
|
||||
<li>Nach der Aktivierung erhalten Sie Backup-Codes für den Notfall</li>
|
||||
<li>Bewahren Sie diese Backup-Codes sicher auf</li>
|
||||
<li>Sie benötigen bei jeder Anmeldung einen Code aus der App</li>
|
||||
<li>Die Zwei-Faktor-Authentifizierung erhöht die Sicherheit Ihres Kontos erheblich</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Auto-focus on token input
|
||||
const tokenInput = document.getElementById('token');
|
||||
if (tokenInput) {
|
||||
tokenInput.focus();
|
||||
|
||||
// Auto-submit when 6 digits entered
|
||||
tokenInput.addEventListener('input', function() {
|
||||
if (this.value.length === 6 && /^\d{6}$/.test(this.value)) {
|
||||
// Small delay to allow user to see complete input
|
||||
setTimeout(() => {
|
||||
this.closest('form').submit();
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
89
app/templates/stiftung/auth/two_factor_verify.html
Normal file
89
app/templates/stiftung/auth/two_factor_verify.html
Normal file
@@ -0,0 +1,89 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header text-center">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-shield-alt text-primary"></i>
|
||||
Zwei-Faktor-Authentifizierung
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-center text-muted mb-4">
|
||||
Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein
|
||||
oder verwenden Sie einen Backup-Code.
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if next %}
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="otp_token" class="form-label">Authentifizierungscode</label>
|
||||
<input type="text"
|
||||
class="form-control form-control-lg text-center"
|
||||
id="otp_token"
|
||||
name="otp_token"
|
||||
placeholder="000000"
|
||||
maxlength="8"
|
||||
required
|
||||
autocomplete="off"
|
||||
autofocus>
|
||||
<div class="form-text text-center">
|
||||
6-stelliger Code aus der App oder 8-stelliger Backup-Code
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
Bestätigen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<details>
|
||||
<summary class="text-muted small">Probleme beim Anmelden?</summary>
|
||||
<div class="mt-2 small text-muted">
|
||||
<p>Falls Sie keinen Zugriff auf Ihre Authenticator-App haben:</p>
|
||||
<ul class="text-start">
|
||||
<li>Verwenden Sie einen der 8-stelligen Backup-Codes</li>
|
||||
<li>Kontaktieren Sie den Administrator</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tokenInput = document.getElementById('otp_token');
|
||||
if (tokenInput) {
|
||||
// Auto-submit when 6 digits entered (TOTP) or 8 characters (backup code)
|
||||
tokenInput.addEventListener('input', function() {
|
||||
const value = this.value.trim();
|
||||
if ((value.length === 6 && /^\d{6}$/.test(value)) ||
|
||||
(value.length === 8 && /^[a-f0-9]{8}$/i.test(value))) {
|
||||
// Small delay to allow user to see complete input
|
||||
setTimeout(() => {
|
||||
this.closest('form').submit();
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user