webauthn: stop considering the last-used transport.

We only have three transports in practice: USB, internal, and caBLE/AOA.
We already have logic that ignores kInternal as a last-used transport,
and caBLE is always selected if the extension is provided anyway. So
remembering the last-used transport does nothing.

Change-Id: I0b3b007dcd98376e70cddf02f7e6dd66be35aca3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2803514
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Martin Kreichgauer <[email protected]>
Reviewed-by: Avi Drissman <[email protected]>
Commit-Queue: Adam Langley <[email protected]>
Cr-Commit-Position: refs/heads/master@{#870569}
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 245688a1..fa13f8b 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -538,6 +538,10 @@
     "optimization_guide.session_statistic.fcp_mean";
 const char kSessionStatisticFCPStdDev[] =
     "optimization_guide.session_statistic.fcp_std_dev";
+#if !defined(OS_ANDROID)
+const char kWebAuthnLastTransportUsedPrefName[] =
+    "webauthn.last_transport_used";
+#endif
 
 // Register local state used only for migration (clearing or moving to a new
 // key).
@@ -649,6 +653,8 @@
   registry->RegisterBooleanPref(prefs::kMediaFeedsBackgroundFetching, false);
   registry->RegisterBooleanPref(prefs::kMediaFeedsSafeSearchEnabled, false);
   registry->RegisterBooleanPref(prefs::kMediaFeedsAutoSelectEnabled, false);
+  registry->RegisterStringPref(kWebAuthnLastTransportUsedPrefName,
+                               std::string());
 #endif
 
   registry->RegisterDoublePref(kSessionStatisticFCPStdDev, -1.0f);
@@ -1335,6 +1341,7 @@
   profile_prefs->ClearPref(prefs::kMediaFeedsBackgroundFetching);
   profile_prefs->ClearPref(prefs::kMediaFeedsSafeSearchEnabled);
   profile_prefs->ClearPref(prefs::kMediaFeedsAutoSelectEnabled);
+  profile_prefs->ClearPref(kWebAuthnLastTransportUsedPrefName);
 #endif
   // Added 04/2021.
   profile_prefs->ClearPref(kSessionStatisticFCPMean);
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index 3f9d124..59f42d7 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -49,8 +49,8 @@
     model->set_cable_transport_info(/*cable_extension_provided=*/true,
                                     /*has_paired_phones=*/false,
                                     "fido://qrcode");
-    model->StartFlow(std::move(transport_availability), base::nullopt,
-                     /*is_conditional=*/false);
+    model->StartFlow(std::move(transport_availability),
+                     /*use_location_bar_bubble=*/false);
 
     // The dialog should immediately close as soon as it is displayed.
     if (name == "transports") {
diff --git a/chrome/browser/ui/webauthn/transport_hover_list_model.h b/chrome/browser/ui/webauthn/transport_hover_list_model.h
index 9156c06..554b02c 100644
--- a/chrome/browser/ui/webauthn/transport_hover_list_model.h
+++ b/chrome/browser/ui/webauthn/transport_hover_list_model.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <vector>
 
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/webauthn/hover_list_model.h"
 #include "chrome/browser/webauthn/authenticator_transport.h"
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index cfb53d5..c53eeb27 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -25,7 +25,6 @@
 base::Optional<device::FidoTransportProtocol> SelectMostLikelyTransport(
     const device::FidoRequestHandlerBase::TransportAvailabilityInfo&
         transport_availability,
-    base::Optional<device::FidoTransportProtocol> last_used_transport,
     bool cable_extension_provided,
     bool have_paired_phones) {
   const base::flat_set<AuthenticatorTransport>& candidate_transports(
@@ -62,31 +61,7 @@
     return AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy;
   }
 
-  // The remaining decisions are based on the most recently used successful
-  // transport.
-  if (!last_used_transport ||
-      !base::Contains(candidate_transports, *last_used_transport)) {
-    return base::nullopt;
-  }
-
-  // Auto-advancing to platform authenticator based on credential availability
-  // has been handled above. Hence, at this point it does not have a matching
-  // credential and should not be advanced to, because it would fail
-  // immediately.
-  if (*last_used_transport == device::FidoTransportProtocol::kInternal) {
-    return base::nullopt;
-  }
-
-  // Auto-advancing to caBLE based on a caBLEv1 request extension has been
-  // handled above. For caBLEv2, only auto-advance if the user has previously
-  // paired a caBLEv2 authenticator.
-  if (*last_used_transport ==
-          device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy &&
-      !have_paired_phones) {
-    return base::nullopt;
-  }
-
-  return *last_used_transport;
+  return base::nullopt;
 }
 
 }  // namespace
@@ -121,13 +96,11 @@
 
 void AuthenticatorRequestDialogModel::StartFlow(
     TransportAvailabilityInfo transport_availability,
-    base::Optional<device::FidoTransportProtocol> last_used_transport,
     bool use_location_bar_bubble) {
   use_location_bar_bubble_ = use_location_bar_bubble;
   DCHECK_EQ(current_step(), Step::kNotStarted);
 
   transport_availability_ = std::move(transport_availability);
-  last_used_transport_ = last_used_transport;
 
   if (use_location_bar_bubble_) {
     // This is a conditional request so show a lightweight, non-modal dialog
@@ -173,9 +146,8 @@
     }
   }
 
-  auto most_likely_transport =
-      SelectMostLikelyTransport(transport_availability_, last_used_transport_,
-                                cable_extension_provided_, have_paired_phones_);
+  auto most_likely_transport = SelectMostLikelyTransport(
+      transport_availability_, cable_extension_provided_, have_paired_phones_);
   if (most_likely_transport) {
     StartGuidedFlowForTransport(*most_likely_transport);
   } else if (!transport_availability_.available_transports.empty()) {
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 3816bb7..e7ab49f 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -188,7 +188,6 @@
   // Valid action when at step: kNotStarted.
   void StartFlow(
       TransportAvailabilityInfo transport_availability,
-      base::Optional<device::FidoTransportProtocol> last_used_transport,
       bool use_location_bar_bubble);
 
   // Restarts the UX flow.
@@ -482,9 +481,8 @@
 
   base::ObserverList<Observer>::Unchecked observers_;
 
-  // These fields are only filled out when the UX flow is started.
+  // This field is only filled out once the UX flow is started.
   TransportAvailabilityInfo transport_availability_;
-  base::Optional<device::FidoTransportProtocol> last_used_transport_;
 
   RequestCallback request_callback_;
   base::RepeatingClosure bluetooth_adapter_power_on_callback_;
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
index 8d8fb1b..5c7d9a4d 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -12,6 +13,7 @@
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
@@ -26,6 +28,7 @@
 namespace {
 
 using testing::ElementsAre;
+using RequestType = device::FidoRequestHandlerBase::RequestType;
 
 const base::flat_set<AuthenticatorTransport> kAllTransports = {
     AuthenticatorTransport::kUsbHumanInterfaceDevice,
@@ -80,12 +83,45 @@
   DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterPowerOnCallbackReceiver);
 };
 
+base::StringPiece RequestTypeToString(RequestType req_type) {
+  switch (req_type) {
+    case RequestType::kGetAssertion:
+      return "GetAssertion";
+    case RequestType::kMakeCredential:
+      return "MakeCredential";
+  }
+}
+
+enum class TransportAvailabilityParam {
+  kHasPlatformCredential,
+  kHasWinNativeAuthenticator,
+  kHasCableExtension,
+};
+
+base::StringPiece TransportAvailabilityParamToString(
+    TransportAvailabilityParam param) {
+  switch (param) {
+    case TransportAvailabilityParam::kHasPlatformCredential:
+      return "kHasPlatformCredential";
+    case TransportAvailabilityParam::kHasWinNativeAuthenticator:
+      return "kHasWinNativeAuthenticator";
+    case TransportAvailabilityParam::kHasCableExtension:
+      return "kHasCableExtension";
+  }
+}
+
+template <typename T, base::StringPiece (*F)(T)>
+std::string SetToString(base::flat_set<T> s) {
+  std::vector<base::StringPiece> names;
+  std::transform(s.begin(), s.end(), std::back_inserter(names), F);
+  return base::JoinString(names, ", ");
+}
+
 }  // namespace
 
 class AuthenticatorRequestDialogModelTest : public ::testing::Test {
  public:
   using Step = AuthenticatorRequestDialogModel::Step;
-  using RequestType = ::device::FidoRequestHandlerBase::RequestType;
 
   AuthenticatorRequestDialogModelTest() = default;
 
@@ -98,99 +134,63 @@
 };
 
 TEST_F(AuthenticatorRequestDialogModelTest, TransportAutoSelection) {
-  enum class TransportAvailabilityParam {
-    kHasPlatformCredential,
-    kHasWinNativeAuthenticator,
-    kHasCableExtension,
-  };
   const struct {
     RequestType request_type;
     base::flat_set<AuthenticatorTransport> available_transports;
-    base::Optional<AuthenticatorTransport> last_used_transport;
     base::flat_set<TransportAvailabilityParam> transport_params;
     Step expected_first_step;
   } kTestCases[] = {
       // Only a single transport is available for a GetAssertion call.
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       base::nullopt,
        {},
        Step::kUsbInsertAndActivate},
       {RequestType::kGetAssertion,
-       {AuthenticatorTransport::kNearFieldCommunication},
-       base::nullopt,
-       {},
-       Step::kTransportSelection},
-      {RequestType::kGetAssertion,
        {AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {},
        Step::kErrorInternalUnrecognized},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kInternal},
-       {},
        {TransportAvailabilityParam::kHasPlatformCredential},
        Step::kNotStarted},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy},
