Add passkey / biometric login (WebAuthn FIDO2)
Shop floor workers can log in once with a password, enroll a passkey, and use Face ID / Windows Hello / fingerprint for all future logins. - UserPasskey entity + AddUserPasskeys migration (Fido2 v4.0.1) - PasskeyController: RegisterOptions, Register, LoginOptions, Login, Manage, Remove endpoints - Login page: platform-aware button (Face ID / Windows Hello / etc.) hidden automatically if browser doesn't support WebAuthn - Post-login floating prompt to enroll on first use; session-dismissed - Passkeys & Biometrics link in user dropdown menu - Manage page: list registered devices, add new, remove individual - passkey.js: targeted base64url conversion (only challenge + user.id + credential IDs) — fixes "Required parameters missing" error caused by blindly converting rp.id and other string fields to ArrayBuffers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -895,6 +895,33 @@
|
||||
<div id="tempdata-info-message" style="display:none;">@TempData["Info"]</div>
|
||||
}
|
||||
|
||||
@* Passkey setup prompt — shown once per session to authenticated users who have no passkeys yet *@
|
||||
@if (User.Identity?.IsAuthenticated == true && !User.IsInRole("SuperAdmin"))
|
||||
{
|
||||
<div id="passkey-setup-prompt" class="d-none"
|
||||
style="position:fixed;bottom:1.25rem;right:1.25rem;z-index:1090;max-width:320px;">
|
||||
<div class="card shadow-lg border-0">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex align-items-start gap-2 mb-2">
|
||||
<i class="bi bi-fingerprint text-primary" style="font-size:1.4rem;flex-shrink:0;margin-top:2px;"></i>
|
||||
<div>
|
||||
<div class="fw-semibold" style="font-size:.9rem;">Enable Face ID / Biometric Login</div>
|
||||
<div class="text-muted" style="font-size:.8rem;">Skip the password next time — use your fingerprint or Face ID.</div>
|
||||
</div>
|
||||
<button type="button" id="passkey-dismiss-btn" class="btn-close ms-auto" style="font-size:.75rem;" aria-label="Dismiss"></button>
|
||||
</div>
|
||||
<p id="passkey-setup-status" class="small mb-2"></p>
|
||||
<div class="d-flex gap-2">
|
||||
<button id="passkey-enable-btn" type="button" class="btn btn-primary btn-sm flex-grow-1">
|
||||
<i class="bi bi-fingerprint me-1"></i>Enable
|
||||
</button>
|
||||
<a href="/Passkey/Manage" class="btn btn-outline-secondary btn-sm">Manage</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Hidden container for ModelState errors (read by toast-notifications.js) *@
|
||||
@if (!ViewData.ModelState.IsValid && ViewData.ModelState.ErrorCount > 0)
|
||||
{
|
||||
@@ -1487,6 +1514,7 @@
|
||||
}
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" asp-controller="Profile" asp-action="Index"><i class="bi bi-person me-2"></i>Profile</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Passkey" asp-action="Manage"><i class="bi bi-fingerprint me-2"></i>Passkeys & Biometrics</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="TwoFactorSetup" asp-action="Index"><i class="bi bi-shield-lock me-2"></i>Two-Factor Auth</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="ReleaseNotes" asp-action="Index"><i class="bi bi-rocket-takeoff me-2"></i>What's New</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Help" asp-action="Index"><i class="bi bi-question-circle me-2"></i>Help</a></li>
|
||||
@@ -2091,6 +2119,7 @@
|
||||
{
|
||||
@* @await Html.PartialAsync("_AiQuickQuoteWidget") *@
|
||||
@await Html.PartialAsync("_AiHelpWidget")
|
||||
<script src="~/js/passkey.js"></script>
|
||||
}
|
||||
|
||||
<!-- ── Quick-Add Modal (reusable inline form host) ─────────────────────── -->
|
||||
|
||||
Reference in New Issue
Block a user