Commit c7c55df8 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

webauthn: update transport selection for caBLE v2.

When caBLE v2 is enabled, use an updated transport selection dialog that
indicates active discoveries with spinner entries. This communicates
that either a USB or caBLE authenticator may be used, while still
allowing the user to select other options.

BUG=1002262

Change-Id: I8bd38500911025d5441975980e3ab9a63517410a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2025577Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Commit-Queue: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736567}
parent 751ac60d
...@@ -10348,6 +10348,12 @@ Please help our engineers fix this problem. Tell us what happened right before y ...@@ -10348,6 +10348,12 @@ Please help our engineers fix this problem. Tell us what happened right before y
<message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION" desc="Contents of the dialog shown when the user tries to sign-in using a phone as a security key."> <message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION" desc="Contents of the dialog shown when the user tries to sign-in using a phone as a security key.">
A notification was sent to your phone to confirm it's you. A notification was sent to your phone to confirm it's you.
</message> </message>
<message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION_SHORT" desc="Second line of text in an item letting the user know that they can use their phone as a security key. The user needs to check their phone and respond to a notification that will ask them to press a button on the phone's screen to confirm that they're logging in.">
Unlock your phone and confirm it's you
</message>
<message name="IDS_WEBAUTHN_CABLE_V2_ACTIVATE_DESCRIPTION_SHORT" desc="Second line of text in an item letting the user know that they can use their phone as a security key. The user needs to unlock their phone and open some app on their phone to complete the login process.">
Open the security key app on your phone
</message>
<message name="IDS_WEBAUTHN_CABLE_QR_TITLE" desc="Title of a dialog that contains a QR-code for the user to scan with a mobile phone app. A QR code is a type of barcode that can be read using a smartphone's camera."> <message name="IDS_WEBAUTHN_CABLE_QR_TITLE" desc="Title of a dialog that contains a QR-code for the user to scan with a mobile phone app. A QR code is a type of barcode that can be read using a smartphone's camera.">
Pair new phone Pair new phone
</message> </message>
......
776332296af9d7bfe4d01eb7072be80c03f7454e
\ No newline at end of file
e69eec784ececc9af2b8c9e3241c8f51515302b6
\ No newline at end of file
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/ui/views/webauthn/authenticator_transport_selector_sheet_view.h" #include "chrome/browser/ui/views/webauthn/authenticator_transport_selector_sheet_view.h"
#include "chrome/browser/webauthn/authenticator_transport.h"
#include <utility> #include <utility>
#include "chrome/browser/webauthn/authenticator_transport.h"
#include "device/fido/features.h"
AuthenticatorTransportSelectorSheetView:: AuthenticatorTransportSelectorSheetView::
AuthenticatorTransportSelectorSheetView( AuthenticatorTransportSelectorSheetView(
std::unique_ptr<AuthenticatorTransportSelectorSheetModel> model) std::unique_ptr<AuthenticatorTransportSelectorSheetModel> model)
...@@ -17,6 +19,13 @@ AuthenticatorTransportSelectorSheetView:: ...@@ -17,6 +19,13 @@ AuthenticatorTransportSelectorSheetView::
std::unique_ptr<views::View> std::unique_ptr<views::View>
AuthenticatorTransportSelectorSheetView::BuildStepSpecificContent() { AuthenticatorTransportSelectorSheetView::BuildStepSpecificContent() {
if (base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
return std::make_unique<HoverListView>(
std::make_unique<TransportHoverListModel2>(
model()->dialog_model()->available_transports(),
model()->dialog_model()->cable_extension_provided(), model()));
}
return std::make_unique<HoverListView>( return std::make_unique<HoverListView>(
std::make_unique<TransportHoverListModel>( std::make_unique<TransportHoverListModel>(
model()->dialog_model()->available_transports(), model())); model()->dialog_model()->available_transports(), model()));
......
...@@ -65,7 +65,8 @@ class AuthenticatorSheetModelBase ...@@ -65,7 +65,8 @@ class AuthenticatorSheetModelBase
// should be accessed. // should be accessed.
class AuthenticatorTransportSelectorSheetModel class AuthenticatorTransportSelectorSheetModel
: public AuthenticatorSheetModelBase, : public AuthenticatorSheetModelBase,
public TransportHoverListModel::Delegate { public TransportHoverListModel::Delegate,
public TransportHoverListModel2::Delegate {
public: public:
using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase; using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase;
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "chrome/app/vector_icons/vector_icons.h" #include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/webauthn/transport_utils.h" #include "chrome/browser/ui/webauthn/transport_utils.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "device/fido/features.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/paint_vector_icon.h"
...@@ -51,14 +50,104 @@ std::vector<int> TransportHoverListModel::GetButtonTags() const { ...@@ -51,14 +50,104 @@ std::vector<int> TransportHoverListModel::GetButtonTags() const {
std::transform( std::transform(
transport_list_.begin(), transport_list_.end(), tag_list.begin(), transport_list_.begin(), transport_list_.end(), tag_list.begin(),
[](const auto& transport) { return base::strict_cast<int>(transport); }); [](const auto& transport) { return base::strict_cast<int>(transport); });
if (base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
tag_list.push_back(kPairPhoneTag);
}
return tag_list; return tag_list;
} }
base::string16 TransportHoverListModel::GetItemText(int item_tag) const { base::string16 TransportHoverListModel::GetItemText(int item_tag) const {
return GetTransportHumanReadableName(
static_cast<AuthenticatorTransport>(item_tag),
TransportSelectionContext::kTransportSelectionSheet);
}
base::string16 TransportHoverListModel::GetDescriptionText(int item_tag) const {
return base::string16();
}
const gfx::VectorIcon* TransportHoverListModel::GetItemIcon(
int item_tag) const {
return GetTransportVectorIcon(static_cast<AuthenticatorTransport>(item_tag));
}
void TransportHoverListModel::OnListItemSelected(int item_tag) {
if (!delegate_) {
return;
}
delegate_->OnTransportSelected(static_cast<AuthenticatorTransport>(item_tag));
}
size_t TransportHoverListModel::GetPreferredItemCount() const {
return transport_list_.size();
}
bool TransportHoverListModel::StyleForTwoLines() const {
return false;
}
TransportHoverListModel2::TransportHoverListModel2(
std::vector<AuthenticatorTransport> transport_list,
bool cable_extension_provided,
Delegate* delegate)
: transport_list_(std::move(transport_list)),
cable_extension_provided_(cable_extension_provided),
delegate_(delegate) {}
TransportHoverListModel2::~TransportHoverListModel2() = default;
bool TransportHoverListModel2::ShouldShowPlaceholderForEmptyList() const {
return false;
}
base::string16 TransportHoverListModel2::GetPlaceholderText() const {
return base::string16();
}
const gfx::VectorIcon* TransportHoverListModel2::GetPlaceholderIcon() const {
return &gfx::kNoneIcon;
}
std::vector<int> TransportHoverListModel2::GetThrobberTags() const {
std::vector<int> tags;
if (base::Contains(
transport_list_,
AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy)) {
tags.push_back(base::strict_cast<int>(
AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy));
}
if (base::Contains(transport_list_,
AuthenticatorTransport::kUsbHumanInterfaceDevice)) {
tags.push_back(base::strict_cast<int>(
AuthenticatorTransport::kUsbHumanInterfaceDevice));
}
return tags;
}
std::vector<int> TransportHoverListModel2::GetButtonTags() const {
std::vector<int> tags({kPairPhoneTag});
for (const auto transport : transport_list_) {
switch (transport) {
// These are throbbers, not buttons; ignore.
case AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy:
case AuthenticatorTransport::kUsbHumanInterfaceDevice:
break;
case AuthenticatorTransport::kInternal:
tags.push_back(base::strict_cast<int>(transport));
break;
default:
DCHECK(false) << static_cast<int>(transport);
}
}
return tags;
}
base::string16 TransportHoverListModel2::GetItemText(int item_tag) const {
if (item_tag == kPairPhoneTag) { if (item_tag == kPairPhoneTag) {
return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_POPUP_PAIR_PHONE); return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_POPUP_PAIR_PHONE);
} }
...@@ -68,11 +157,28 @@ base::string16 TransportHoverListModel::GetItemText(int item_tag) const { ...@@ -68,11 +157,28 @@ base::string16 TransportHoverListModel::GetItemText(int item_tag) const {
TransportSelectionContext::kTransportSelectionSheet); TransportSelectionContext::kTransportSelectionSheet);
} }
base::string16 TransportHoverListModel::GetDescriptionText(int item_tag) const { base::string16 TransportHoverListModel2::GetDescriptionText(
return base::string16(); int item_tag) const {
switch (item_tag) {
case base::strict_cast<int>(
AuthenticatorTransport::kCloudAssistedBluetoothLowEnergy):
if (cable_extension_provided_) {
return l10n_util::GetStringUTF16(
IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION_SHORT);
}
return l10n_util::GetStringUTF16(
IDS_WEBAUTHN_CABLE_V2_ACTIVATE_DESCRIPTION_SHORT);
case base::strict_cast<int>(
AuthenticatorTransport::kUsbHumanInterfaceDevice):
return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USB_ACTIVATE_DESCRIPTION);
default:
return base::string16();
}
} }
const gfx::VectorIcon* TransportHoverListModel::GetItemIcon( const gfx::VectorIcon* TransportHoverListModel2::GetItemIcon(
int item_tag) const { int item_tag) const {
if (item_tag == kPairPhoneTag) { if (item_tag == kPairPhoneTag) {
return &kSmartphoneIcon; return &kSmartphoneIcon;
...@@ -80,7 +186,7 @@ const gfx::VectorIcon* TransportHoverListModel::GetItemIcon( ...@@ -80,7 +186,7 @@ const gfx::VectorIcon* TransportHoverListModel::GetItemIcon(
return GetTransportVectorIcon(static_cast<AuthenticatorTransport>(item_tag)); return GetTransportVectorIcon(static_cast<AuthenticatorTransport>(item_tag));
} }
void TransportHoverListModel::OnListItemSelected(int item_tag) { void TransportHoverListModel2::OnListItemSelected(int item_tag) {
if (!delegate_) { if (!delegate_) {
return; return;
} }
...@@ -93,11 +199,10 @@ void TransportHoverListModel::OnListItemSelected(int item_tag) { ...@@ -93,11 +199,10 @@ void TransportHoverListModel::OnListItemSelected(int item_tag) {
delegate_->OnTransportSelected(static_cast<AuthenticatorTransport>(item_tag)); delegate_->OnTransportSelected(static_cast<AuthenticatorTransport>(item_tag));
} }
size_t TransportHoverListModel::GetPreferredItemCount() const { size_t TransportHoverListModel2::GetPreferredItemCount() const {
return transport_list_.size() + static_cast<int>(base::FeatureList::IsEnabled( return transport_list_.size() + /* pairing a phone */ 1;
device::kWebAuthPhoneSupport));
} }
bool TransportHoverListModel::StyleForTwoLines() const { bool TransportHoverListModel2::StyleForTwoLines() const {
return false; return true;
} }
...@@ -49,4 +49,46 @@ class TransportHoverListModel : public HoverListModel { ...@@ -49,4 +49,46 @@ class TransportHoverListModel : public HoverListModel {
DISALLOW_COPY_AND_ASSIGN(TransportHoverListModel); DISALLOW_COPY_AND_ASSIGN(TransportHoverListModel);
}; };
// TransportHoverListModel2 is an intended replacement for
// |TransportHoverListModel|. Once the replacement has occurred, the "2" can be
// dropped.
class TransportHoverListModel2 : public HoverListModel {
public:
// Interface that the client should implement to learn when the user clicks on
// views that observe the model.
class Delegate {
public:
// Called when the given |transport| is selected by the user.
virtual void OnTransportSelected(AuthenticatorTransport transport) = 0;
// Called when the user selects the item to pair a new phone.
virtual void StartPhonePairing() = 0;
};
explicit TransportHoverListModel2(
std::vector<AuthenticatorTransport> transport_list,
bool cable_extension_provided,
Delegate* delegate);
~TransportHoverListModel2() override;
// HoverListModel:
bool ShouldShowPlaceholderForEmptyList() const override;
base::string16 GetPlaceholderText() const override;
const gfx::VectorIcon* GetPlaceholderIcon() const override;
std::vector<int> GetThrobberTags() const override;
std::vector<int> GetButtonTags() const override;
base::string16 GetItemText(int item_tag) const override;
base::string16 GetDescriptionText(int item_tag) const override;
const gfx::VectorIcon* GetItemIcon(int item_tag) const override;
void OnListItemSelected(int item_tag) override;
size_t GetPreferredItemCount() const override;
bool StyleForTwoLines() const override;
private:
std::vector<AuthenticatorTransport> transport_list_;
const bool cable_extension_provided_;
Delegate* const delegate_; // Weak, may be nullptr.
DISALLOW_COPY_AND_ASSIGN(TransportHoverListModel2);
};
#endif // CHROME_BROWSER_UI_WEBAUTHN_TRANSPORT_HOVER_LIST_MODEL_H_ #endif // CHROME_BROWSER_UI_WEBAUTHN_TRANSPORT_HOVER_LIST_MODEL_H_
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "device/fido/features.h"
#include "device/fido/fido_authenticator.h" #include "device/fido/fido_authenticator.h"
namespace { namespace {
...@@ -197,10 +198,10 @@ void AuthenticatorRequestDialogModel:: ...@@ -197,10 +198,10 @@ void AuthenticatorRequestDialogModel::
auto most_likely_transport = auto most_likely_transport =
SelectMostLikelyTransport(transport_availability_, last_used_transport_, SelectMostLikelyTransport(transport_availability_, last_used_transport_,
cable_extension_provided_, have_paired_phones_); cable_extension_provided_, have_paired_phones_);
if (most_likely_transport) { if (most_likely_transport &&
!base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
StartGuidedFlowForTransport(*most_likely_transport); StartGuidedFlowForTransport(*most_likely_transport);
} else if (!transport_availability_.available_transports.empty()) { } else if (!transport_availability_.available_transports.empty()) {
DCHECK_GE(transport_availability_.available_transports.size(), 2u);
SetCurrentStep(Step::kTransportSelection); SetCurrentStep(Step::kTransportSelection);
} else { } else {
SetCurrentStep(Step::kErrorNoAvailableTransports); SetCurrentStep(Step::kErrorNoAvailableTransports);
......
...@@ -432,6 +432,8 @@ class AuthenticatorRequestDialogModel { ...@@ -432,6 +432,8 @@ class AuthenticatorRequestDialogModel {
bool has_paired_phones, bool has_paired_phones,
base::Optional<device::QRGeneratorKey> qr_generator_key); base::Optional<device::QRGeneratorKey> qr_generator_key);
bool cable_extension_provided() const { return cable_extension_provided_; }
const std::string& relying_party_id() const { return relying_party_id_; } const std::string& relying_party_id() const { return relying_party_id_; }
bool request_may_start_over() const { return request_may_start_over_; } bool request_may_start_over() const { return request_may_start_over_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment