Private
Public Access
1
0

chg: usr: add modern Webauthn authentication #4

This commit is contained in:
2026-04-12 15:19:03 +02:00
parent acbe9c7f63
commit 0144a3953c
23 changed files with 2845 additions and 13 deletions

View File

@@ -21,6 +21,9 @@
<i class="fa fa-user-circle"></i>
{{ app.user.username }}
</a>
<a href="{{ path('MineSeekerBundle_profile_security') }}" class="hero-auth-btn hero-auth-btn--security">
<i class="fa fa-lock"></i> Security
</a>
<form method="post" action="{{ path('MineSeekerBundle_logout') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('logout') }}"/>
<button type="submit" class="hero-auth-btn hero-auth-btn--out">

View File

@@ -4,7 +4,6 @@
{% block body %}
<div class="auth-page">
{% for message in app.flashes('success') %}
<div class="auth-flash auth-flash--success">
<i class="fa fa-check-circle"></i> {{ message }}
@@ -63,10 +62,16 @@
</div>
</div>
<label class="auth-remember">
<input type="checkbox" name="_remember_me"/>
<span>Remember me</span>
</label>
<div class="auth-below-password">
<label class="auth-remember">
<input type="checkbox" name="_remember_me"/>
<span>Remember me</span>
</label>
<p class="auth-forgot-password">
<a href="{{ path('MineSeekerBundle_forgot_password') }}">Forgot your password?</a>
</p>
</div>
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response"/>
@@ -75,21 +80,28 @@
</button>
</form>
<p class="auth-switch">
<a href="{{ path('MineSeekerBundle_forgot_password') }}">Forgot your password?</a>
</p>
<div class="auth-divider">
<span>or</span>
</div>
<div id="passkey-login-root"
data-api-routes="{{ {
authenticationBegin: path('api_webauthn_authentication_begin'),
authenticationComplete: path('api_webauthn_authentication_complete'),
}|json_encode|e('html') }}"
></div>
<p class="auth-switch">
No account yet?
<a href="{{ path('MineSeekerBundle_register') }}">Create one</a>
</p>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="https://www.google.com/recaptcha/api.js?render={{ recaptcha_site_key }}" async defer></script>
<script>
document.querySelector('.auth-form').addEventListener('submit', function (e) {
@@ -102,5 +114,8 @@
});
});
});
</script>
{{ vite_entry_script_tags('passkey') }}
{% endblock %}

View File

@@ -45,6 +45,12 @@
</div>
</div>
<div class="profile-actions">
<a href="{{ path('MineSeekerBundle_profile_security') }}" class="profile-action-btn">
<i class="fa fa-lock"></i> Security Settings
</a>
</div>
{% if recent|length > 0 %}
<div class="profile-section">
<h2 class="profile-section__title">

View File

@@ -0,0 +1,53 @@
{% extends 'Game/index.html.twig' %}
{% block title %} - Security Settings{% endblock %}
{% 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
</a>
</div>
<div class="profile-section">
<h2 class="profile-section__title">
<i class="fa fa-key"></i> Passkeys (WebAuthn)
</h2>
<p class="profile-section__description">
Passkeys provide a secure, passwordless way to sign in to your account. Manage your registered passkeys below.
</p>
<div id="passkey-manager-root"
data-credentials="{{ credentials|json_encode|e('html') }}"
data-api-routes="{{ {
registrationBegin: path('api_webauthn_registration_begin'),
registrationComplete: path('api_webauthn_registration_complete'),
credentials: path('api_webauthn_credentials'),
}|json_encode|e('html') }}"
></div>
</div>
</div>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
{{ vite_entry_link_tags('passkeyStyle') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ vite_entry_script_tags('passkey') }}
{% endblock %}