-       base::nullopt,
        {TransportAvailabilityParam::kHasCableExtension},
        Step::kCableActivate},
 
-      // The last used transport is available (and caBLE is not).
       {RequestType::kGetAssertion,
        kAllTransportsWithoutCable,
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {},
-       Step::kUsbInsertAndActivate},
+       Step::kTransportSelection},
 
-      // The last used transport is not available.
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kInternal,
         AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       AuthenticatorTransport::kNearFieldCommunication,
        {},
        Step::kTransportSelection},
 
       // The KeyChain contains an allowed Touch ID credential.
       {RequestType::kGetAssertion,
        kAllTransports,
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {TransportAvailabilityParam::kHasPlatformCredential},
        Step::kNotStarted},
 
       // The KeyChain does not contain an allowed Touch ID credential.
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {},
        Step::kErrorInternalUnrecognized},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kInternal,
        {},
        Step::kErrorInternalUnrecognized},
       {RequestType::kGetAssertion,
        kAllTransportsWithoutCable,
-       base::nullopt,
        {},
        Step::kTransportSelection},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice,
         AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
-       {},
-       Step::kUsbInsertAndActivate},
-      {RequestType::kGetAssertion,
-       {AuthenticatorTransport::kUsbHumanInterfaceDevice,
-        AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kBluetoothLowEnergy,
-       {},
-       Step::kTransportSelection},
-      {RequestType::kGetAssertion,
-       {AuthenticatorTransport::kUsbHumanInterfaceDevice,
-        AuthenticatorTransport::kInternal},
-       AuthenticatorTransport::kInternal,
        {},
        Step::kTransportSelection},
 
@@ -198,28 +198,24 @@
       // not enabled by the relying party.
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       base::nullopt,
        {TransportAvailabilityParam::kHasPlatformCredential},
        Step::kUsbInsertAndActivate},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice,
         AuthenticatorTransport::kNearFieldCommunication},
