0

[webauthn] Use provider name for MacOS passkeys

Use ASAuthorizationWebBrowserPlatformPublicKeyCredential's providerName
to identify the provider of a passkey sourced from a third-party passkey
provider on MacOS.

Fixed: 392837090
Change-Id: I0a6747c451c813768065cd4f70d13f96a89e2541
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6213993
Reviewed-by: Mohamed Amir Yosef <mamir@chromium.org>
Commit-Queue: Mohamed Amir Yosef <mamir@chromium.org>
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: Adem Derinel <derinel@google.com>
Cr-Commit-Position: refs/heads/main@{#1419889}
This commit is contained in:
Nina Satragno
2025-02-13 07:27:37 -08:00
committed by Chromium LUCI CQ
parent a3c55198aa
commit dffa0e82f4
23 changed files with 169 additions and 64 deletions

@ -71,21 +71,24 @@ const device::DiscoverableCredentialMetadata user1{
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
device::fido_parsing_utils::Materialize(kUserId), device::fido_parsing_utils::Materialize(kUserId),
kUserName1, kUserName1,
/*display_name=*/std::nullopt)}; /*display_name=*/std::nullopt),
/*provider_name=*/std::nullopt};
const device::DiscoverableCredentialMetadata user2{ const device::DiscoverableCredentialMetadata user2{
device::AuthenticatorType::kOther, kRpId, device::AuthenticatorType::kOther, kRpId,
device::fido_parsing_utils::Materialize(kCredId2), device::fido_parsing_utils::Materialize(kCredId2),
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
device::fido_parsing_utils::Materialize(kUserId), device::fido_parsing_utils::Materialize(kUserId),
kUserName2, kUserName2,
/*display_name=*/std::nullopt)}; /*display_name=*/std::nullopt),
/*provider_name=*/std::nullopt};
const device::DiscoverableCredentialMetadata userGpm{ const device::DiscoverableCredentialMetadata userGpm{
device::AuthenticatorType::kEnclave, kRpId, device::AuthenticatorType::kEnclave, kRpId,
device::fido_parsing_utils::Materialize(kCredIdGpm), device::fido_parsing_utils::Materialize(kCredIdGpm),
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
device::fido_parsing_utils::Materialize(kUserId), device::fido_parsing_utils::Materialize(kUserId),
kUserName1, kUserName1,
/*display_name=*/std::nullopt)}; /*display_name=*/std::nullopt),
/*provider_name=*/std::nullopt};
PasskeyCredential CreatePasskey(std::vector<uint8_t> cred_id, PasskeyCredential CreatePasskey(std::vector<uint8_t> cred_id,
std::string username, std::string username,

@ -39,7 +39,7 @@ AccountHoverListModel::AccountHoverListModel(
items_.emplace_back( items_.emplace_back(
NameTokenForDisplay(cred.user.name.value_or("")), NameTokenForDisplay(cred.user.name.value_or("")),
AuthenticatorRequestDialogModel::GetMechanismDescription( AuthenticatorRequestDialogModel::GetMechanismDescription(
cred.source, dialog_model->priority_phone_name), cred, dialog_model->priority_phone_name),
ui::ImageModel::FromVectorIcon(vector_icons::kPasskeyIcon, ui::ImageModel::FromVectorIcon(vector_icons::kPasskeyIcon,
dialog_model->ui_disabled_ dialog_model->ui_disabled_
? ui::kColorIconDisabled ? ui::kColorIconDisabled

@ -664,23 +664,38 @@ class GPMPasskeysAuthenticatorDialogTest : public DialogBrowserTest {
device::DiscoverableCredentialMetadata gpm_cred( device::DiscoverableCredentialMetadata gpm_cred(
device::AuthenticatorType::kEnclave, "example.com", {1}, device::AuthenticatorType::kEnclave, "example.com", {1},
device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com", device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com",
"Elisa Beckett")); "Elisa Beckett"),
std::nullopt);
device::DiscoverableCredentialMetadata local_cred1( device::DiscoverableCredentialMetadata local_cred1(
device::AuthenticatorType::kTouchID, "example.com", {1}, device::AuthenticatorType::kTouchID, "example.com", {1},
device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com", device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com",
"Elisa Beckett")); "Elisa Beckett"),
std::nullopt);
device::DiscoverableCredentialMetadata local_cred2( device::DiscoverableCredentialMetadata local_cred2(
device::AuthenticatorType::kTouchID, "example.com", {2}, device::AuthenticatorType::kTouchID, "example.com", {2},
device::PublicKeyCredentialUserEntity({2}, "elisa.beckett@ink-42.com", device::PublicKeyCredentialUserEntity({2}, "elisa.beckett@ink-42.com",
"Elisa Beckett")); "Elisa Beckett"),
std::nullopt);
device::DiscoverableCredentialMetadata phone_cred1( device::DiscoverableCredentialMetadata phone_cred1(
device::AuthenticatorType::kPhone, "example.com", {3}, device::AuthenticatorType::kPhone, "example.com", {3},
device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com", device::PublicKeyCredentialUserEntity({1}, "elisa.g.beckett@gmail.com",
"Elisa Beckett")); "Elisa Beckett"),
std::nullopt);
device::DiscoverableCredentialMetadata phone_cred2( device::DiscoverableCredentialMetadata phone_cred2(
device::AuthenticatorType::kPhone, "example.com", {4}, device::AuthenticatorType::kPhone, "example.com", {4},
device::PublicKeyCredentialUserEntity({2}, "elisa.beckett@ink-42.com", device::PublicKeyCredentialUserEntity({2}, "elisa.beckett@ink-42.com",
"Elisa Beckett")); "Elisa Beckett"),
std::nullopt);
device::DiscoverableCredentialMetadata ick_cred1(
device::AuthenticatorType::kICloudKeychain, "example.com", {5},
device::PublicKeyCredentialUserEntity({1}, "elisa.beckett@gmail.com",
"Elisa Beckett"),
"Example Passkey Provider");
device::DiscoverableCredentialMetadata ick_cred2(
device::AuthenticatorType::kICloudKeychain, "example.com", {6},
device::PublicKeyCredentialUserEntity({2}, "elisa.beckett@ink-42.com",
"Elisa Beckett"),
"Another Example Passkey Provider");
model_->user_entity = local_cred1.user; model_->user_entity = local_cred1.user;
// Configure a phone from sync. // Configure a phone from sync.
@ -822,6 +837,13 @@ class GPMPasskeysAuthenticatorDialogTest : public DialogBrowserTest {
} else if (name == "gpm_locked_pin") { } else if (name == "gpm_locked_pin") {
controller_->SetCurrentStepForTesting( controller_->SetCurrentStepForTesting(
AuthenticatorRequestDialogModel::Step::kGPMLockedPin); AuthenticatorRequestDialogModel::Step::kGPMLockedPin);
} else if (name == "icloud_keychain_cred") {
transport_availability.has_empty_allow_list = true;
controller_->set_allow_icloud_keychain(true);
transport_availability.recognized_credentials = {
std::move(ick_cred1),
std::move(ick_cred2),
};
} else { } else {
NOTREACHED(); NOTREACHED();
} }
@ -973,6 +995,11 @@ IN_PROC_BROWSER_TEST_F(GPMPasskeysAuthenticatorDialogTest,
ShowAndVerifyUi(); ShowAndVerifyUi();
} }
IN_PROC_BROWSER_TEST_F(GPMPasskeysAuthenticatorDialogTest,
InvokeUi_icloud_keychain_cred) {
ShowAndVerifyUi();
}
#if BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(GPMPasskeysAuthenticatorDialogTest, InvokeUi_touchid) { IN_PROC_BROWSER_TEST_F(GPMPasskeysAuthenticatorDialogTest, InvokeUi_touchid) {
if (__builtin_available(macos 12, *)) { if (__builtin_available(macos 12, *)) {

@ -441,7 +441,8 @@ TEST_F(PasskeysHandlerTest, TestHandleEdit) {
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
{0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, {0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa,
0xa, 0xa, 0xa, 0xa}, 0xa, 0xa, 0xa, 0xa},
"new-username", "new-username")}}; "new-username", "new-username"),
/*provider_name=*/std::nullopt}};
std::move(callback).Run(std::move(credential_metadata)); std::move(callback).Run(std::move(credential_metadata));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
}); });

