blob: 5a8cf0f824e1d0fbcde1374d2ff9f41078cb2db3 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webauthn/user_actions.h"
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <variant>
#include <vector>
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/strcat.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_types.h"
#include "ui/gfx/vector_icon_types.h"
namespace webauthn::user_actions {
namespace {
using Mechanism = AuthenticatorRequestDialogModel::Mechanism;
using AuthenticatorType = device::AuthenticatorType;
constexpr std::string_view kGpmAndICloudAndProfile = "GpmAndICloudAndProfile";
constexpr std::string_view kGpmAndICloud = "GpmAndICloud";
constexpr std::string_view kGpmAndWin = "GpmAndWin";
constexpr std::string_view kGpmAndProfile = "GpmAndProfile";
constexpr std::string_view kICloudAndProfile = "ICloudAndProfile";
constexpr std::string_view kGpmOnly = "GpmOnly";
constexpr std::string_view kICloudOnly = "ICloudOnly";
constexpr std::string_view kWinOnly = "WinOnly";
constexpr std::string_view kProfileOnly = "ProfileOnly";
constexpr std::string_view kPasswordOnly = "PasswordOnly";
constexpr std::string_view kOthers = "Others";
enum class AuthenticatorCategory {
kGpm,
kICloud,
kWindows,
kProfile,
kPassword,
kOther,
};
AuthenticatorCategory CategoryFromMechanism(const Mechanism& mechanism) {
if (std::holds_alternative<Mechanism::Credential>(mechanism.type)) {
switch (std::get<Mechanism::Credential>(mechanism.type)->source) {
case AuthenticatorType::kEnclave:
return AuthenticatorCategory::kGpm;
case AuthenticatorType::kTouchID:
return AuthenticatorCategory::kProfile;
case AuthenticatorType::kICloudKeychain:
return AuthenticatorCategory::kICloud;
case AuthenticatorType::kWinNative:
return AuthenticatorCategory::kWindows;
case AuthenticatorType::kChromeOS:
case AuthenticatorType::kPhone:
case AuthenticatorType::kOther:
return AuthenticatorCategory::kOther;
}
} else if (std::holds_alternative<Mechanism::Password>(mechanism.type)) {
return AuthenticatorCategory::kPassword;
} else if (std::holds_alternative<Mechanism::Enclave>(mechanism.type)) {
return AuthenticatorCategory::kGpm;
} else if (std::holds_alternative<Mechanism::WindowsAPI>(mechanism.type)) {
return AuthenticatorCategory::kWindows;
} else if (std::holds_alternative<Mechanism::ICloudKeychain>(
mechanism.type)) {
return AuthenticatorCategory::kICloud;
}
return AuthenticatorCategory::kOther;
}
std::tuple<bool, bool, bool, bool> AuthenticatorsAvailable(
const std::vector<Mechanism>& mechanisms) {
bool has_gpm = false;
bool has_icloud = false;
bool has_profile = false;
bool has_win = false;
// TODO(derinel): Add ChromeOS combinations.
for (const auto& mech : mechanisms) {
switch (CategoryFromMechanism(mech)) {
case AuthenticatorCategory::kGpm:
has_gpm = true;
break;
case AuthenticatorCategory::kProfile:
has_profile = true;
break;
case AuthenticatorCategory::kICloud:
has_icloud = true;
break;
case AuthenticatorCategory::kWindows:
has_win = true;
break;
case AuthenticatorCategory::kPassword:
case AuthenticatorCategory::kOther:
break;
}
}
return {has_gpm, has_icloud, has_profile, has_win};
}
} // namespace
void RecordMultipleOptionsShown(const std::vector<Mechanism>& mechanisms,
device::FidoRequestType request_type) {
bool has_gpm = false;
bool has_icloud = false;
bool has_profile = false;
bool has_win = false;
std::tie(has_gpm, has_icloud, has_profile, has_win) =
AuthenticatorsAvailable(mechanisms);
std::string_view metric_to_emit;
if (has_gpm && has_icloud && has_profile) {
metric_to_emit = kGpmAndICloudAndProfile;
} else if (has_gpm && has_icloud) {
metric_to_emit = kGpmAndICloud;
} else if (has_gpm && has_win) {
metric_to_emit = kGpmAndWin;
} else if (has_gpm && has_profile) {
metric_to_emit = kGpmAndProfile;
} else if (has_gpm) {
metric_to_emit = kGpmOnly;
} else if (has_icloud && has_profile) {
metric_to_emit = kICloudAndProfile;
} else if (has_icloud) {
metric_to_emit = kICloudOnly;
} else if (has_win) {
metric_to_emit = kWinOnly;
} else if (has_profile) {
metric_to_emit = kProfileOnly;
} else {
metric_to_emit = kOthers;
}
std::string metric;
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
metric = base::StrCat(
{"WebAuthn.MakeCredential.MultipleOptionsShown.", metric_to_emit});
break;
case device::FidoRequestType::kGetAssertion:
metric = base::StrCat(
{"WebAuthn.GetAssertion.MultipleOptionsShown.", metric_to_emit});
break;
}
base::RecordAction(base::UserMetricsAction(metric.c_str()));
}
void RecordPriorityOptionShown(const Mechanism& mechanism) {
std::optional<std::string_view> metric_to_emit;
switch (CategoryFromMechanism(mechanism)) {
case AuthenticatorCategory::kGpm:
metric_to_emit = kGpmOnly;
break;
case AuthenticatorCategory::kProfile:
metric_to_emit = kProfileOnly;
break;
case AuthenticatorCategory::kICloud:
metric_to_emit = kICloudOnly;
break;
case AuthenticatorCategory::kWindows:
metric_to_emit = kWinOnly;
break;
case AuthenticatorCategory::kPassword:
case AuthenticatorCategory::kOther:
break;
}
if (metric_to_emit.has_value()) {
base::RecordAction(base::UserMetricsAction(
base::StrCat(
{"WebAuthn.GetAssertion.PriorityOptionShown.", *metric_to_emit})
.c_str()));
}
}
void RecordCombinedSelectorShown() {
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.CombinedSelectorShown"));
}
void RecordHybridAndSecurityKeyDialogShown(
device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.MakeCredential.HybridAndSecurityKeyDialogShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.GetAssertion.HybridAndSecurityKeyDialogShown"));
break;
}
}
void RecordSecurityKeyDialogShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.MakeCredential.SecurityKeyDialogShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.GetAssertion.SecurityKeyDialogShown"));
break;
}
}
void RecordMechanismClick(const Mechanism& mech) {
std::string_view metric_to_emit;
switch (CategoryFromMechanism(mech)) {
case AuthenticatorCategory::kGpm:
metric_to_emit = kGpmOnly;
break;
case AuthenticatorCategory::kProfile:
metric_to_emit = kProfileOnly;
break;
case AuthenticatorCategory::kICloud:
metric_to_emit = kICloudOnly;
break;
case AuthenticatorCategory::kWindows:
metric_to_emit = kWinOnly;
break;
case AuthenticatorCategory::kPassword:
metric_to_emit = kPasswordOnly;
break;
case AuthenticatorCategory::kOther:
metric_to_emit = kOthers;
break;
}
base::RecordAction(base::UserMetricsAction(
base::StrCat({"WebAuthn.Dialog.UserSelected.", metric_to_emit}).c_str()));
}
void RecordCancelClick() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Dialog.Cancelled"));
}
void RecordAcceptClick() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Dialog.Accepted"));
}
void RecordTrustDialogShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.MakeCredential.TrustGpmDialogShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.TrustGpmDialogShown"));
break;
}
}
void RecordCreateGpmDialogShown() {
base::RecordAction(
base::UserMetricsAction("WebAuthn.MakeCredential.CreateGpmDialogShown"));
}
void RecordRecoveryShown(device::FidoRequestType type) {
switch (type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(
base::UserMetricsAction("WebAuthn.MakeCredential.RecoverGpmShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.RecoverGpmShown"));
break;
}
}
void RecordRecoveryCancelled() {
base::RecordAction(
base::UserMetricsAction("WebAuthn.Window.RecoverGpmCancelled"));
}
void RecordRecoverySucceeded() {
base::RecordAction(
base::UserMetricsAction("WebAuthn.Window.RecoverGpmSucceeded"));
}
void RecordICloudShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(
base::UserMetricsAction("WebAuthn.MakeCredential.ICloudShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.ICloudShown"));
break;
}
}
void RecordICloudCancelled() {
base::RecordAction(base::UserMetricsAction("WebAuthn.ICloud.Cancelled"));
}
void RecordICloudSuccess() {
base::RecordAction(base::UserMetricsAction("WebAuthn.ICloud.Success"));
}
void RecordGpmTouchIdDialogShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.MakeCredential.GpmTouchIdDialogShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.GetAssertion.GpmTouchIdDialogShown"));
break;
}
}
void RecordGpmPinSheetShown(device::FidoRequestType request_type,
bool is_pin_creation,
bool is_arbitrary) {
std::string_view webauthn_request_type;
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
webauthn_request_type = "MakeCredential.";
break;
case device::FidoRequestType::kGetAssertion:
webauthn_request_type = "GetAssertion.";
break;
}
std::string_view pin_mode = is_pin_creation ? "GpmCreate" : "GpmEnter";
std::string_view pin_type = is_arbitrary ? "Arbitrary" : "";
base::RecordAction(base::UserMetricsAction(
base::StrCat({"WebAuthn.", webauthn_request_type, pin_mode, pin_type,
"PinDialogShown"})
.c_str()));
}
void RecordGpmForgotPinClick() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Gpm.ForgotPinClicked"));
}
void RecordGpmPinOptionChangeClick() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Gpm.PinOptionChanged"));
}
void RecordGpmLockedShown() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Gpm.LockedDialogShown"));
}
void RecordGpmWinUvShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(
base::UserMetricsAction("WebAuthn.MakeCredential.GpmWinUvShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.GpmWinUvShown"));
break;
}
}
void RecordGpmSuccess() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Gpm.Success"));
}
void RecordGpmFailureShown() {
base::RecordAction(base::UserMetricsAction("WebAuthn.Gpm.Failure"));
}
void RecordChromeProfileAuthenticatorShown(
device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.MakeCredential.ChromeProfileAuthenticatorShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(base::UserMetricsAction(
"WebAuthn.GetAssertion.ChromeProfileAuthenticatorShown"));
break;
}
}
void RecordChromeProfileCancelled() {
base::RecordAction(
base::UserMetricsAction("WebAuthn.ChromeProfile.Cancelled"));
}
void RecordChromeProfileSuccess() {
base::RecordAction(base::UserMetricsAction("WebAuthn.ChromeProfile.Success"));
}
void RecordWindowsHelloShown(device::FidoRequestType request_type) {
switch (request_type) {
case device::FidoRequestType::kMakeCredential:
base::RecordAction(
base::UserMetricsAction("WebAuthn.MakeCredential.WinHelloShown"));
break;
case device::FidoRequestType::kGetAssertion:
base::RecordAction(
base::UserMetricsAction("WebAuthn.GetAssertion.WinHelloShown"));
break;
}
}
void RecordWindowsHelloCancelled() {
base::RecordAction(base::UserMetricsAction("WebAuthn.WinHello.Cancelled"));
}
void RecordWindowsHelloSuccess() {
base::RecordAction(base::UserMetricsAction("WebAuthn.WinHello.Success"));
}
void RecordContextMenuEntryClick() {
base::RecordAction(base::UserMetricsAction(
"WebAuthn.ContextMenu.PasskeyFromAnotherDeviceClicked"));
}
} // namespace webauthn::user_actions