0

[webauthn] Use provider name for Windows passkeys

Use the new Windows WebAuthn v8 pwszAuthenticatorName property to label
credentials from third party providers on Windows.

Fixed: 396434681
Change-Id: I9044484a5d40617b0f1487f43710c5f659e91d8f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6270536
Commit-Queue: Ken Buchanan <kenrb@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1421527}
This commit is contained in:
Nina Satragno
2025-02-18 11:17:17 -08:00
committed by Chromium LUCI CQ
parent 49c6c62a6c
commit fdc85d82ca
8 changed files with 88 additions and 55 deletions

@@ -658,7 +658,8 @@ class WebAuthnWindowsAutofillIntegrationTest
"Flandre Scarlet");
device::PublicKeyCredentialRpEntity rp(kRpId);
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialID1, std::move(rp), std::move(user));
kCredentialID1, std::move(rp), std::move(user),
/*provider_name=*/std::nullopt);
win_webauthn_api_override_ =
std::make_unique<device::WinWebAuthnApi::ScopedOverride>(
@@ -698,7 +699,8 @@ IN_PROC_BROWSER_TEST_F(WebAuthnWindowsAutofillIntegrationTest,
"Sakuya Izayoi");
device::PublicKeyCredentialRpEntity rp(kRpId);
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialID2, std::move(rp), std::move(user));
kCredentialID2, std::move(rp), std::move(user),
/*provider_name=*/std::nullopt);
RunSelectAccountTest(kConditionalUIRequestFiltered);
}