@ -1412,7 +1412,8 @@ void AuthenticatorRequestDialogController::SelectAccount(
for (const auto& response : ephemeral_state_.responses_) { for (const auto& response : ephemeral_state_.responses_) {
model_->creds.emplace_back(AuthenticatorType::kOther, model_->creds.emplace_back(AuthenticatorType::kOther,
model_->relying_party_id, model_->relying_party_id,
response.credential->id, *response.user_entity); response.credential->id, *response.user_entity,
/*provider_name=*/std::nullopt);
} }
selection_callback_ = std::move(callback); selection_callback_ = std::move(callback);
SetCurrentStep(Step::kSelectAccount); SetCurrentStep(Step::kSelectAccount);
@ -2145,7 +2146,7 @@ void AuthenticatorRequestDialogController::PopulateMechanisms() {
base::Unretained(this), cred.cred_id)); base::Unretained(this), cred.cred_id));
mechanism.description = mechanism.description =
AuthenticatorRequestDialogModel::GetMechanismDescription( AuthenticatorRequestDialogModel::GetMechanismDescription(
cred.source, model_->priority_phone_name); cred, model_->priority_phone_name);
} }
for (const auto& password : passwords_) { for (const auto& password : passwords_) {
Mechanism mechanism( Mechanism mechanism(

@ -29,6 +29,7 @@
#include "content/public/browser/global_routing_id.h" #include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "device/fido/discoverable_credential_metadata.h"
#include "device/fido/features.h" #include "device/fido/features.h"
#include "device/fido/fido_types.h" #include "device/fido/fido_types.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
@ -91,16 +92,22 @@ AUTHENTICATOR_EVENTS
// static // static
std::u16string AuthenticatorRequestDialogModel::GetMechanismDescription( std::u16string AuthenticatorRequestDialogModel::GetMechanismDescription(
device::AuthenticatorType type, const device::DiscoverableCredentialMetadata& cred,
const std::optional<std::string>& phone_name) { const std::optional<std::string>& phone_name) {
if (type == device::AuthenticatorType::kPhone) { if (cred.source == device::AuthenticatorType::kPhone) {
return l10n_util::GetStringFUTF16(IDS_WEBAUTHN_SOURCE_PHONE, return l10n_util::GetStringFUTF16(IDS_WEBAUTHN_SOURCE_PHONE,
base::UTF8ToUTF16(*phone_name)); base::UTF8ToUTF16(*phone_name));
} }
int message;
const bool gpm_enabled = const bool gpm_enabled =
base::FeatureList::IsEnabled(device::kWebAuthnEnclaveAuthenticator); base::FeatureList::IsEnabled(device::kWebAuthnEnclaveAuthenticator);
switch (type) { if (cred.provider_name) {
return gpm_enabled ? base::UTF8ToUTF16(*cred.provider_name)
: l10n_util::GetStringFUTF16(
IDS_WEBAUTHN_SOURCE_CUSTOM_VENDOR,
base::UTF8ToUTF16(*cred.provider_name));
}
int message;
switch (cred.source) {
case device::AuthenticatorType::kWinNative: case device::AuthenticatorType::kWinNative:
message = gpm_enabled ? IDS_WEBAUTHN_SOURCE_WINDOWS_HELLO_NEW message = gpm_enabled ? IDS_WEBAUTHN_SOURCE_WINDOWS_HELLO_NEW
: IDS_WEBAUTHN_SOURCE_WINDOWS_HELLO; : IDS_WEBAUTHN_SOURCE_WINDOWS_HELLO;
@ -110,8 +117,6 @@ std::u16string AuthenticatorRequestDialogModel::GetMechanismDescription(
: IDS_WEBAUTHN_SOURCE_CHROME_PROFILE; : IDS_WEBAUTHN_SOURCE_CHROME_PROFILE;
break; break;
case device::AuthenticatorType::kICloudKeychain: case device::AuthenticatorType::kICloudKeychain:
// TODO(crbug.com/40265798): Use IDS_WEBAUTHN_SOURCE_CUSTOM_VENDOR for
// third party providers.
message = gpm_enabled ? IDS_WEBAUTHN_SOURCE_ICLOUD_KEYCHAIN_NEW message = gpm_enabled ? IDS_WEBAUTHN_SOURCE_ICLOUD_KEYCHAIN_NEW
: IDS_WEBAUTHN_SOURCE_ICLOUD_KEYCHAIN; : IDS_WEBAUTHN_SOURCE_ICLOUD_KEYCHAIN;
break; break;

@ -379,7 +379,7 @@ struct AuthenticatorRequestDialogModel
// Returns a user-friendly description for a |type|. If |type| is kPhone, a // Returns a user-friendly description for a |type|. If |type| is kPhone, a
// |phone_name| must be passed. // |phone_name| must be passed.
static std::u16string GetMechanismDescription( static std::u16string GetMechanismDescription(
device::AuthenticatorType type, const device::DiscoverableCredentialMetadata& cred,
const std::optional<std::string>& phone_name); const std::optional<std::string>& phone_name);
explicit AuthenticatorRequestDialogModel( explicit AuthenticatorRequestDialogModel(

@ -312,32 +312,66 @@ const device::PublicKeyCredentialUserEntity kPhoneUser2({3, 4, 5, 6},
"D", "D",
std::nullopt); std::nullopt);
const device::DiscoverableCredentialMetadata const device::DiscoverableCredentialMetadata kCred1(
kCred1(device::AuthenticatorType::kOther, "rp.com", {0}, kUser1); device::AuthenticatorType::kOther,
"rp.com",
{0},
kUser1,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kCred1FromICloudKeychain( const device::DiscoverableCredentialMetadata kCred1FromICloudKeychain(
device::AuthenticatorType::kICloudKeychain, device::AuthenticatorType::kICloudKeychain,
"rp.com", "rp.com",
{4}, {4},
kUser1); kUser1,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kCred1FromChromeOS( const device::DiscoverableCredentialMetadata kCred1FromChromeOS(
device::AuthenticatorType::kChromeOS, device::AuthenticatorType::kChromeOS,
"rp.com", "rp.com",
{4}, {4},
kUser1); kUser1,
const device::DiscoverableCredentialMetadata /*provider_name=*/std::nullopt);
kCred2(device::AuthenticatorType::kOther, "rp.com", {1}, kUser2); const device::DiscoverableCredentialMetadata kCred2(
const device::DiscoverableCredentialMetadata device::AuthenticatorType::kOther,
kPhoneCred1(device::AuthenticatorType::kPhone, "rp.com", {2}, kPhoneUser1); "rp.com",
const device::DiscoverableCredentialMetadata {1},
kPhoneCred2(device::AuthenticatorType::kPhone, "rp.com", {3}, kPhoneUser2); kUser2,
const device::DiscoverableCredentialMetadata /*provider_name=*/std::nullopt);
kWinCred1(device::AuthenticatorType::kWinNative, "rp.com", {0}, kUser1); const device::DiscoverableCredentialMetadata kPhoneCred1(
const device::DiscoverableCredentialMetadata device::AuthenticatorType::kPhone,
kWinCred2(device::AuthenticatorType::kWinNative, "rp.com", {1}, kUser2); "rp.com",
const device::DiscoverableCredentialMetadata {2},
kTouchIDCred1(device::AuthenticatorType::kTouchID, "rp.com", {4}, kUser1); kPhoneUser1,
const device::DiscoverableCredentialMetadata /*provider_name=*/std::nullopt);
kEnclaveCred1(device::AuthenticatorType::kEnclave, "rp.com", {1}, kUser1); const device::DiscoverableCredentialMetadata kPhoneCred2(
device::AuthenticatorType::kPhone,
"rp.com",
{3},
kPhoneUser2,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kWinCred1(
device::AuthenticatorType::kWinNative,
"rp.com",
{0},
kUser1,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kWinCred2(
device::AuthenticatorType::kWinNative,
"rp.com",
{1},
kUser2,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kTouchIDCred1(
device::AuthenticatorType::kTouchID,
"rp.com",
{4},
kUser1,
/*provider_name=*/std::nullopt);
const device::DiscoverableCredentialMetadata kEnclaveCred1(
device::AuthenticatorType::kEnclave,
"rp.com",
{1},
kUser1,
/*provider_name=*/std::nullopt);
AuthenticatorRequestDialogModel::Mechanism::CredentialInfo CredentialInfoFrom( AuthenticatorRequestDialogModel::Mechanism::CredentialInfo CredentialInfoFrom(
const device::DiscoverableCredentialMetadata& metadata) { const device::DiscoverableCredentialMetadata& metadata) {

@ -1171,7 +1171,8 @@ void ChromeAuthenticatorRequestDelegate::GetPhoneContactableGpmPasskeysForRpId(
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
std::vector<uint8_t>(passkey.user_id().begin(), std::vector<uint8_t>(passkey.user_id().begin(),
passkey.user_id().end()), passkey.user_id().end()),
passkey.user_name(), passkey.user_display_name())); passkey.user_name(), passkey.user_display_name()),
/*provider_name=*/std::nullopt);
} }
} }

@ -651,7 +651,8 @@ TEST_F(ChromeAuthenticatorRequestDelegateTest, FilterGoogleComPasskeys) {
device::AuthenticatorType::kOther, test.rp_id, device::AuthenticatorType::kOther, test.rp_id,
std::vector<uint8_t>{0}, std::vector<uint8_t>{0},
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
std::vector<uint8_t>(user_id.begin(), user_id.end()))); std::vector<uint8_t>(user_id.begin(), user_id.end())),
/*provider_name=*/std::nullopt);
} }
data.has_platform_authenticator_credential = test.recognized_credential; data.has_platform_authenticator_credential = test.recognized_credential;
@ -659,7 +660,8 @@ TEST_F(ChromeAuthenticatorRequestDelegateTest, FilterGoogleComPasskeys) {
// affect setting the recognized credentials flag. // affect setting the recognized credentials flag.
data.recognized_credentials.emplace_back( data.recognized_credentials.emplace_back(
device::AuthenticatorType::kICloudKeychain, test.rp_id, device::AuthenticatorType::kICloudKeychain, test.rp_id,
std::vector<uint8_t>{0}, device::PublicKeyCredentialUserEntity({1})); std::vector<uint8_t>{0}, device::PublicKeyCredentialUserEntity({1}),
/*provider_name=*/std::nullopt);
data.has_icloud_keychain_credential = device::FidoRequestHandlerBase:: data.has_icloud_keychain_credential = device::FidoRequestHandlerBase::
RecognizedCredential::kHasRecognizedCredential; RecognizedCredential::kHasRecognizedCredential;
@ -708,7 +710,8 @@ TEST_F(ChromeAuthenticatorRequestDelegateTest,
data.recognized_credentials.emplace_back( data.recognized_credentials.emplace_back(
device::AuthenticatorType::kOther, kGoogleRpId, std::vector<uint8_t>{0}, device::AuthenticatorType::kOther, kGoogleRpId, std::vector<uint8_t>{0},
device::PublicKeyCredentialUserEntity( device::PublicKeyCredentialUserEntity(
std::vector<uint8_t>(user_id.begin(), user_id.end()))); std::vector<uint8_t>(user_id.begin(), user_id.end())),
/*provider_name=*/std::nullopt);
data.has_platform_authenticator_credential = device::FidoRequestHandlerBase:: data.has_platform_authenticator_credential = device::FidoRequestHandlerBase::
RecognizedCredential::kHasRecognizedCredential; RecognizedCredential::kHasRecognizedCredential;
@ -1010,7 +1013,8 @@ TEST_F(ChromeAuthenticatorRequestDelegateWithPasswordsTest,
transports_info.recognized_credentials = { transports_info.recognized_credentials = {
device::DiscoverableCredentialMetadata( device::DiscoverableCredentialMetadata(
device::AuthenticatorType::kEnclave, kRpId, {}, device::AuthenticatorType::kEnclave, kRpId, {},
device::PublicKeyCredentialUserEntity())}; device::PublicKeyCredentialUserEntity(),
/*provider_name=*/std::nullopt)};
// still waiting for passwords. // still waiting for passwords.
EXPECT_CALL(mock_closure, Run).Times(0); EXPECT_CALL(mock_closure, Run).Times(0);

@ -51,7 +51,8 @@ void LocalCredentialManagementMac::Enumerate(
credential_metadata.emplace_back( credential_metadata.emplace_back(
device::AuthenticatorType::kTouchID, credential.rp_id, device::AuthenticatorType::kTouchID, credential.rp_id,
credential.credential_id, credential.credential_id,
credential.metadata.ToPublicKeyCredentialUserEntity()); credential.metadata.ToPublicKeyCredentialUserEntity(),
/*provider_name=*/std::nullopt);
} }
std::sort(credential_metadata.begin(), credential_metadata.end(), std::sort(credential_metadata.begin(), credential_metadata.end(),
CredentialComparator()); CredentialComparator());

@ -9485,7 +9485,8 @@ class ICloudKeychainAuthenticatorImplTest : public AuthenticatorImplTest {
static std::vector<device::DiscoverableCredentialMetadata> GetCredentials() { static std::vector<device::DiscoverableCredentialMetadata> GetCredentials() {
device::DiscoverableCredentialMetadata metadata( device::DiscoverableCredentialMetadata metadata(
device::AuthenticatorType::kICloudKeychain, kTestRelyingPartyId, device::AuthenticatorType::kICloudKeychain, kTestRelyingPartyId,
{1, 2, 3, 4}, {{5, 6, 7, 8}, "name", "displayName"}); {1, 2, 3, 4}, {{5, 6, 7, 8}, "name", "displayName"},
/*provider_name=*/std::nullopt);
return {std::move(metadata)}; return {std::move(metadata)};
} }

@ -4,17 +4,21 @@
#include "device/fido/discoverable_credential_metadata.h" #include "device/fido/discoverable_credential_metadata.h"
#include <optional>
namespace device { namespace device {
DiscoverableCredentialMetadata::DiscoverableCredentialMetadata( DiscoverableCredentialMetadata::DiscoverableCredentialMetadata(
AuthenticatorType source_in, AuthenticatorType source_in,
std::string rp_id_in, std::string rp_id_in,
std::vector<uint8_t> cred_id_in, std::vector<uint8_t> cred_id_in,
PublicKeyCredentialUserEntity user_in) PublicKeyCredentialUserEntity user_in,
std::optional<std::string> provider_name_in)
: source(source_in), : source(source_in),
rp_id(std::move(rp_id_in)), rp_id(std::move(rp_id_in)),
cred_id(std::move(cred_id_in)), cred_id(std::move(cred_id_in)),
user(std::move(user_in)) {} user(std::move(user_in)),
provider_name(std::move(provider_name_in)) {}
DiscoverableCredentialMetadata::DiscoverableCredentialMetadata() = default; DiscoverableCredentialMetadata::DiscoverableCredentialMetadata() = default;
DiscoverableCredentialMetadata::DiscoverableCredentialMetadata( DiscoverableCredentialMetadata::DiscoverableCredentialMetadata(

@ -5,6 +5,7 @@
#ifndef DEVICE_FIDO_DISCOVERABLE_CREDENTIAL_METADATA_H_ #ifndef DEVICE_FIDO_DISCOVERABLE_CREDENTIAL_METADATA_H_
#define DEVICE_FIDO_DISCOVERABLE_CREDENTIAL_METADATA_H_ #define DEVICE_FIDO_DISCOVERABLE_CREDENTIAL_METADATA_H_
#include <optional>
#include <vector> #include <vector>
#include "base/component_export.h" #include "base/component_export.h"
@ -21,7 +22,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) DiscoverableCredentialMetadata {
DiscoverableCredentialMetadata(AuthenticatorType source, DiscoverableCredentialMetadata(AuthenticatorType source,
std::string rp_id, std::string rp_id,
std::vector<uint8_t> cred_id, std::vector<uint8_t> cred_id,
PublicKeyCredentialUserEntity user); PublicKeyCredentialUserEntity user,
std::optional<std::string> provider_name);
DiscoverableCredentialMetadata(); DiscoverableCredentialMetadata();
DiscoverableCredentialMetadata(const DiscoverableCredentialMetadata& other); DiscoverableCredentialMetadata(const DiscoverableCredentialMetadata& other);
@ -41,6 +43,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) DiscoverableCredentialMetadata {
// automatically by the system. This can happen on Windows where (at least) a // automatically by the system. This can happen on Windows where (at least) a
// credential for login.microsoft.com can be auto-created for users. // credential for login.microsoft.com can be auto-created for users.
bool system_created = false; bool system_created = false;
// The name of the third-party provider the passkey is stored in. This is
// populated for credentials coming from the MacOS API.
std::optional<std::string> provider_name;
}; };
} // namespace device } // namespace device

@ -76,7 +76,8 @@ void TouchIdAuthenticator::GetPlatformCredentialInfoForRequest(
for (const auto& credential : *credentials) { for (const auto& credential : *credentials) {
result.emplace_back(AuthenticatorType::kTouchID, request.rp_id, result.emplace_back(AuthenticatorType::kTouchID, request.rp_id,
credential.credential_id, credential.credential_id,
credential.metadata.ToPublicKeyCredentialUserEntity()); credential.metadata.ToPublicKeyCredentialUserEntity(),
/*provider_name=*/std::nullopt);
} }
std::move(callback).Run( std::move(callback).Run(
std::move(result), std::move(result),

@ -60,7 +60,7 @@ TEST_F(TouchIdAuthenticatorTest, GetPlatformCredentialInfoForRequest_RK) {
->first; ->first;
DiscoverableCredentialMetadata credential_metadata( DiscoverableCredentialMetadata credential_metadata(
AuthenticatorType::kTouchID, kRp1, credential.credential_id, AuthenticatorType::kTouchID, kRp1, credential.credential_id,
std::move(user)); std::move(user), /*provider_name=*/std::nullopt);
// Inject a non resident credential for RP 2. This one should be ignored. // Inject a non resident credential for RP 2. This one should be ignored.
PublicKeyCredentialUserEntity user2(kUserId2); PublicKeyCredentialUserEntity user2(kUserId2);
@ -105,7 +105,7 @@ TEST_F(TouchIdAuthenticatorTest, GetPlatformCredentialInfoForRequest_NonRK) {
->first; ->first;
DiscoverableCredentialMetadata credential_metadata( DiscoverableCredentialMetadata credential_metadata(
AuthenticatorType::kTouchID, kRp1, credential.credential_id, AuthenticatorType::kTouchID, kRp1, credential.credential_id,
std::move(user)); std::move(user), /*provider_name=*/std::nullopt);
{ {
// RP 1 should report the credential if it is in the allow list but not // RP 1 should report the credential if it is in the allow list but not

@ -22,6 +22,7 @@ API_AVAILABLE(macos(13.3))
@property(nonatomic, copy) NSString* name; @property(nonatomic, copy) NSString* name;
@property(nonatomic, copy) NSString* relyingParty; @property(nonatomic, copy) NSString* relyingParty;
@property(nonatomic, copy) NSData* userHandle; @property(nonatomic, copy) NSData* userHandle;
@property(nonatomic, copy) NSString* providerName;
@end @end
@implementation FakeBrowserPlatformPublicKeyCredential @implementation FakeBrowserPlatformPublicKeyCredential
@ -29,6 +30,7 @@ API_AVAILABLE(macos(13.3))
@synthesize name = _name; @synthesize name = _name;
@synthesize relyingParty = _relyingParty; @synthesize relyingParty = _relyingParty;
@synthesize userHandle = _userHandle; @synthesize userHandle = _userHandle;
@synthesize providerName = _providerName;
@end @end
API_AVAILABLE(macos(13.3)) API_AVAILABLE(macos(13.3))
@ -199,6 +201,8 @@ void FakeSystemInterface::GetPlatformCredentials(
cred.relyingParty = base::SysUTF8ToNSString(rp_id); cred.relyingParty = base::SysUTF8ToNSString(rp_id);
cred.name = base::SysUTF8ToNSString(cred_values.user.name.value_or("")); cred.name = base::SysUTF8ToNSString(cred_values.user.name.value_or(""));
[ret addObject:cred]; [ret addObject:cred];
cred.providerName = base::SysUTF8ToNSString(
cred_values.provider_name.value_or("(Not provided)"));
} }
block(ret); block(ret);

@ -255,7 +255,8 @@ class API_AVAILABLE(macos(13.3)) Authenticator : public FidoAuthenticator {
ToVector(cred.userHandle), cred.name.UTF8String, ToVector(cred.userHandle), cred.name.UTF8String,
/* iCloud Keychain does not store /* iCloud Keychain does not store
a displayName for passkeys */ a displayName for passkeys */
std::nullopt)); std::nullopt),
cred.providerName.UTF8String);
} }
const auto has_credentials = const auto has_credentials =
ret.empty() ? FidoRequestHandlerBase::RecognizedCredential:: ret.empty() ? FidoRequestHandlerBase::RecognizedCredential::

@ -511,7 +511,8 @@ TEST_F(iCloudKeychainTest, FetchCredentialMetadata) {
{AuthenticatorType::kICloudKeychain, {AuthenticatorType::kICloudKeychain,
"example.com", "example.com",
{1, 2, 3, 4}, {1, 2, 3, 4},
{{4, 3, 2, 1}, "name", std::nullopt}}}; {{4, 3, 2, 1}, "name", std::nullopt},
"Example provider"}};
fake_->SetCredentials(creds); fake_->SetCredentials(creds);
base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>, base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>,
FidoRequestHandlerBase::RecognizedCredential> FidoRequestHandlerBase::RecognizedCredential>
@ -538,11 +539,13 @@ TEST_F(iCloudKeychainTest, FetchCredentialMetadataWithAllowlist) {
{AuthenticatorType::kICloudKeychain, {AuthenticatorType::kICloudKeychain,
"example.com", "example.com",
{1, 2, 3, 4}, {1, 2, 3, 4},
{{4, 3, 2, 1}, "name", std::nullopt}}, {{4, 3, 2, 1}, "name", std::nullopt},
"Example provider"},
{AuthenticatorType::kICloudKeychain, {AuthenticatorType::kICloudKeychain,
"example.com", "example.com",
{1, 2, 3, 5}, {1, 2, 3, 5},
{{4, 3, 2, 2}, "name", std::nullopt}}, {{4, 3, 2, 2}, "name", std::nullopt},
"Example provider"},
}; };
fake_->SetCredentials(creds); fake_->SetCredentials(creds);
base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>, base::test::TestFuture<std::vector<DiscoverableCredentialMetadata>,

@ -41,7 +41,8 @@ void VirtualFidoDeviceAuthenticator::GetPlatformCredentialInfoForRequest(
}))) { }))) {
credentials.emplace_back( credentials.emplace_back(
AuthenticatorType::kOther, request.rp_id, registration.first, AuthenticatorType::kOther, request.rp_id, registration.first,
registration.second.user.value_or(PublicKeyCredentialUserEntity())); registration.second.user.value_or(PublicKeyCredentialUserEntity()),
std::nullopt);
} }
} }
FidoRequestHandlerBase::RecognizedCredential has_credentials = FidoRequestHandlerBase::RecognizedCredential has_credentials =

