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:
2026-04-25 15:07:01 -04:00
parent 4f976b1332
commit 0bb96a502a
16 changed files with 16101 additions and 17 deletions
+11
View File
@@ -290,6 +290,17 @@ builder.Services.AddSession(options =>
// Add memory cache
builder.Services.AddMemoryCache();
// Register Fido2/WebAuthn for passkey (biometric) login
builder.Services.AddFido2(options =>
{
options.ServerDomain = builder.Configuration["Fido2:ServerDomain"] ?? "localhost";
options.ServerName = builder.Configuration["Fido2:ServerName"] ?? "Powder Coating Logix";
var origins = builder.Configuration.GetSection("Fido2:Origins").Get<HashSet<string>>();
if (origins?.Count > 0) options.Origins = origins;
options.TimestampDriftTolerance = int.Parse(
builder.Configuration["Fido2:TimestampDriftTolerance"] ?? "300");
});
// Configure authorization policies for multi-tenancy
builder.Services.AddAuthorization(options =>
{