-       base::nullopt,
        {TransportAvailabilityParam::kHasPlatformCredential},
        Step::kTransportSelection},
 
       // If caBLE is one of the allowed transports, it has second-highest
       // priority after Touch ID, and is auto-selected for GetAssertion
-      // operations even if the last used transport was something else.
+      // operations.
       {RequestType::kGetAssertion,
        kAllTransports,
-       base::nullopt,
        {TransportAvailabilityParam::kHasCableExtension},
        Step::kCableActivate},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy,
         AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {TransportAvailabilityParam::kHasCableExtension},
        Step::kCableActivate},
 
@@ -227,22 +223,15 @@
       // calls.
       {RequestType::kMakeCredential,
        kAllTransports,
-       base::nullopt,
        {},
        Step::kTransportSelection},
 
       // No transports available.
-      {RequestType::kGetAssertion,
-       {},
-       AuthenticatorTransport::kNearFieldCommunication,
-       {},
-       Step::kErrorNoAvailableTransports},
+      {RequestType::kGetAssertion, {}, {}, Step::kErrorNoAvailableTransports},
 
-      // Even when last transport used is available, we default to transport
-      // selection modal for MakeCredential.
+      // We default to transport selection modal for MakeCredential.
       {RequestType::kMakeCredential,
        kAllTransports,
-       AuthenticatorTransport::kUsbHumanInterfaceDevice,
        {},
        Step::kTransportSelection},
 
