Commit a275018e authored by Balazs Engedy's avatar Balazs Engedy Committed by Commit Bot

Decouple creating AuthenticatorRequestClientDelegate from showing UI.

This allows AuthenticatorImpl to first create the delegate and ask it if the
WebContents has focus, and only then triggering the request dialog (which may
steal the focus), so as to avoid race conditions around focus checking at
request start.

The CL also fixes fake FidoDiscoveries to use weak pointers so as to support
being immediately destroyed after the discovery is started.

Bug: 849323, 851593
Change-Id: Iae4ac25dc39b527e5481836e4522a6f4da7e31ba
Reviewed-on: https://chromium-review.googlesource.com/1095619
Commit-Queue: Balazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#566750}
parent a1f6d823
...@@ -54,20 +54,7 @@ bool IsWebauthnRPIDListedInEnterprisePolicy( ...@@ -54,20 +54,7 @@ bool IsWebauthnRPIDListedInEnterprisePolicy(
ChromeAuthenticatorRequestDelegate::ChromeAuthenticatorRequestDelegate( ChromeAuthenticatorRequestDelegate::ChromeAuthenticatorRequestDelegate(
content::RenderFrameHost* render_frame_host) content::RenderFrameHost* render_frame_host)
: render_frame_host_(render_frame_host), weak_ptr_factory_(this) { : render_frame_host_(render_frame_host), weak_ptr_factory_(this) {}
#if !defined(OS_ANDROID)
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWebAuthenticationUI)) {
return;
}
auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>();
weak_dialog_model_ = dialog_model.get();
weak_dialog_model_->AddObserver(this);
ShowAuthenticatorRequestDialog(
content::WebContents::FromRenderFrameHost(render_frame_host),
std::move(dialog_model));
#endif
}
ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() { ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() {
// Currently, completion of the request is indicated by //content destroying // Currently, completion of the request is indicated by //content destroying
...@@ -88,6 +75,21 @@ ChromeAuthenticatorRequestDelegate::AsWeakPtr() { ...@@ -88,6 +75,21 @@ ChromeAuthenticatorRequestDelegate::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr(); return weak_ptr_factory_.GetWeakPtr();
} }
void ChromeAuthenticatorRequestDelegate::DidStartRequest() {
#if !defined(OS_ANDROID)
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWebAuthenticationUI)) {
return;
}
auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>();
weak_dialog_model_ = dialog_model.get();
weak_dialog_model_->AddObserver(this);
ShowAuthenticatorRequestDialog(
content::WebContents::FromRenderFrameHost(render_frame_host()),
std::move(dialog_model));
#endif
}
bool ChromeAuthenticatorRequestDelegate::ShouldPermitIndividualAttestation( bool ChromeAuthenticatorRequestDelegate::ShouldPermitIndividualAttestation(
const std::string& relying_party_id) { const std::string& relying_party_id) {
// If the RP ID is listed in the policy, signal that individual attestation is // If the RP ID is listed in the policy, signal that individual attestation is
......
...@@ -32,6 +32,7 @@ class ChromeAuthenticatorRequestDelegate ...@@ -32,6 +32,7 @@ class ChromeAuthenticatorRequestDelegate
} }
// content::AuthenticatorRequestClientDelegate: // content::AuthenticatorRequestClientDelegate:
void DidStartRequest() override;
bool ShouldPermitIndividualAttestation( bool ShouldPermitIndividualAttestation(
const std::string& relying_party_id) override; const std::string& relying_party_id) override;
void ShouldReturnAttestation( void ShouldReturnAttestation(
......
...@@ -460,6 +460,7 @@ void AuthenticatorImpl::MakeCredential( ...@@ -460,6 +460,7 @@ void AuthenticatorImpl::MakeCredential(
DCHECK(make_credential_response_callback_.is_null()); DCHECK(make_credential_response_callback_.is_null());
make_credential_response_callback_ = std::move(callback); make_credential_response_callback_ = std::move(callback);
request_delegate_->DidStartRequest();
timer_->Start( timer_->Start(
FROM_HERE, options->adjusted_timeout, FROM_HERE, options->adjusted_timeout,
...@@ -562,6 +563,7 @@ void AuthenticatorImpl::GetAssertion( ...@@ -562,6 +563,7 @@ void AuthenticatorImpl::GetAssertion(
DCHECK(get_assertion_response_callback_.is_null()); DCHECK(get_assertion_response_callback_.is_null());
get_assertion_response_callback_ = std::move(callback); get_assertion_response_callback_ = std::move(callback);
request_delegate_->DidStartRequest();
timer_->Start( timer_->Start(
FROM_HERE, options->adjusted_timeout, FROM_HERE, options->adjusted_timeout,
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/bind_helpers.h"
#include "base/json/json_parser.h" #include "base/json/json_parser.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/run_loop.h" #include "base/run_loop.h"
...@@ -198,6 +199,7 @@ using TestMakeCredentialCallback = device::test::StatusAndValueCallbackReceiver< ...@@ -198,6 +199,7 @@ using TestMakeCredentialCallback = device::test::StatusAndValueCallbackReceiver<
using TestGetAssertionCallback = device::test::StatusAndValueCallbackReceiver< using TestGetAssertionCallback = device::test::StatusAndValueCallbackReceiver<
AuthenticatorStatus, AuthenticatorStatus,
GetAssertionAuthenticatorResponsePtr>; GetAssertionAuthenticatorResponsePtr>;
using TestRequestStartedCallback = device::test::TestCallbackReceiver<>;
std::vector<uint8_t> GetTestChallengeBytes() { std::vector<uint8_t> GetTestChallengeBytes() {
return std::vector<uint8_t>(std::begin(kTestChallengeBytes), return std::vector<uint8_t>(std::begin(kTestChallengeBytes),
...@@ -1045,14 +1047,21 @@ class TestAuthenticatorRequestDelegate ...@@ -1045,14 +1047,21 @@ class TestAuthenticatorRequestDelegate
: public AuthenticatorRequestClientDelegate { : public AuthenticatorRequestClientDelegate {
public: public:
TestAuthenticatorRequestDelegate(RenderFrameHost* render_frame_host, TestAuthenticatorRequestDelegate(RenderFrameHost* render_frame_host,
base::OnceClosure did_start_request_callback,
IndividualAttestation individual_attestation, IndividualAttestation individual_attestation,
AttestationConsent attestation_consent, AttestationConsent attestation_consent,
bool is_focused) bool is_focused)
: individual_attestation_(individual_attestation), : did_start_request_callback_(std::move(did_start_request_callback)),
individual_attestation_(individual_attestation),
attestation_consent_(attestation_consent), attestation_consent_(attestation_consent),
is_focused_(is_focused) {} is_focused_(is_focused) {}
~TestAuthenticatorRequestDelegate() override {} ~TestAuthenticatorRequestDelegate() override {}
void DidStartRequest() override {
ASSERT_TRUE(did_start_request_callback_) << "DidStartRequest called twice.";
std::move(did_start_request_callback_).Run();
}
bool ShouldPermitIndividualAttestation( bool ShouldPermitIndividualAttestation(
const std::string& relying_party_id) override { const std::string& relying_party_id) override {
return individual_attestation_ == IndividualAttestation::REQUESTED; return individual_attestation_ == IndividualAttestation::REQUESTED;
...@@ -1067,6 +1076,7 @@ class TestAuthenticatorRequestDelegate ...@@ -1067,6 +1076,7 @@ class TestAuthenticatorRequestDelegate
bool IsFocused() override { return is_focused_; } bool IsFocused() override { return is_focused_; }
base::OnceClosure did_start_request_callback_;
const IndividualAttestation individual_attestation_; const IndividualAttestation individual_attestation_;
const AttestationConsent attestation_consent_; const AttestationConsent attestation_consent_;
const bool is_focused_; const bool is_focused_;
...@@ -1083,10 +1093,16 @@ class TestAuthenticatorContentBrowserClient : public ContentBrowserClient { ...@@ -1083,10 +1093,16 @@ class TestAuthenticatorContentBrowserClient : public ContentBrowserClient {
if (return_null_delegate) if (return_null_delegate)
return nullptr; return nullptr;
return std::make_unique<TestAuthenticatorRequestDelegate>( return std::make_unique<TestAuthenticatorRequestDelegate>(
render_frame_host, individual_attestation, attestation_consent, render_frame_host,
is_focused); request_started_callback ? std::move(request_started_callback)
: base::DoNothing(),
individual_attestation, attestation_consent, is_focused);
} }
// If set, this closure will be called when the subsequently constructed
// delegate is informed that the request has started.
base::OnceClosure request_started_callback;
IndividualAttestation individual_attestation = IndividualAttestation individual_attestation =
IndividualAttestation::NOT_REQUESTED; IndividualAttestation::NOT_REQUESTED;
AttestationConsent attestation_consent = AttestationConsent::DENIED; AttestationConsent attestation_consent = AttestationConsent::DENIED;
...@@ -1369,6 +1385,36 @@ TEST_F(AuthenticatorContentBrowserClientTest, ...@@ -1369,6 +1385,36 @@ TEST_F(AuthenticatorContentBrowserClientTest,
RunTestCases(kTests); RunTestCases(kTests);
} }
TEST_F(AuthenticatorContentBrowserClientTest,
MakeCredentialRequestStartedCallback) {
TestServiceManagerContext smc;
NavigateAndCommit(GURL(kTestOrigin1));
AuthenticatorPtr authenticator = ConnectToAuthenticator();
PublicKeyCredentialCreationOptionsPtr options =
GetTestPublicKeyCredentialCreationOptions();
TestRequestStartedCallback request_started;
test_client_.request_started_callback = request_started.callback();
authenticator->MakeCredential(std::move(options), base::DoNothing());
request_started.WaitForCallback();
}
TEST_F(AuthenticatorContentBrowserClientTest,
GetAssertionRequestStartedCallback) {
TestServiceManagerContext smc;
NavigateAndCommit(GURL(kTestOrigin1));
AuthenticatorPtr authenticator = ConnectToAuthenticator();
PublicKeyCredentialRequestOptionsPtr options =
GetTestPublicKeyCredentialRequestOptions();
TestRequestStartedCallback request_started;
test_client_.request_started_callback = request_started.callback();
authenticator->GetAssertion(std::move(options), base::DoNothing());
request_started.WaitForCallback();
}
TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) {
// When the |ContentBrowserClient| considers the tab to be unfocused, // When the |ContentBrowserClient| considers the tab to be unfocused,
// registration requests should fail with a |NOT_FOCUSED| error, but getting // registration requests should fail with a |NOT_FOCUSED| error, but getting
...@@ -1384,9 +1430,14 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { ...@@ -1384,9 +1430,14 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) {
options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); options->public_key_parameters = GetTestPublicKeyCredentialParameters(123);
TestMakeCredentialCallback cb; TestMakeCredentialCallback cb;
TestRequestStartedCallback request_started;
test_client_.request_started_callback = request_started.callback();
authenticator->MakeCredential(std::move(options), cb.callback()); authenticator->MakeCredential(std::move(options), cb.callback());
cb.WaitForCallback(); cb.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::NOT_FOCUSED, cb.status()); EXPECT_EQ(AuthenticatorStatus::NOT_FOCUSED, cb.status());
EXPECT_FALSE(request_started.was_called());
} }
{ {
...@@ -1403,10 +1454,14 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { ...@@ -1403,10 +1454,14 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) {
options->allow_credentials.emplace_back(std::move(credential)); options->allow_credentials.emplace_back(std::move(credential));
TestGetAssertionCallback cb; TestGetAssertionCallback cb;
TestRequestStartedCallback request_started;
test_client_.request_started_callback = request_started.callback();
authenticator->GetAssertion(std::move(options), cb.callback()); authenticator->GetAssertion(std::move(options), cb.callback());
cb.WaitForCallback(); cb.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::SUCCESS, cb.status()); EXPECT_EQ(AuthenticatorStatus::SUCCESS, cb.status());
EXPECT_TRUE(request_started.was_called());
} }
} }
......
...@@ -13,6 +13,8 @@ AuthenticatorRequestClientDelegate::AuthenticatorRequestClientDelegate() = ...@@ -13,6 +13,8 @@ AuthenticatorRequestClientDelegate::AuthenticatorRequestClientDelegate() =
AuthenticatorRequestClientDelegate::~AuthenticatorRequestClientDelegate() = AuthenticatorRequestClientDelegate::~AuthenticatorRequestClientDelegate() =
default; default;
void AuthenticatorRequestClientDelegate::DidStartRequest() {}
bool AuthenticatorRequestClientDelegate::ShouldPermitIndividualAttestation( bool AuthenticatorRequestClientDelegate::ShouldPermitIndividualAttestation(
const std::string& relying_party_id) { const std::string& relying_party_id) {
return false; return false;
......
...@@ -23,6 +23,9 @@ class CONTENT_EXPORT AuthenticatorRequestClientDelegate { ...@@ -23,6 +23,9 @@ class CONTENT_EXPORT AuthenticatorRequestClientDelegate {
AuthenticatorRequestClientDelegate(); AuthenticatorRequestClientDelegate();
virtual ~AuthenticatorRequestClientDelegate(); virtual ~AuthenticatorRequestClientDelegate();
// Notifies the delegate that the request is actually starting.
virtual void DidStartRequest();
// Returns true if the given relying party ID is permitted to receive // Returns true if the given relying party ID is permitted to receive
// individual attestation certificates. This: // individual attestation certificates. This:
// a) triggers a signal to the security key that returning individual // a) triggers a signal to the security key that returning individual
......
...@@ -43,7 +43,7 @@ void FakeFidoDiscovery::StartInternal() { ...@@ -43,7 +43,7 @@ void FakeFidoDiscovery::StartInternal() {
if (mode_ == StartMode::kAutomatic) { if (mode_ == StartMode::kAutomatic) {
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeFidoDiscovery::SimulateStarted, FROM_HERE, base::BindOnce(&FakeFidoDiscovery::SimulateStarted,
base::Unretained(this), true /* success */)); AsWeakPtr(), true /* success */));
} }
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/location.h" #include "base/location.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "device/fido/virtual_u2f_device.h" #include "device/fido/virtual_u2f_device.h"
...@@ -17,7 +18,9 @@ namespace device { ...@@ -17,7 +18,9 @@ namespace device {
namespace test { namespace test {
// A FidoDiscovery that always vends a single |VirtualFidoDevice|. // A FidoDiscovery that always vends a single |VirtualFidoDevice|.
class VirtualFidoDeviceDiscovery : public FidoDiscovery { class VirtualFidoDeviceDiscovery
: public FidoDiscovery,
public base::SupportsWeakPtr<VirtualFidoDeviceDiscovery> {
public: public:
explicit VirtualFidoDeviceDiscovery( explicit VirtualFidoDeviceDiscovery(
scoped_refptr<VirtualFidoDevice::State> state) scoped_refptr<VirtualFidoDevice::State> state)
...@@ -32,7 +35,7 @@ class VirtualFidoDeviceDiscovery : public FidoDiscovery { ...@@ -32,7 +35,7 @@ class VirtualFidoDeviceDiscovery : public FidoDiscovery {
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&VirtualFidoDeviceDiscovery::NotifyDiscoveryStarted, base::BindOnce(&VirtualFidoDeviceDiscovery::NotifyDiscoveryStarted,
base::Unretained(this), true /* success */)); AsWeakPtr(), true /* success */));
} }
private: private:
......
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