diff --git a/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt b/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
index 8e7ea89bc368..3884283ff48e 100644
--- a/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
+++ b/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
@@ -1,3 +1,4 @@
#nullable enable
Microsoft.AspNetCore.Identity.UserPasskeyInfo.Aaguid.get -> byte[]?
Microsoft.AspNetCore.Identity.UserPasskeyInfo.Aaguid.set -> void
+Microsoft.AspNetCore.Identity.UserPasskeyInfo.UserPasskeyInfo(byte[]! credentialId, byte[]! publicKey, System.DateTimeOffset createdAt, uint signCount, string![]? transports, bool isUserVerified, bool isBackupEligible, bool isBackedUp, byte[]! attestationObject, byte[]! clientDataJson, string? name) -> void
diff --git a/src/Identity/Extensions.Core/src/UserPasskeyInfo.cs b/src/Identity/Extensions.Core/src/UserPasskeyInfo.cs
index a8454f2dd04d..bd93c54f9043 100644
--- a/src/Identity/Extensions.Core/src/UserPasskeyInfo.cs
+++ b/src/Identity/Extensions.Core/src/UserPasskeyInfo.cs
@@ -34,6 +34,36 @@ public UserPasskeyInfo(
bool isBackedUp,
byte[] attestationObject,
byte[] clientDataJson)
+ : this(credentialId, publicKey, createdAt, signCount, transports, isUserVerified, isBackupEligible, isBackedUp, attestationObject, clientDataJson, name: null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of with a friendly name.
+ ///
+ /// The credential ID for the passkey.
+ /// The public key for the passkey.
+ /// The time when the passkey was created.
+ /// The signature counter for the passkey.
+ /// The transports supported by this passkey.
+ /// Indicates if the passkey has a verified user.
+ /// Indicates if the passkey is eligible for backup.
+ /// Indicates if the passkey is currently backed up.
+ /// The passkey's attestation object.
+ /// The passkey's client data JSON.
+ /// An optional friendly name for the passkey.
+ public UserPasskeyInfo(
+ byte[] credentialId,
+ byte[] publicKey,
+ DateTimeOffset createdAt,
+ uint signCount,
+ string[]? transports,
+ bool isUserVerified,
+ bool isBackupEligible,
+ bool isBackedUp,
+ byte[] attestationObject,
+ byte[] clientDataJson,
+ string? name)
{
CredentialId = credentialId;
PublicKey = publicKey;
@@ -45,6 +75,7 @@ public UserPasskeyInfo(
IsBackedUp = isBackedUp;
AttestationObject = attestationObject;
ClientDataJson = clientDataJson;
+ Name = name;
}
///
diff --git a/src/Identity/test/Identity.Test/UserPasskeyInfoTest.cs b/src/Identity/test/Identity.Test/UserPasskeyInfoTest.cs
new file mode 100644
index 000000000000..4d268671c23c
--- /dev/null
+++ b/src/Identity/test/Identity.Test/UserPasskeyInfoTest.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Identity.Test;
+
+public class UserPasskeyInfoTest
+{
+ [Fact]
+ public void Ctor_WithName_SetsAllProperties()
+ {
+ // Arrange
+ var credentialId = new byte[] { 1, 2, 3 };
+ var publicKey = new byte[] { 4, 5, 6 };
+ var createdAt = DateTimeOffset.UtcNow;
+ var transports = new[] { "usb", "nfc" };
+ var attestationObject = new byte[] { 7, 8, 9 };
+ var clientDataJson = new byte[] { 10, 11 };
+ var name = "My Security Key";
+
+ // Act
+ var passkey = new UserPasskeyInfo(
+ credentialId,
+ publicKey,
+ createdAt,
+ signCount: 42,
+ transports,
+ isUserVerified: true,
+ isBackupEligible: true,
+ isBackedUp: false,
+ attestationObject,
+ clientDataJson,
+ name);
+
+ // Assert
+ Assert.Equal(credentialId, passkey.CredentialId);
+ Assert.Equal(publicKey, passkey.PublicKey);
+ Assert.Equal(createdAt, passkey.CreatedAt);
+ Assert.Equal(42u, passkey.SignCount);
+ Assert.Equal(transports, passkey.Transports);
+ Assert.True(passkey.IsUserVerified);
+ Assert.True(passkey.IsBackupEligible);
+ Assert.False(passkey.IsBackedUp);
+ Assert.Equal(attestationObject, passkey.AttestationObject);
+ Assert.Equal(clientDataJson, passkey.ClientDataJson);
+ Assert.Equal("My Security Key", passkey.Name);
+ }
+
+ [Fact]
+ public void Ctor_WithNullName_LeavesNameNull()
+ {
+ var passkey = new UserPasskeyInfo(
+ new byte[] { 1 },
+ new byte[] { 2 },
+ DateTimeOffset.UtcNow,
+ signCount: 0,
+ transports: null,
+ isUserVerified: false,
+ isBackupEligible: false,
+ isBackedUp: false,
+ new byte[] { 3 },
+ new byte[] { 4 },
+ name: null);
+
+ Assert.Null(passkey.Name);
+ }
+
+ [Fact]
+ public void Ctor_WithoutName_LeavesNameNull()
+ {
+ // The original constructor should still work and leave Name unset.
+ var passkey = new UserPasskeyInfo(
+ new byte[] { 1 },
+ new byte[] { 2 },
+ DateTimeOffset.UtcNow,
+ signCount: 0,
+ transports: null,
+ isUserVerified: false,
+ isBackupEligible: false,
+ isBackedUp: false,
+ new byte[] { 3 },
+ new byte[] { 4 });
+
+ Assert.Null(passkey.Name);
+ }
+}