Files
biiproject-kit-v1/resources/views/profile/partials/passkey-registration-form.blade.php
T

122 lines
5.8 KiB
PHP

<section>
<div class="card border-0 shadow-sm mb-3">
<div class="card-body">
<header>
<h4 class="fw-bold mb-3">
<i class="bi bi-fingerprint me-2"></i> {{ __('Passkeys (WebAuthn)') }}
</h6>
<p class="text-secondary small mb-4">
{{ __('Register your biometrics (Fingerprint, FaceID) or security keys to login securely without a password.') }}
</p>
</header>
<div id="passkey-list" class="mb-4">
@if(Auth::user()->webAuthnCredentials->count() > 0)
<div class="list-group list-group-flush border-top border-bottom mb-3">
@foreach(Auth::user()->webAuthnCredentials as $credential)
<div class="list-group-item d-flex justify-content-between align-items-center py-2 px-0">
<div>
<span class="fw-semibold small d-block">{{ $credential->alias ?: __('Unnamed Device') }}</span>
<small class="text-muted">{{ __('Registered on') }}: {{ $credential->created_at->format('d M Y') }}</small>
</div>
<form method="POST" action="{{ route('webauthn.destroy', $credential->id) }}" onsubmit="return confirm('{{ __('Are you sure you want to remove this passkey?') }}')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-outline-danger border-0">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
@endforeach
</div>
@else
<div class="alert alert-light border small text-center py-3">
{{ __('No passkeys registered yet.') }}
</div>
@endif
</div>
<div class="d-flex align-items-center gap-3">
<button type="button" id="register-passkey" class="btn btn-primary theme-black px-4">
{{ __('Register New Passkey') }}
</button>
<div id="passkey-status" class="small text-info d-none">
<span class="spinner-border spinner-border-sm me-1"></span> {{ __('Processing...') }}
</div>
</div>
</div>
</div>
@if(get_setting('webauthn_enabled', false))
<script src="{{ asset('vendor/webauthn/webauthn.js') }}"></script>
<script>
document.getElementById('register-passkey').addEventListener('click', async () => {
const btn = document.getElementById('register-passkey');
const status = document.getElementById('passkey-status');
btn.disabled = true;
status.classList.remove('d-none');
if (typeof WebAuthn === 'undefined') {
console.error('WebAuthn library not loaded.');
return;
}
const webauthn = new WebAuthn();
if (WebAuthn.supportsWebAuthn()) {
try {
// Ask for a nickname for the key
const alias = prompt("{{ __('Enter a name for this device/key (e.g. My Laptop, iPhone):') }}", "My Device");
if (alias === null) {
btn.disabled = false;
status.classList.add('d-none');
return;
}
// Laragear WebAuthn v5 registration
const response = await webauthn.register({ alias: alias });
if (response) {
window.location.reload();
}
} catch (error) {
console.error('WebAuthn Error:', error);
let errorMsg = 'Failed to register Passkey. Please use a secure connection.';
if (!window.isSecureContext) {
errorMsg = 'Passkeys require a secure context (HTTPS). \n\n' +
'Developer Tip: If you are on a local domain (like .test), you can bypass this in Chrome/Edge by going to: \n' +
'chrome://flags/#unsafely-treat-insecure-origin-as-secure \n' +
'and adding your domain to the list.';
} else if (error.name === 'NotAllowedError') {
errorMsg = 'Registration was cancelled or timed out.';
} else {
errorMsg = 'Failed to register Passkey. Ensure your device supports biometrics and is configured correctly.';
}
StandardSwal.fire({
icon: 'error',
title: 'Registration Failed!',
text: errorMsg || 'The system was unable to register your Passkey. Please ensure your device is compatible.',
confirmButtonText: 'OK'
});
}
} else {
StandardSwal.fire({
icon: 'info',
title: 'Passkey Not Supported',
text: 'Your current browser or device does not support secure Passkey authentication.',
confirmButtonText: 'Understood'
});
}
btn.disabled = false;
status.classList.add('d-none');
});
</script>
@endif
</section>