@ -125,7 +125,7 @@ TEST_F(VirtualFidoDeviceAuthenticatorTest,
EXPECT_TRUE(future.Wait()); EXPECT_TRUE(future.Wait());
DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata( DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata(
AuthenticatorType::kOther, kRpId, credential_id, AuthenticatorType::kOther, kRpId, credential_id,
PublicKeyCredentialUserEntity()); PublicKeyCredentialUserEntity(), /*provider_name=*/std::nullopt);
EXPECT_THAT(std::get<0>(future.Get()), EXPECT_THAT(std::get<0>(future.Get()),
testing::UnorderedElementsAre(expected)); testing::UnorderedElementsAre(expected));
EXPECT_EQ( EXPECT_EQ(
@ -147,10 +147,12 @@ TEST_F(VirtualFidoDeviceAuthenticatorTest,
authenticator_->GetPlatformCredentialInfoForRequest( authenticator_->GetPlatformCredentialInfoForRequest(
request, CtapGetAssertionOptions(), future.GetCallback()); request, CtapGetAssertionOptions(), future.GetCallback());
EXPECT_TRUE(future.Wait()); EXPECT_TRUE(future.Wait());
DiscoverableCredentialMetadata expected1 = DiscoverableCredentialMetadata( DiscoverableCredentialMetadata expected1 =
AuthenticatorType::kOther, kRpId, id1, user1); DiscoverableCredentialMetadata(AuthenticatorType::kOther, kRpId, id1,
DiscoverableCredentialMetadata expected2 = DiscoverableCredentialMetadata( user1, /*provider_name=*/std::nullopt);
AuthenticatorType::kOther, kRpId, id2, user2); DiscoverableCredentialMetadata expected2 =
DiscoverableCredentialMetadata(AuthenticatorType::kOther, kRpId, id2,
user2, /*provider_name=*/std::nullopt);
EXPECT_THAT(std::get<0>(future.Get()), EXPECT_THAT(std::get<0>(future.Get()),
testing::UnorderedElementsAre(expected1, expected2)); testing::UnorderedElementsAre(expected1, expected2));
EXPECT_EQ( EXPECT_EQ(

@ -128,7 +128,8 @@ TEST_F(WinAuthenticatorTest,
EXPECT_TRUE(future.Wait()); EXPECT_TRUE(future.Wait());
DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata( DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata(
AuthenticatorType::kWinNative, kRpId, kCredentialId, user); AuthenticatorType::kWinNative, kRpId, kCredentialId, user,
/*provider_name=*/std::nullopt);
EXPECT_EQ(std::get<0>(future.Get()), EXPECT_EQ(std::get<0>(future.Get()),
std::vector<DiscoverableCredentialMetadata>{expected}); std::vector<DiscoverableCredentialMetadata>{expected});
EXPECT_EQ( EXPECT_EQ(
@ -202,7 +203,8 @@ TEST_F(WinAuthenticatorTest, GetCredentialInformationForRequest_Unsupported) {
EXPECT_TRUE(future.Wait()); EXPECT_TRUE(future.Wait());
DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata( DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata(
AuthenticatorType::kWinNative, kRpId, kCredentialId, user); AuthenticatorType::kWinNative, kRpId, kCredentialId, user,
/*provider_name=*/std::nullopt);
EXPECT_EQ(std::get<0>(future.Get()), EXPECT_EQ(std::get<0>(future.Get()),
std::vector<DiscoverableCredentialMetadata>{}); std::vector<DiscoverableCredentialMetadata>{});
EXPECT_EQ(std::get<1>(future.Get()), EXPECT_EQ(std::get<1>(future.Get()),
@ -230,7 +232,8 @@ TEST_F(WinAuthenticatorTest,
EXPECT_TRUE(future.Wait()); EXPECT_TRUE(future.Wait());
DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata( DiscoverableCredentialMetadata expected = DiscoverableCredentialMetadata(
AuthenticatorType::kWinNative, kRpId, kCredentialId, user1); AuthenticatorType::kWinNative, kRpId, kCredentialId, user1,
/*provider_name=*/std::nullopt);
EXPECT_THAT(std::get<0>(future.Get()), testing::ElementsAre(expected)); EXPECT_THAT(std::get<0>(future.Get()), testing::ElementsAre(expected));
EXPECT_EQ( EXPECT_EQ(
std::get<1>(future.Get()), std::get<1>(future.Get()),

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