blob: 0a925984a16d9c25a9dfbfffe6502c6043a9d278 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEBAUTHN_CHROME_AUTHENTICATOR_REQUEST_DELEGATE_H_
#define CHROME_BROWSER_WEBAUTHN_CHROME_AUTHENTICATOR_REQUEST_DELEGATE_H_
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "components/trusted_vault/trusted_vault_connection.h"
#include "content/public/browser/authenticator_request_client_delegate.h"
#include "content/public/browser/global_routing_id.h"
#include "device/fido/cable/cable_discovery_data.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "google_apis/gaia/google_service_auth_error.h"
class EnclaveManager;
class PrefService;
class Profile;
namespace content {
class BrowserContext;
class RenderFrameHost;
} // namespace content
namespace device {
class FidoAuthenticator;
class FidoDiscoveryFactory;
class PublicKeyCredentialDescriptor;
class PublicKeyCredentialUserEntity;
enum class FidoRequestType : uint8_t;
namespace enclave {
struct CredentialRequest;
}
} // namespace device
namespace signin {
class PrimaryAccountAccessTokenFetcher;
} // namespace signin
namespace sync_pb {
class WebauthnCredentialSpecifics;
}
namespace user_prefs {
class PrefRegistrySyncable;
}
// ChromeWebAuthenticationDelegate is the //chrome layer implementation of
// content::WebAuthenticationDelegate.
class ChromeWebAuthenticationDelegate
: public content::WebAuthenticationDelegate {
public:
#if BUILDFLAG(IS_MAC)
// Returns a configuration struct for instantiating the macOS WebAuthn
// platform authenticator for the given Profile.
static TouchIdAuthenticatorConfig TouchIdAuthenticatorConfigForProfile(
Profile* profile);
#endif // BUILDFLAG(IS_MAC)
~ChromeWebAuthenticationDelegate() override;
// content::WebAuthenticationDelegate:
bool OverrideCallerOriginAndRelyingPartyIdValidation(
content::BrowserContext* browser_context,
const url::Origin& caller_origin,
const std::string& relying_party_id) override;
bool OriginMayUseRemoteDesktopClientOverride(
content::BrowserContext* browser_context,
const url::Origin& caller_origin) override;
std::optional<std::string> MaybeGetRelyingPartyIdOverride(
const std::string& claimed_relying_party_id,
const url::Origin& caller_origin) override;
bool ShouldPermitIndividualAttestation(
content::BrowserContext* browser_context,
const url::Origin& caller_origin,
const std::string& relying_party_id) override;
bool SupportsResidentKeys(
content::RenderFrameHost* render_frame_host) override;
bool SupportsPasskeyMetadataSyncing() override;
bool IsFocused(content::WebContents* web_contents) override;
std::optional<bool> IsUserVerifyingPlatformAuthenticatorAvailableOverride(
content::RenderFrameHost* render_frame_host) override;
content::WebAuthenticationRequestProxy* MaybeGetRequestProxy(
content::BrowserContext* browser_context,
const url::Origin& caller_origin) override;
bool IsEnclaveAuthenticatorAvailable(
content::BrowserContext* browser_context) override;
#if BUILDFLAG(IS_MAC)
std::optional<TouchIdAuthenticatorConfig> GetTouchIdAuthenticatorConfig(
content::BrowserContext* browser_context) override;
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_CHROMEOS)
ChromeOSGenerateRequestIdCallback GetGenerateRequestIdCallback(
content::RenderFrameHost* render_frame_host) override;
#endif // BUILDFLAG(IS_CHROMEOS)
};
class ChromeAuthenticatorRequestDelegate
: public content::AuthenticatorRequestClientDelegate,
public AuthenticatorRequestDialogModel::Observer {
public:
// TestObserver is an interface that observes certain events related to this
// class for testing purposes. Only a single instance of this interface can
// be installed at a given time.
class TestObserver {
public:
virtual void Created(ChromeAuthenticatorRequestDelegate* delegate) = 0;
virtual std::vector<std::unique_ptr<device::cablev2::Pairing>>
GetCablePairingsFromSyncedDevices() = 0;
virtual void OnTransportAvailabilityEnumerated(
ChromeAuthenticatorRequestDelegate* delegate,
device::FidoRequestHandlerBase::TransportAvailabilityInfo* tai) = 0;
virtual void UIShown(ChromeAuthenticatorRequestDelegate* delegate) = 0;
virtual void CableV2ExtensionSeen(
base::span<const uint8_t> server_link_data) = 0;
virtual void ConfiguringCable(device::FidoRequestType request_type) {}
virtual void AccountSelectorShown(
const std::vector<device::AuthenticatorGetAssertionResponse>&
responses) {}
};
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
// The |render_frame_host| must outlive this instance.
explicit ChromeAuthenticatorRequestDelegate(
content::RenderFrameHost* render_frame_host);
ChromeAuthenticatorRequestDelegate(
const ChromeAuthenticatorRequestDelegate&) = delete;
ChromeAuthenticatorRequestDelegate& operator=(
const ChromeAuthenticatorRequestDelegate&) = delete;
~ChromeAuthenticatorRequestDelegate() override;
// SetGlobalObserverForTesting sets the single |TestObserver| that is active
// at a given time. Call be called with |nullptr| to unregister a
// |TestObserver|. It is a fatal error to try and register a |TestObserver|
// while one is still installed.
static void SetGlobalObserverForTesting(TestObserver*);
base::WeakPtr<ChromeAuthenticatorRequestDelegate> AsWeakPtr();
AuthenticatorRequestDialogModel* dialog_model() const {
return dialog_model_.get();
}
// content::AuthenticatorRequestClientDelegate:
void SetRelyingPartyId(const std::string& rp_id) override;
bool DoesBlockRequestOnFailure(InterestingFailureReason reason) override;
void RegisterActionCallbacks(
base::OnceClosure cancel_callback,
base::RepeatingClosure start_over_callback,
AccountPreselectedCallback account_preselected_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback) override;
void OnTransactionSuccessful(RequestSource request_source,
device::FidoRequestType,
device::AuthenticatorType) override;
void ShouldReturnAttestation(
const std::string& relying_party_id,
const device::FidoAuthenticator* authenticator,
bool is_enterprise_attestation,
base::OnceCallback<void(bool)> callback) override;
void ConfigureDiscoveries(
const url::Origin& origin,
const std::string& rp_id,
RequestSource request_source,
device::FidoRequestType request_type,
std::optional<device::ResidentKeyRequirement> resident_key_requirement,
device::UserVerificationRequirement user_verification_requirement,
base::span<const device::CableDiscoveryData> pairings_from_extension,
bool is_enclave_authenticator_available,
device::FidoDiscoveryFactory* discovery_factory) override;
void SetHints(
const AuthenticatorRequestClientDelegate::Hints& hints) override;
void SelectAccount(
std::vector<device::AuthenticatorGetAssertionResponse> responses,
base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)>
callback) override;
void DisableUI() override;
bool IsWebAuthnUIEnabled() override;
void SetConditionalRequest(bool is_conditional) override;
void SetCredentialIdFilter(std::vector<device::PublicKeyCredentialDescriptor>
credential_list) override;
void SetUserEntityForMakeCredentialRequest(
const device::PublicKeyCredentialUserEntity& user_entity) override;
// device::FidoRequestHandlerBase::Observer:
void OnTransportAvailabilityEnumerated(
device::FidoRequestHandlerBase::TransportAvailabilityInfo data) override;
bool EmbedderControlsAuthenticatorDispatch(
const device::FidoAuthenticator& authenticator) override;
void FidoAuthenticatorAdded(
const device::FidoAuthenticator& authenticator) override;
void FidoAuthenticatorRemoved(base::StringPiece authenticator_id) override;
void BluetoothAdapterPowerChanged(bool is_powered_on) override;
bool SupportsPIN() const override;
void CollectPIN(
CollectPINOptions options,
base::OnceCallback<void(std::u16string)> provide_pin_cb) override;
void StartBioEnrollment(base::OnceClosure next_callback) override;
void OnSampleCollected(int bio_samples_remaining) override;
void FinishCollectToken() override;
void OnRetryUserVerification(int attempts) override;
// AuthenticatorRequestDialogModel::Observer:
void OnStartOver() override;
void OnModelDestroyed(AuthenticatorRequestDialogModel* model) override;
void OnStepTransition() override;
void OnCancelRequest() override;
void OnManageDevicesClicked() override;
// A non-const version of dialog_model().
AuthenticatorRequestDialogModel* GetDialogModelForTesting();
// SetPassEmptyUsbDeviceManagerForTesting controls whether the
// `DiscoveryFactory` will be given an empty USB device manager. This is
// needed in tests because creating a real `device::mojom::UsbDeviceManager`
// can create objects on thread-pool threads. Those objects aren't scheduled
// for deletion until after the thread-pool is shutdown when testing, causing
// "leaks" to be reported.
void SetPassEmptyUsbDeviceManagerForTesting(bool value);
private:
FRIEND_TEST_ALL_PREFIXES(ChromeAuthenticatorRequestDelegatePrivateTest,
DaysSinceDate);
FRIEND_TEST_ALL_PREFIXES(ChromeAuthenticatorRequestDelegatePrivateTest,
GetICloudKeychainPref);
FRIEND_TEST_ALL_PREFIXES(ChromeAuthenticatorRequestDelegatePrivateTest,
ShouldCreateInICloudKeychain);
class EnclaveManagerObserver;
// GetRenderFrameHost returns a pointer to the RenderFrameHost that was given
// to the constructor.
content::RenderFrameHost* GetRenderFrameHost() const;
content::BrowserContext* GetBrowserContext() const;
void ShowUI(device::FidoRequestHandlerBase::TransportAvailabilityInfo data);
std::optional<device::FidoTransportProtocol> GetLastTransportUsed() const;
// Called each time `enclave_manager_` has finished processing all pending
// actions.
void OnEnclaveManagerIdle();
// Called when the user selects an account from modal or conditional UI.
// Stores the credential ID in `preselected_cred_id_` then forwards to the
// `AccountPreselectedCallback` that was passed to
// `RegisterActionCallbacks`.
void OnAccountPreselected(device::PublicKeyCredentialDescriptor);
// Called to start fetching the state of the primary account from the
// trusted vault service.
void DownloadAccountState();
// Called when the state of the trusted vault has been determined by
// `DownloadAccountState`.
void OnAccountStateDownloaded(
std::unique_ptr<trusted_vault::TrustedVaultConnection> unused,
trusted_vault::DownloadAuthenticationFactorsRegistrationStateResult
result);
// Called when the UI has reached a state where it needs to do an enclave
// operation and an OAuth token for the enclave has been fetched.
void StartEnclaveTransaction(std::optional<std::string> token);
// ShouldPermitCableExtension returns true if the given |origin| may set a
// caBLE extension. This extension contains website-chosen BLE pairing
// information that will be broadcast by the device.
bool ShouldPermitCableExtension(const url::Origin& origin);
void OnInvalidatedCablePairing(
std::unique_ptr<device::cablev2::Pairing> failed_pairing);
void OnCableEvent(device::cablev2::Event event);
// Adds GPM passkeys matching |rp_id| to |passkeys|.
void GetPhoneContactableGpmPasskeysForRpId(
std::vector<device::DiscoverableCredentialMetadata>* passkeys);
// Configures an WebAuthn enclave authenticator discovery and provides it with
// synced passkeys.
void ConfigureEnclaveDiscovery(
const std::string& rp_id,
device::FidoDiscoveryFactory* discovery_factory);
// Invoked when a new GPM passkey is created, to save it to sync data.
void OnPasskeyCreated(sync_pb::WebauthnCredentialSpecifics passkey);
#if BUILDFLAG(IS_MAC)
// DaysSinceDate returns the number of days between `formatted_date` (in ISO
// 8601 format) and `now`. It returns `nullopt` if `formatted_date` cannot be
// parsed or if it's in `now`s future.
//
// It does not parse `formatted_date` strictly and is intended for trusted
// inputs.
static std::optional<int> DaysSinceDate(const std::string& formatted_date,
base::Time now);
// GetICloudKeychainPref returns the value of the iCloud Keychain preference
// as a tristate. If no value for the preference has been set then it
// returns `std::nullopt`.
static std::optional<bool> GetICloudKeychainPref(const PrefService* prefs);
// IsActiveProfileAuthenticatorUser returns true if the profile authenticator
// has been used in the past 31 days.
static bool IsActiveProfileAuthenticatorUser(const PrefService* prefs);
// ShouldCreateInICloudKeychain returns true if attachment=platform creation
// requests should default to iCloud Keychain.
static bool ShouldCreateInICloudKeychain(
RequestSource request_source,
bool is_active_profile_authenticator_user,
bool has_icloud_drive_enabled,
bool request_is_for_google_com,
std::optional<bool> preference);
// ConfigureICloudKeychain is called by `ConfigureDiscoveries` to configure
// the `AuthenticatorRequestDialogModel` with iCloud Keychain-related values.
void ConfigureICloudKeychain(RequestSource request_source,
const std::string& rp_id);
#endif
const content::GlobalRenderFrameHostId render_frame_host_id_;
const std::unique_ptr<AuthenticatorRequestDialogModel> dialog_model_;
base::OnceClosure cancel_callback_;
base::RepeatingClosure start_over_callback_;
AccountPreselectedCallback account_preselected_callback_;
device::FidoRequestHandlerBase::RequestCallback request_callback_;
// If in the TransportAvailabilityInfo reported by the request handler,
// disable_embedder_ui is set, this will be set to true. No UI must be
// rendered and all request handler callbacks will be ignored.
bool disable_ui_ = false;
// If true, show a more subtle UI unless the user has platform discoverable
// credentials on the device.
bool is_conditional_ = false;
// A list of credentials used to filter passkeys by ID. When non-empty,
// non-matching passkeys will not be displayed during conditional mediation
// requests. When empty, no filter is applied and all passkeys are displayed.
std::vector<device::PublicKeyCredentialDescriptor> credential_filter_;
// See `SetPassEmptyUsbDeviceManagerForTesting`.
bool pass_empty_usb_device_manager_ = false;
// cable_device_ready_ is true if a caBLE handshake has completed. At this
// point we assume that any errors were communicated on the caBLE device and
// don't show errors on the desktop too.
bool cable_device_ready_ = false;
// can_use_synced_phone_passkeys_ is true if there is a phone pairing
// available that can service requests for synced GPM passkeys.
bool can_use_synced_phone_passkeys_ = false;
// Non-null when the cloud enclave authenticator is available for use. The
// `EnclaveManager` is a `KeyedService` for the current profile and so
// outlives this object.
raw_ptr<EnclaveManager> enclave_manager_ = nullptr;
// Used to observe `enclave_manager_` without implementing the Observer
// interface directly and thus needing to #include all of `enclave_manager.h`
// in this header file.
std::unique_ptr<EnclaveManagerObserver> enclave_manager_observer_;
// Stores the TransportAvailabilityInfo while we're waiting for the enclave
// state to load from the disk.
std::unique_ptr<device::FidoRequestHandlerBase::TransportAvailabilityInfo>
pending_transport_availability_info_;
std::optional<device::FidoRequestType> request_type_;
std::optional<device::UserVerificationRequirement>
user_verification_requirement_;
// The set of pertinent synced passkeys for this request. Persisted here
// so that a consistent set of passkeys is used throughout the transaction.
std::optional<std::vector<sync_pb::WebauthnCredentialSpecifics>>
gpm_credentials_;
// The pending request to fetch the state of the trusted vault.
std::unique_ptr<trusted_vault::TrustedVaultConnection::Request>
download_account_state_request_;
// The pending request to fetch an OAuth token for the enclave request.
std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
access_token_fetcher_;
// The callback used to trigger a request to the enclave.
base::RepeatingCallback<void(
std::unique_ptr<device::enclave::CredentialRequest>)>
enclave_request_callback_;
// The credential ID of the last credential to be selected by the user in
// modal or conditional UI.
std::optional<std::vector<uint8_t>> preselected_cred_id_;
base::WeakPtrFactory<ChromeAuthenticatorRequestDelegate> weak_ptr_factory_{
this};
};
#endif // CHROME_BROWSER_WEBAUTHN_CHROME_AUTHENTICATOR_REQUEST_DELEGATE_H_