Private
Public Access
1
0

new: usr: implement the 2FA authentication (TOTP and backup codes) #4

This commit is contained in:
2026-04-12 17:55:57 +02:00
parent 0144a3953c
commit fb8a54f687
23 changed files with 1603 additions and 266 deletions

View File

@@ -4,18 +4,6 @@
{% block body %}
<div class="profile-page">
<div class="profile-header">
<div class="profile-avatar">
{{ app.user.username|slice(0, 2)|upper }}
</div>
<div class="profile-info">
<h1 class="profile-name">{{ app.user.username }}</h1>
<p class="profile-role">
<i class="fa fa-lock"></i> Security Settings
</p>
</div>
</div>
<div class="profile-actions">
<a href="{{ path('MineSeekerBundle_profile') }}" class="profile-action-btn">
<i class="fa fa-chevron-left"></i> Back to Profile
@@ -39,6 +27,83 @@
}|json_encode|e('html') }}"
></div>
</div>
<div class="profile-section">
<h2 class="profile-section__title">
<i class="fa fa-shield"></i> Two-Factor Authentication
</h2>
<p class="profile-section__description">
Add an extra layer of security by requiring a one-time code from your authenticator app each time you sign in
with a password.
</p>
{% set newBackupCodes = app.flashes('2fa_backup_codes') %}
{% if isTotpEnabled %}
<div class="twofa-status twofa-status--enabled">
<i class="fa fa-check-circle"></i>
Two-factor authentication is <strong>active</strong>.
</div>
{% if newBackupCodes|length > 0 %}
<div class="twofa-backup-reveal">
<p class="twofa-backup-reveal__warning">
<i class="fa fa-exclamation-triangle"></i>
Save these backup codes now — they will not be shown again.
</p>
<div class="twofa-backup-reveal__grid">
{% for code in newBackupCodes[0] %}
<code class="twofa-backup-code">{{ code }}</code>
{% endfor %}
</div>
</div>
{% endif %}
<div class="twofa-actions">
<form method="post" action="{{ path('MineSeekerBundle_2fa_disable') }}" class="twofa-actions__form">
<input type="hidden" name="_token" value="{{ csrf_token('2fa_disable') }}"/>
<button type="submit" class="btn btn--danger btn--sm">
<i class="fa fa-times"></i> Disable 2FA
</button>
</form>
<div class="twofa-backup-meta">
<span class="twofa-backup-meta__count">
<i class="fa fa-life-ring"></i>
{{ backupCodesCount }} backup code{{ backupCodesCount != 1 ? 's' : '' }} remaining
</span>
<form method="post" action="{{ path('MineSeekerBundle_2fa_backup_regenerate') }}">
<input type="hidden" name="_token" value="{{ csrf_token('2fa_backup_regen') }}"/>
<button type="submit" class="btn btn--secondary btn--sm">
<i class="fa fa-refresh"></i> Regenerate codes
</button>
</form>
</div>
</div>
{% else %}
<div class="twofa-status twofa-status--disabled">
<i class="fa fa-times-circle"></i>
Two-factor authentication is <strong>not enabled</strong>.
</div>
<form method="post" action="{{ path('MineSeekerBundle_2fa_prepare') }}">
<input type="hidden" name="_token" value="{{ csrf_token('2fa_prepare') }}"/>
<button type="submit" class="btn btn--primary">
<i class="fa fa-shield"></i> Enable 2FA
</button>
</form>
{% endif %}
</div>
<div class="profile-section">
<h2 class="profile-section__title">
<i class="fa fa-key"></i> Password changing
</h2>
<p class="profile-section__description">
The password changing only possible through the Forgot password functionality on the Login page.
The system would log you out anyway after requesting a new password, so this feature wasn't implemented here.
</p>
</div>
</div>
{% endblock %}