@@ -250,40 +239,43 @@
       // selection view for MakeCredential call.
       {RequestType::kMakeCredential,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       base::nullopt,
        {},
        Step::kUsbInsertAndActivate},
       {RequestType::kMakeCredential,
        {AuthenticatorTransport::kInternal},
-       base::nullopt,
        {},
        Step::kNotStarted},
       {RequestType::kMakeCredential,
        {AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy},
-       base::nullopt,
        {TransportAvailabilityParam::kHasCableExtension},
        Step::kCableActivate},
       {RequestType::kMakeCredential,
        {AuthenticatorTransport::kUsbHumanInterfaceDevice},
-       base::nullopt,
        {},
        Step::kUsbInsertAndActivate},
 
-      // Windows authenticator will bypass the UI unless BLE is also available.
+      // Windows authenticator will bypass the UI unless caBLE is also
+      // available.
       {RequestType::kGetAssertion,
        {},
-       base::nullopt,
        {TransportAvailabilityParam::kHasWinNativeAuthenticator},
        Step::kNotStarted},
       {RequestType::kGetAssertion,
        {AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy},
-       base::nullopt,
        {TransportAvailabilityParam::kHasWinNativeAuthenticator,
         TransportAvailabilityParam::kHasCableExtension},
        Step::kCableActivate},
   };
 
   for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(static_cast<int>(test_case.expected_first_step));
+    SCOPED_TRACE((SetToString<TransportAvailabilityParam,
+                              TransportAvailabilityParamToString>(
+        test_case.transport_params)));
+    SCOPED_TRACE((SetToString<device::FidoTransportProtocol, device::ToString>(
+        test_case.available_transports)));
+    SCOPED_TRACE(RequestTypeToString(test_case.request_type));
+
     TransportAvailabilityInfo transports_info;
     transports_info.is_ble_powered = true;
     transports_info.request_type = test_case.request_type;
@@ -307,7 +299,7 @@
       model.set_cable_transport_info(true, false, base::nullopt);
     }
 
