WebAuthn: Centralize icon logic and use color icons for immediate mode
Implementation
Light: photos.app.goo.gl/jYhmR21XkMRuQGoW8
Dark: photos.app.goo.gl/wAxw9D81wghxHbw28
Bug: 393055190
Change-Id: If0ab5e1c94f30a82a19297e441e6a4a87af9e44b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6494562
Commit-Queue: Adem Derinel <derinel@google.com>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Reviewed-by: Alex Ilin <alexilin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1453801}
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index ab5b009b..c02b08d 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -217,6 +217,7 @@
"web.icon",
"webauthn/camera.icon",
"webauthn/icloud_keychain.icon",
+ "webauthn/icloud_keychain_color.icon",
"webauthn/passkey_aoa.icon",
"webauthn/passkey_aoa_dark.icon",
"webauthn/passkey_error.icon",
@@ -232,6 +233,7 @@
"webauthn/usb_security_key.icon",
"webauthn/webauthn_error.icon",
"webauthn/webauthn_error_dark.icon",
+ "webauthn/windows_hello_color.icon",
"webid/webid_arrow.icon",
"webid/webid_globe.icon",
"zoom_in.icon",
diff --git a/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon b/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon
new file mode 100644
index 0000000..4cd16f4d
--- /dev/null
+++ b/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 170,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0x4C, 0xD3, 0x6D,
+MOVE_TO, 84.03f, 11.96f,
+H_LINE_TO, 84,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, -37.57f, 38.1f,
+R_CUBIC_TO, 0, 15.83f, 9.78f, 30, 24.57f, 35.63f,
+R_V_LINE_TO, 59.13f,
+R_LINE_TO, 13.74f, 13.74f,
+R_LINE_TO, 6.66f, -6.66f,
+R_LINE_TO, -1.95f, -5.21f,
+R_H_LINE_TO, -0.2f,
+R_V_LINE_TO, -56.5f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, -24.34f, -40.77f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, 19.1f, -37.46f,
+CLOSE,
+NEW_PATH,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0xFD, 0xCE, 0x48,
+MOVE_TO, 57.59f, 11.96f,
+R_H_LINE_TO, -0.01f,
+ARC_TO, 38.13f, 38.13f, 0, 0, 0, 20, 50.06f,
+R_CUBIC_TO, 0, 15.83f, 9.78f, 30, 24.57f, 35.63f,
+R_V_LINE_TO, 59.13f,
+R_LINE_TO, 13.74f, 13.74f,
+R_LINE_TO, 6.67f, -6.66f,
+R_LINE_TO, -1.96f, -5.21f,
+R_H_LINE_TO, -0.2f,
+R_V_LINE_TO, -56.5f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, -24.33f, -40.77f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, 19.1f, -37.46f,
+CLOSE,
+NEW_PATH,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0x44, 0xA4, 0xF9,
+MOVE_TO, 111.01f, 11.93f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, -38.13f, 38.14f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, 24.56f, 35.62f,
+R_V_LINE_TO, 7.9f,
+R_H_LINE_TO, 0.16f,
+R_LINE_TO, -0.16f, 51.23f,
+R_LINE_TO, 13.75f, 13.74f,
+R_LINE_TO, 21.99f, -21.99f,
+R_LINE_TO, -13.63f, -13.63f,
+R_LINE_TO, 18.3f, -18.3f,
+R_LINE_TO, -16.2f, -16.2f,
+R_H_LINE_TO, -0.23f,
+R_V_LINE_TO, -1.71f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, 27.73f, -36.66f,
+ARC_TO, 38.13f, 38.13f, 0, 0, 0, 111, 11.93f,
+CLOSE,
+R_MOVE_TO, 0.23f, 13.69f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, 10.88f, 10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, -10.88f, 10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, -10.87f, -10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, 10.87f, -10.88f,
+CLOSE
diff --git a/chrome/app/vector_icons/webauthn/windows_hello_color.icon b/chrome/app/vector_icons/webauthn/windows_hello_color.icon
new file mode 100644
index 0000000..d814772
--- /dev/null
+++ b/chrome/app/vector_icons/webauthn/windows_hello_color.icon
@@ -0,0 +1,38 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 200,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 40.7f, 11.1f,
+R_CUBIC_TO, -11.6f, 2.8f, -18.9f, 12.1f, -18.9f, 23.9f,
+R_CUBIC_TO, 0, 6.6f, 1.8f, 11.3f, 6.1f, 16.2f,
+R_CUBIC_TO, 11, 12.5f, 32.7f, 9.5f, 40, -5.5f,
+R_CUBIC_TO, 2.7f, -5.6f, 2.8f, -15.6f, 0.2f, -21.2f,
+R_CUBIC_TO, -4.6f, -10, -16.8f, -16, -27.4f, -13.4f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 148.7f, 11,
+R_CUBIC_TO, -7.9f, 2, -14.1f, 7.3f, -17.2f, 14.7f,
+R_CUBIC_TO, -3.2f, 7.7f, -1, 19.9f, 4.8f, 26.2f,
+R_CUBIC_TO, 5.7f, 6.2f, 19.2f, 9, 27.1f, 5.7f,
+R_CUBIC_TO, 15.9f, -6.6f, 20, -27.8f, 8, -40,
+R_CUBIC_TO, -5.5f, -5.6f, -15.3f, -8.4f, -22.7f, -6.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 6.2f, 108,
+R_CUBIC_TO, -4, 2.4f, -6.6f, 8, -5.7f, 12.2f,
+R_CUBIC_TO, 2.1f, 9.3f, 23.1f, 31.9f, 38.8f, 41.6f,
+R_CUBIC_TO, 38.5f, 23.9f, 82.9f, 23.9f, 121.4f, 0,
+R_CUBIC_TO, 15.7f, -9.7f, 36.7f, -32.3f, 38.8f, -41.6f,
+R_CUBIC_TO, 1.4f, -6.7f, -5.1f, -14.2f, -12.3f, -14.2f,
+R_CUBIC_TO, -5, 0, -8.5f, 2.3f, -14.2f, 9.4f,
+R_CUBIC_TO, -40.7f, 51.2f, -105.3f, 51.2f, -146, 0,
+R_CUBIC_TO, -2.9f, -3.5f, -6.5f, -7.1f, -8.1f, -7.9f,
+R_CUBIC_TO, -3.9f, -2.1f, -8.8f, -1.9f, -12.7f, 0.5f,
+CLOSE
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
index 47f1b63..6b7fcab 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
@@ -25,6 +25,7 @@
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
+#include "base/functional/overloaded.h"
#include "base/i18n/string_compare.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
@@ -41,6 +42,7 @@
#include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/ui/passwords/ui_utils.h"
#include "chrome/browser/ui/webauthn/ambient/ambient_signin_controller.h"
#include "chrome/browser/ui/webauthn/passkey_upgrade_request_controller.h"
#include "chrome/browser/ui/webauthn/user_actions.h"
@@ -380,6 +382,64 @@
ui_presentation == UIPresentation::kModalImmediate;
}
+// Returns the vector icon associated with the given mechanism type.
+// For Mechanism::WindowsAPI, the effective transport must be provided.
+const gfx::VectorIcon& GetMechanismIcon(
+ const Mechanism::Type& type,
+ content::AuthenticatorRequestClientDelegate::UIPresentation ui_presentation,
+ std::optional<AuthenticatorTransport> effective_transport = std::nullopt) {
+ return std::visit(
+ base::Overloaded{
+ [ui_presentation](const Mechanism::Credential& credential)
+ -> const gfx::VectorIcon& {
+ if (ui_presentation == UIPresentation::kModalImmediate) {
+ switch (credential.value().source) {
+ case AuthenticatorType::kICloudKeychain:
+ return kIcloudKeychainColorIcon;
+ case AuthenticatorType::kEnclave:
+ return GooglePasswordManagerVectorIcon();
+ case AuthenticatorType::kWinNative:
+ return kWindowsHelloColorIcon;
+ case AuthenticatorType::kTouchID:
+ return vector_icons::kProductRefreshIcon;
+ default:
+ break;
+ }
+ }
+ // Default icon for non-immediate mode or other credential sources.
+ return GetCredentialIcon(credential.value().source);
+ },
+ [](const Mechanism::Password&) -> const gfx::VectorIcon& {
+ return GooglePasswordManagerVectorIcon();
+ },
+ [](const Mechanism::Transport& transport) -> const gfx::VectorIcon& {
+ return GetTransportIcon(transport.value());
+ },
+ [&effective_transport](
+ const Mechanism::WindowsAPI&) -> const gfx::VectorIcon& {
+ CHECK(effective_transport.has_value());
+ return GetTransportIcon(*effective_transport);
+ },
+ [](const Mechanism::ICloudKeychain&) -> const gfx::VectorIcon& {
+ // Always use the standard iCloud Keychain icon here.
+ return kIcloudKeychainIcon;
+ },
+ [](const Mechanism::Phone&) -> const gfx::VectorIcon& {
+ return kSmartphoneIcon;
+ },
+ [](const Mechanism::AddPhone&) -> const gfx::VectorIcon& {
+ return kQrcodeGeneratorIcon;
+ },
+ [](const Mechanism::Enclave&) -> const gfx::VectorIcon& {
+ // Always use the standard password manager icon here.
+ return vector_icons::kPasswordManagerIcon;
+ },
+ [](const Mechanism::SignInAgain&) -> const gfx::VectorIcon& {
+ return vector_icons::kSyncIcon;
+ }},
+ type);
+}
+
} // namespace
AuthenticatorRequestDialogController::EphemeralState::EphemeralState() =
@@ -2157,10 +2217,11 @@
specific_local_passkeys_listed = true;
}
std::u16string name = base::UTF8ToUTF16(cred.user.name.value_or(""));
+ Mechanism::Type mechanism_type =
+ Mechanism::Credential({cred.source, cred.user.id});
auto& mechanism = model_->mechanisms.emplace_back(
- AuthenticatorRequestDialogModel::Mechanism::Credential(
- {cred.source, cred.user.id}),
- name, name, GetCredentialIcon(cred.source),
+ mechanism_type, name, name,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
base::IgnoreResult(
&AuthenticatorRequestDialogController::OnAccountPreselected),
@@ -2240,8 +2301,10 @@
device::AuthenticatorAttachment::kCrossPlatform) {
const std::u16string name =
l10n_util::GetStringUTF16(IDS_WEBAUTHN_SOURCE_GOOGLE_PASSWORD_MANAGER);
+ Mechanism::Type mechanism_type = Mechanism::Enclave();
Mechanism mechanism(
- Mechanism::Enclave(), name, name, vector_icons::kPasswordManagerIcon,
+ mechanism_type, name, name,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(&AuthenticatorRequestDialogController::StartEnclave,
base::Unretained(this)));
mechanism.description = base::UTF8ToUTF16(model_->GetGpmAccountEmail());
@@ -2249,15 +2312,17 @@
}
if (enclave_enabled_status_ ==
EnclaveEnabledStatus::kEnabledAndReauthNeeded &&
- UIPresentation::kModal == ui_presentation() &&
+ UIPresentation::kModal == ui_presentation() &&
model_->relying_party_id != "google.com") {
// Show a button that lets the user sign in again to restore sync. This
// cancels the request, so we can't do it for conditional UI requests.
// TODO(crbug.com/345413738): add support for conditional UI.
const std::u16string name =
l10n_util::GetStringUTF16(IDS_WEBAUTHN_SIGN_IN_AGAIN_TITLE);
+ Mechanism::Type mechanism_type = Mechanism::SignInAgain();
Mechanism enclave(
- Mechanism::SignInAgain(), name, name, vector_icons::kSyncIcon,
+ mechanism_type, name, name,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogController::ReauthForSyncRestore,
base::Unretained(this)));
@@ -2275,8 +2340,10 @@
device::FidoRequestHandlerBase::RecognizedCredential::kUnknown)) {
const std::u16string name =
l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_ICLOUD_KEYCHAIN);
+ Mechanism::Type mechanism_type = Mechanism::ICloudKeychain();
model_->mechanisms.emplace_back(
- Mechanism::ICloudKeychain(), name, name, kIcloudKeychainIcon,
+ mechanism_type, name, name,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogController::StartICloudKeychain,
base::Unretained(this)));
@@ -2310,9 +2377,10 @@
gfx::ElideString(name16, kMaxLongNameChars, &long_name);
gfx::ElideString(name16, kMaxShortNameChars, &short_name);
+ Mechanism::Type mechanism_type = Mechanism::Phone(phone_name);
model_->mechanisms.emplace_back(
- Mechanism::Phone(phone_name), std::move(long_name),
- std::move(short_name), kSmartphoneIcon,
+ mechanism_type, std::move(long_name), std::move(short_name),
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogController::ContactPhone,
base::Unretained(this), phone_name));
@@ -2354,8 +2422,10 @@
!include_usb_option;
std::u16string label = l10n_util::GetStringUTF16(
GetHybridButtonLabel(merge_usb_and_hybrid, specific_phones_listed));
+ Mechanism::Type mechanism_type = Mechanism::AddPhone();
model_->mechanisms.emplace_back(
- Mechanism::AddPhone(), label, label, kQrcodeGeneratorIcon,
+ mechanism_type, label, label,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogController::StartGuidedFlowForAddPhone,
base::Unretained(this)));
@@ -2371,9 +2441,11 @@
continue;
}
+ Mechanism::Type mechanism_type = Mechanism::Transport(transport);
model_->mechanisms.emplace_back(
- Mechanism::Transport(transport), GetTransportDescription(transport),
- GetTransportShortDescription(transport), GetTransportIcon(transport),
+ mechanism_type, GetTransportDescription(transport),
+ GetTransportShortDescription(transport),
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogController::StartGuidedFlowForTransport,
base::Unretained(this), transport));
@@ -2389,8 +2461,10 @@
int label,
AuthenticatorTransport transport) {
const std::u16string desc = l10n_util::GetStringUTF16(label);
+ Mechanism::Type mechanism_type = Mechanism::WindowsAPI();
model_->mechanisms.emplace_back(
- Mechanism::WindowsAPI(), desc, desc, GetTransportIcon(transport),
+ mechanism_type, desc, desc,
+ GetMechanismIcon(mechanism_type, ui_presentation(), transport),
base::BindRepeating(
&AuthenticatorRequestDialogController::StartWinNativeApi,
base::Unretained(this)));
@@ -2610,9 +2684,10 @@
void AuthenticatorRequestDialogController::PopulatePasswords() {
for (const auto& password : passwords_) {
+ Mechanism::Type mechanism_type = Mechanism::Password();
Mechanism mechanism(
- Mechanism::Password(), password->username_value,
- password->username_value, vector_icons::kPasswordManagerIcon,
+ mechanism_type, password->username_value, password->username_value,
+ GetMechanismIcon(mechanism_type, ui_presentation()),
base::BindRepeating(
&AuthenticatorRequestDialogModel::OnPasswordCredentialSelected,
base::Unretained(model_),