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:
@@ -0,0 +1,39 @@
|
||||
namespace PowderCoating.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a WebAuthn public-key credential (passkey) registered by an application user.
|
||||
/// One row per device per user. Does not inherit BaseEntity — passkeys are identity
|
||||
/// credentials, not business-domain records, and require no soft-delete or company-scoped
|
||||
/// global query filter (the Login flow queries across tenants by credentialId before auth).
|
||||
/// </summary>
|
||||
public class UserPasskey
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>FK to AspNetUsers.Id (GUID string).</summary>
|
||||
public string UserId { get; set; } = default!;
|
||||
|
||||
/// <summary>Stored for display/management queries. NOT used as a query filter.</summary>
|
||||
public int CompanyId { get; set; }
|
||||
|
||||
/// <summary>WebAuthn credential ID — unique identifier for this passkey.</summary>
|
||||
public byte[] CredentialId { get; set; } = default!;
|
||||
|
||||
/// <summary>COSE-encoded public key from the authenticator.</summary>
|
||||
public byte[] PublicKey { get; set; } = default!;
|
||||
|
||||
/// <summary>Opaque user handle sent by the authenticator during login.</summary>
|
||||
public byte[] UserHandle { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Monotonically increasing counter used to detect cloned authenticators.
|
||||
/// Stored as long to avoid SQL Server uint mapping issues; Fido2NetLib uses uint.
|
||||
/// </summary>
|
||||
public long SignCount { get; set; }
|
||||
|
||||
/// <summary>User-supplied or browser-provided friendly name, e.g. "Scott's iPhone".</summary>
|
||||
public string? DeviceFriendlyName { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user