@@ -84,7 +84,8 @@ TEST_F(LocalCredentialManagementTest, OneCredential) {
// With a credential injected, `HasCredentials` should return true and should
// cache that in the profile. Enumerate should return that credential.
api_.InjectDiscoverableCredential(kCredId, {kRpId, std::nullopt},
{{1, 2, 3, 4}, std::nullopt, std::nullopt});
{{1, 2, 3, 4}, std::nullopt, std::nullopt},
std::nullopt);
EXPECT_TRUE(HasCredentials());
EXPECT_TRUE(profile_.GetPrefs()->GetBoolean(kHasPlatformCredentialsPref));
@@ -124,22 +125,26 @@ TEST_F(LocalCredentialManagementTest, Sorting) {
constexpr uint8_t kCredId7[] = {7};
api_.InjectDiscoverableCredential(kCredId7, {"zzz.de", std::nullopt},
{{1, 2, 3, 4}, "username", std::nullopt});
{{1, 2, 3, 4}, "username", std::nullopt},
std::nullopt);
api_.InjectDiscoverableCredential(kCredId2, {"zzz.de", std::nullopt},
{{1, 2, 3, 4}, "username", std::nullopt});
api_.InjectDiscoverableCredential(kCredId3,
{"www.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt});
api_.InjectDiscoverableCredential(kCredId4,
{"foo.www.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt});
api_.InjectDiscoverableCredential(kCredId5,
{"foo.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt});
{{1, 2, 3, 4}, "username", std::nullopt},
std::nullopt);
api_.InjectDiscoverableCredential(
kCredId3, {"www.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt}, std::nullopt);
api_.InjectDiscoverableCredential(
kCredId4, {"foo.www.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt}, std::nullopt);
api_.InjectDiscoverableCredential(
kCredId5, {"foo.example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user1", std::nullopt}, std::nullopt);
api_.InjectDiscoverableCredential(kCredId6, {"aardvark.us", std::nullopt},
{{1, 2, 3, 4}, "username", std::nullopt});
{{1, 2, 3, 4}, "username", std::nullopt},
std::nullopt);
api_.InjectDiscoverableCredential(kCredId1, {"example.co.uk", std::nullopt},
{{1, 2, 3, 4}, "user2", std::nullopt});
{{1, 2, 3, 4}, "user2", std::nullopt},
std::nullopt);
const std::vector<device::DiscoverableCredentialMetadata> result =
Enumerate().value();

@@ -8409,7 +8409,8 @@ TEST_F(ResidentKeyAuthenticatorImplTest, ConditionalUI_Incognito) {
device::PublicKeyCredentialRpEntity rp(kTestRelyingPartyId);
device::PublicKeyCredentialUserEntity user({1, 2, 3, 4});
fake_win_webauthn_api_.InjectDiscoverableCredential(
/*credential_id=*/{{4, 3, 2, 1}}, std::move(rp), std::move(user));
/*credential_id=*/{{4, 3, 2, 1}}, std::move(rp), std::move(user),
/*provider_name=*/std::nullopt);
// |SelectAccount| should not be called for conditional UI requests.
test_client_.delegate_config.expected_accounts = "<invalid>";

@@ -129,6 +129,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
std::optional<LargeBlob> large_blob;
std::optional<std::array<uint8_t, 32>> large_blob_key;
std::optional<std::vector<uint8_t>> cred_blob;
// The custom provider name for this credential.
std::optional<std::string> provider_name;
};
using Credential = std::pair<base::span<const uint8_t>, RegistrationData*>;

@@ -57,6 +57,7 @@ constexpr char kRpId2[] = "meteora.example.com";
const std::vector<uint8_t> kUserId = {5, 6, 7, 8};
constexpr char kUserName[] = "unit-aarc-noa";
constexpr char kUserDisplayName[] = "Noa";
constexpr char kProviderName[] = "Windows Provider";
const std::vector<uint8_t> kLargeBlob = {'b', 'l', 'o', 'b'};
const std::vector<uint8_t> kUserId2 = {1, 1, 1, 1};
constexpr char kUserName2[] = "chloe";
@@ -119,7 +120,8 @@ TEST_F(WinAuthenticatorTest,
GetCredentialInformationForRequest_HasCredentials) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user,
kProviderName);
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
GetCredentialFuture future;
@@ -193,7 +195,8 @@ TEST_F(WinAuthenticatorTest, GetCredentialInformationForRequest_UnknownError) {
TEST_F(WinAuthenticatorTest, GetCredentialInformationForRequest_Unsupported) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user,
kProviderName);
fake_webauthn_api_->set_supports_silent_discovery(false);
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
@@ -218,11 +221,12 @@ TEST_F(WinAuthenticatorTest,
GetCredentialInformationForRequest_NonEmptyAllowList_Found) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user1(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user1);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user1,
kProviderName);
PublicKeyCredentialUserEntity user2(kUserId2, kUserName2, kUserDisplayName2);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId2, rp,
std::move(user2));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId2, rp, std::move(user2), kProviderName);
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
request.allow_list.emplace_back(CredentialType::kPublicKey, kCredentialId);
@@ -247,8 +251,8 @@ TEST_F(WinAuthenticatorTest,
GetCredentialInformationForRequest_NonEmptyAllowList_NotMatching) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp,
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, rp, std::move(user), kProviderName);
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
request.allow_list.emplace_back(CredentialType::kPublicKey, kCredentialId2);
@@ -322,7 +326,8 @@ TEST_F(WinAuthenticatorTest,
TEST_F(WinAuthenticatorTest, EnumeratePlatformCredentials_NotSupported) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user,
kProviderName);
fake_webauthn_api_->set_supports_silent_discovery(false);
base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>> future;
@@ -339,7 +344,8 @@ TEST_F(WinAuthenticatorTest, EnumeratePlatformCredentials_NotSupported) {
TEST_F(WinAuthenticatorTest, EnumeratePlatformCredentials_Supported) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, rp, user,
kProviderName);
fake_webauthn_api_->set_supports_silent_discovery(true);
base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>> future;
@@ -358,6 +364,7 @@ TEST_F(WinAuthenticatorTest, EnumeratePlatformCredentials_Supported) {
EXPECT_EQ(cred.cred_id, kCredentialId);
EXPECT_EQ(cred.user.name, kUserName);
EXPECT_EQ(cred.user.display_name, kUserDisplayName);
EXPECT_EQ(cred.provider_name, kProviderName);
}
TEST_F(WinAuthenticatorTest, MakeCredentialLargeBlob) {
@@ -445,8 +452,8 @@ TEST_F(WinAuthenticatorTest, GetAssertionLargeBlobNotSupported) {
SetVersion(WEBAUTHN_API_VERSION_2);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
{
// Read large blob.
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
@@ -477,8 +484,8 @@ TEST_F(WinAuthenticatorTest, GetAssertionLargeBlobError) {
SetVersion(WEBAUTHN_API_VERSION_3);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
fake_webauthn_api_->set_large_blob_result(
WEBAUTHN_CRED_LARGE_BLOB_STATUS_NOT_SUPPORTED);
{
@@ -511,8 +518,8 @@ TEST_F(WinAuthenticatorTest, GetAssertionLargeBlobSuccess) {
SetVersion(WEBAUTHN_API_VERSION_3);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
{
// Read large blob.
CtapGetAssertionRequest request(kRpId, /*client_data_json=*/"");
@@ -561,8 +568,8 @@ TEST_F(WinAuthenticatorTest, SignalUnknownCredential_NotFound) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalUnknownCredential(fake_webauthn_api_.get(),
@@ -577,8 +584,8 @@ TEST_F(WinAuthenticatorTest, SignalUnknownCredential_WrongRpId) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalUnknownCredential(fake_webauthn_api_.get(),
@@ -594,8 +601,8 @@ TEST_F(WinAuthenticatorTest, SignalUnknownCredential_NotSupported) {
fake_webauthn_api_->set_supports_silent_discovery(false);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalUnknownCredential(fake_webauthn_api_.get(),
@@ -610,8 +617,8 @@ TEST_F(WinAuthenticatorTest, SignalUnknownCredential) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalUnknownCredential(fake_webauthn_api_.get(),
@@ -626,8 +633,8 @@ TEST_F(WinAuthenticatorTest, SignalAllAcceptedCredentials_Found) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(
@@ -642,8 +649,8 @@ TEST_F(WinAuthenticatorTest, SignalAllAcceptedCredentials_NoMatchingUserId) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(
@@ -658,8 +665,8 @@ TEST_F(WinAuthenticatorTest, SignalAllAcceptedCredentials_NoMatchingRpId) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(
@@ -675,8 +682,8 @@ TEST_F(WinAuthenticatorTest, SignalAllAcceptedCredentials_NotSupported) {
fake_webauthn_api_->set_supports_silent_discovery(false);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(
@@ -692,8 +699,8 @@ TEST_F(WinAuthenticatorTest, SignalAllAcceptedCredentials_NotFound) {
SetVersion(WEBAUTHN_API_VERSION_4);
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(kUserId, kUserName, kUserDisplayName);
fake_webauthn_api_->InjectDiscoverableCredential(kCredentialId, std::move(rp),
std::move(user));
fake_webauthn_api_->InjectDiscoverableCredential(
kCredentialId, std::move(rp), std::move(user), kProviderName);
ASSERT_EQ(fake_webauthn_api_->registrations().size(), 1u);
WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(

@@ -94,6 +94,7 @@ struct FakeWinWebAuthnApi::CredentialInfo {
WEBAUTHN_CREDENTIAL_DETAILS details;
std::vector<uint8_t> credential_id;
std::optional<std::u16string> provider_name;
WEBAUTHN_RP_ENTITY_INFORMATION rp;
std::u16string rp_id;
@@ -162,13 +163,15 @@ bool FakeWinWebAuthnApi::InjectNonDiscoverableCredential(
bool FakeWinWebAuthnApi::InjectDiscoverableCredential(
base::span<const uint8_t> credential_id,
device::PublicKeyCredentialRpEntity rp,
device::PublicKeyCredentialUserEntity user) {
device::PublicKeyCredentialUserEntity user,
std::optional<std::string> provider_name) {
RegistrationData registration(VirtualFidoDevice::PrivateKey::FreshP256Key(),
fido_parsing_utils::CreateSHA256Hash(rp.id),
/*counter=*/0);
registration.is_resident = true;
registration.user = std::move(user);
registration.rp = std::move(rp);
registration.provider_name = std::move(provider_name);
bool was_inserted;
std::tie(std::ignore, was_inserted) =
@@ -498,6 +501,10 @@ HRESULT FakeWinWebAuthnApi::GetPlatformCredentialList(
auto credential = std::make_unique<CredentialInfo>();
credential->credential_id = registration.first;
credential->provider_name = registration.second.provider_name
? std::make_optional(base::UTF8ToUTF16(
*registration.second.provider_name))
: std::nullopt;
credential->rp_id = base::UTF8ToUTF16(registration.second.rp->id);
credential->rp_name =
base::UTF8ToUTF16(registration.second.rp->name.value_or(""));
@@ -519,12 +526,16 @@ HRESULT FakeWinWebAuthnApi::GetPlatformCredentialList(
.pwszDisplayName = base::as_wcstr(credential->user_display_name),
};
credential->details = {
.dwVersion = WEBAUTHN_CREDENTIAL_DETAILS_VERSION_1,
.dwVersion = WEBAUTHN_CREDENTIAL_DETAILS_CURRENT_VERSION,
.cbCredentialID = static_cast<DWORD>(credential->credential_id.size()),
.pbCredentialID = credential->credential_id.data(),
.pRpInformation = &credential->rp,
.pUserInformation = &credential->user,
.bRemovable = true,
.pwszAuthenticatorName =
credential->provider_name
? base::as_wcstr(*credential->provider_name)
: nullptr,
};
credential_list->win_credentials.push_back(&credential->details);
credential_list->credentials.push_back(std::move(credential));

@@ -47,7 +47,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FakeWinWebAuthnApi : public WinWebAuthnApi {
// AuthenticatorGetAssertion().
bool InjectDiscoverableCredential(base::span<const uint8_t> credential_id,
device::PublicKeyCredentialRpEntity rp,
device::PublicKeyCredentialUserEntity user);
device::PublicKeyCredentialUserEntity user,
std::optional<std::string> provider_name);
// Inject the return value for WinWebAuthnApi::IsAvailable().
void set_available(bool available) { is_available_ = available; }

@@ -375,8 +375,11 @@ WinCredentialDetailsListToCredentialMetadata(
user->pwszDisplayName
? std::make_optional(base::WideToUTF8(user->pwszDisplayName))
: std::nullopt),
// TODO(nsatragno): integrate with pwszAuthenticatorName.
/*provider_name=*/std::nullopt);
credential->dwVersion >= WEBAUTHN_CREDENTIAL_DETAILS_VERSION_3 &&
credential->pwszAuthenticatorName
? std::make_optional(
base::WideToUTF8(credential->pwszAuthenticatorName))
: std::nullopt);
metadata.system_created = !credential->bRemovable;
result.push_back(std::move(metadata));
}