-    model.StartFlow(std::move(transports_info), test_case.last_used_transport,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
     EXPECT_EQ(test_case.expected_first_step, model.current_step());
 
@@ -328,7 +320,7 @@
     model.set_cable_transport_info(cable_extension_provided,
                                    /*has_paired_phones=*/false,
                                    /*cable_qr_string=*/base::nullopt);
-    model.StartFlow(std::move(transports_info), base::nullopt,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
     EXPECT_THAT(model.available_transports(),
                 ::testing::UnorderedElementsAre(
@@ -346,7 +338,6 @@
 
   EXPECT_CALL(mock_observer, OnStepTransition());
   model.StartFlow(TransportAvailabilityInfo(),
-                  AuthenticatorTransport::kInternal,
                   /*use_location_bar_bubble=*/false);
   EXPECT_EQ(Step::kErrorNoAvailableTransports, model.current_step());
   testing::Mock::VerifyAndClearExpectations(&mock_observer);
@@ -384,7 +375,7 @@
     transports_info.available_transports = kAllTransportsWithoutCable;
 
     EXPECT_CALL(mock_observer, OnStepTransition());
-    model.StartFlow(std::move(transports_info), base::nullopt,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
     EXPECT_EQ(Step::kTransportSelection, model.current_step());
     testing::Mock::VerifyAndClearExpectations(&mock_observer);
@@ -423,7 +414,7 @@
     AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.set_cable_transport_info(true, false, base::nullopt);
-    model.StartFlow(std::move(transports_info), base::nullopt,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
     EXPECT_EQ(test_case.expected_final_step, model.current_step());
     EXPECT_TRUE(model.ble_adapter_is_powered());
@@ -452,7 +443,7 @@
     model.AddObserver(&mock_observer);
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.set_cable_transport_info(true, false, base::nullopt);
-    model.StartFlow(std::move(transports_info), base::nullopt,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
 
     EXPECT_EQ(Step::kBlePowerOnManual, model.current_step());
@@ -492,7 +483,7 @@
     AuthenticatorRequestDialogModel model(/*relying_party_id=*/"example.com");
     model.SetBluetoothAdapterPowerOnCallback(power_receiver.GetCallback());
     model.set_cable_transport_info(true, false, base::nullopt);
-    model.StartFlow(std::move(transports_info), base::nullopt,
+    model.StartFlow(std::move(transports_info),
                     /*use_location_bar_bubble=*/false);
 
     EXPECT_EQ(Step::kBlePowerOnAutomatic, model.current_step());
@@ -527,7 +518,7 @@
   model.saved_authenticators().AddAuthenticator(AuthenticatorReference(
       /*device_id=*/"authenticator", AuthenticatorTransport::kInternal));
 
-  model.StartFlow(std::move(transports_info), base::nullopt,
+  model.StartFlow(std::move(transports_info),
                   /*use_location_bar_bubble=*/false);
   EXPECT_EQ(AuthenticatorRequestDialogModel::Step::kTransportSelection,
             model.current_step());
@@ -570,7 +561,7 @@
       },
       &dispatched_authenticator_ids));
 
-  model.StartFlow(std::move(transports_info), base::nullopt,
+  model.StartFlow(std::move(transports_info),
                   /*use_location_bar_bubble=*/false);
 
   EXPECT_TRUE(model.should_dialog_be_hidden());
@@ -595,7 +586,7 @@
   TransportAvailabilityInfo transports_info;
   transports_info.available_transports = kAllTransports;
   transports_info.has_recognized_platform_authenticator_credential = true;
-  model.StartFlow(std::move(transports_info), base::nullopt,
+  model.StartFlow(std::move(transports_info),
                   /*use_location_bar_bubble=*/true);
   EXPECT_EQ(model.current_step(), Step::kLocationBarBubble);
   EXPECT_TRUE(model.should_dialog_be_hidden());
@@ -624,7 +615,7 @@
   device::PublicKeyCredentialUserEntity user_2({5, 6, 7, 8});
   transports_info.recognized_platform_authenticator_credentials = {user_1,
                                                                    user_2};
-  model.StartFlow(std::move(transports_info), base::nullopt,
+  model.StartFlow(std::move(transports_info),
                   /*is_location_bar_bubble_ui==*/true);
   EXPECT_EQ(model.current_step(), Step::kLocationBarBubble);
   EXPECT_TRUE(model.should_dialog_be_hidden());
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index b57e8af..b1d243f 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -138,9 +138,6 @@
     "webauthn.touchid.metadata_secret";
 #endif
 
-const char kWebAuthnLastTransportUsedPrefName[] =
-    "webauthn.last_transport_used";
-
 const char kWebAuthnCablePairingsPrefName[] = "webauthn.cablev2_pairings";
 
 // The |kWebAuthnCablePairingsPrefName| preference contains a list of dicts,
@@ -177,8 +174,6 @@
                                std::string());
 #endif
 
-  registry->RegisterStringPref(kWebAuthnLastTransportUsedPrefName,
-                               std::string());
   registry->RegisterListPref(kWebAuthnCablePairingsPrefName);
 }
 
@@ -496,14 +491,6 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-void ChromeAuthenticatorRequestDelegate::UpdateLastTransportUsed(
-    device::FidoTransportProtocol transport) {
-  PrefService* prefs =
-      Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
-  prefs->SetString(kWebAuthnLastTransportUsedPrefName,
-                   device::ToString(transport));
-}
-
 void ChromeAuthenticatorRequestDelegate::DisableUI() {
   disable_ui_ = true;
 }
@@ -540,8 +527,7 @@
 
   weak_dialog_model_->AddObserver(this);
 
-  weak_dialog_model_->StartFlow(std::move(data), GetLastTransportUsed(),
-                                is_conditional_);
+  weak_dialog_model_->StartFlow(std::move(data), is_conditional_);
 
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(GetRenderFrameHost());
@@ -660,14 +646,6 @@
   std::move(cancel_callback_).Run();
 }
 
-base::Optional<device::FidoTransportProtocol>
-ChromeAuthenticatorRequestDelegate::GetLastTransportUsed() const {
-  PrefService* prefs =
-      Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
-  return device::ConvertToFidoTransportProtocol(
-      prefs->GetString(kWebAuthnLastTransportUsedPrefName));
-}
-
 content::RenderFrameHost*
 ChromeAuthenticatorRequestDelegate::GetRenderFrameHost() const {
   content::RenderFrameHost* ret =
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index d9acb779..ef367eb 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -99,8 +99,6 @@
   bool IsFocused() override;
   base::Optional<bool> IsUserVerifyingPlatformAuthenticatorAvailableOverride()
       override;
-  void UpdateLastTransportUsed(
-      device::FidoTransportProtocol transport) override;
   void DisableUI() override;
   bool IsWebAuthnUIEnabled() override;
   void SetConditionalRequest(bool is_conditional) override;
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
index 74a5bb8..03a7273 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate_unittest.cc
@@ -53,15 +53,6 @@
   AuthenticatorRequestDialogModel::Step last_step_;
 };
 
-TEST_F(ChromeAuthenticatorRequestDelegateTest, TestTransportPrefType) {
-  ChromeAuthenticatorRequestDelegate delegate(main_rfh());
-  EXPECT_FALSE(delegate.GetLastTransportUsed());
-  delegate.UpdateLastTransportUsed(device::FidoTransportProtocol::kInternal);
-  const auto transport = delegate.GetLastTransportUsed();
-  ASSERT_TRUE(transport);
-  EXPECT_EQ(device::FidoTransportProtocol::kInternal, transport);
-}
-
 TEST_F(ChromeAuthenticatorRequestDelegateTest, ConditionalUI) {
   // Enabling conditional mode should cause the modal dialog to stay hidden at
   // the beginning of a request. An omnibar icon might be shown instead.
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 582ac96..78c47da 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -1479,7 +1479,6 @@
       bool is_transport_used_internal = false;
       bool is_transport_used_cable = false;
       if (transport_used) {
-        request_delegate_->UpdateLastTransportUsed(*transport_used);
         is_transport_used_internal =
             *transport_used == device::FidoTransportProtocol::kInternal;
         is_transport_used_cable =
@@ -1693,11 +1692,6 @@
       DCHECK(response_data.has_value());
       DCHECK(authenticator);
 
-      if (authenticator->AuthenticatorTransport()) {
-        request_delegate_->UpdateLastTransportUsed(
-            *authenticator->AuthenticatorTransport());
-      }
-
       // Show an account picker for requests with empty allow lists.
       // Authenticators may omit the identifying information in the user entity
       // if only one credential matches, or if they have account selection UI
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index a97b77b..02a6742 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -99,9 +99,6 @@
   return base::nullopt;
 }
 
-void AuthenticatorRequestClientDelegate::UpdateLastTransportUsed(
-    device::FidoTransportProtocol transport) {}
-
 void AuthenticatorRequestClientDelegate::DisableUI() {}
 
 bool AuthenticatorRequestClientDelegate::IsWebAuthnUIEnabled() {
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index 4a1703f..c36c366d 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -202,10 +202,6 @@
   virtual base::Optional<bool>
   IsUserVerifyingPlatformAuthenticatorAvailableOverride();
 
-  // Saves transport type the user used during WebAuthN API so that the
-  // WebAuthN UI will default to the same transport type during next API call.
-  virtual void UpdateLastTransportUsed(device::FidoTransportProtocol transport);
-
   // Disables the UI (needed in cases when called by other components, like
   // cryptotoken).
   virtual void DisableUI();
diff --git a/device/fido/fido_transport_protocol.cc b/device/fido/fido_transport_protocol.cc
index 0c95160..62cb610 100644
--- a/device/fido/fido_transport_protocol.cc
+++ b/device/fido/fido_transport_protocol.cc
@@ -4,8 +4,6 @@
 
 #include "device/fido/fido_transport_protocol.h"
 
-#include "base/notreached.h"
-
 namespace device {
 
 const char kUsbHumanInterfaceDevice[] = "usb";
@@ -31,7 +29,7 @@
 }
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::string ToString(FidoTransportProtocol protocol) {
+base::StringPiece ToString(FidoTransportProtocol protocol) {
   switch (protocol) {
     case FidoTransportProtocol::kUsbHumanInterfaceDevice:
       return kUsbHumanInterfaceDevice;
@@ -48,8 +46,6 @@
       // is considered a flavour of caBLE.
       return kCloudAssistedBluetoothLowEnergy;
   }
-  NOTREACHED();
-  return "";
 }
 
 }  // namespace device
diff --git a/device/fido/fido_transport_protocol.h b/device/fido/fido_transport_protocol.h
index afcaf9e..9d0502f7 100644
--- a/device/fido/fido_transport_protocol.h
+++ b/device/fido/fido_transport_protocol.h
@@ -5,10 +5,7 @@
 #ifndef DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
 #define DEVICE_FIDO_FIDO_TRANSPORT_PROTOCOL_H_
 
-#include <string>
-
 #include "base/component_export.h"
-#include "base/containers/flat_set.h"
 #include "base/optional.h"
 #include "base/strings/string_piece.h"
 
@@ -41,7 +38,7 @@
     base::StringPiece protocol);
 
 COMPONENT_EXPORT(DEVICE_FIDO)
-std::string ToString(FidoTransportProtocol protocol);
+base::StringPiece ToString(FidoTransportProtocol protocol);
 
 }  // namespace device