blob: 19955f60fee66faf8c97cf0681753b37c785116a [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2019 The Chromium Authors
Manas Verma2ff0cc572019-03-19 23:42:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Amos Lim12696e5e32022-09-16 07:37:585#include "content/browser/webauth/authenticator_common_impl.h"
Manas Verma2ff0cc572019-03-19 23:42:566
Adem Derineldae9eff2025-01-23 12:16:007#include <algorithm>
Manas Verma2ff0cc572019-03-19 23:42:568#include <array>
Adem Derineldae9eff2025-01-23 12:16:009#include <cstddef>
10#include <cstdint>
11#include <memory>
Arthur Sonzognic686e8f2024-01-11 08:36:3712#include <optional>
Manas Verma2ff0cc572019-03-19 23:42:5613#include <string>
Md Hasibul Hasana963a9342024-04-03 10:15:1414#include <string_view>
Manas Verma2ff0cc572019-03-19 23:42:5615#include <utility>
Victor Hugo Vianna Silva00692722025-03-18 19:51:4816#include <variant>
Manas Verma2ff0cc572019-03-19 23:42:5617#include <vector>
18
Andrii Natiahlyi6b2f4b12024-09-03 14:58:4219#include "base/barrier_callback.h"
Hans Wennborg0917de892020-04-28 20:21:1520#include "base/check.h"
Adem Derineldae9eff2025-01-23 12:16:0021#include "base/check_op.h"
22#include "base/containers/contains.h"
Adam Langleyf59b55602023-07-05 19:51:2023#include "base/containers/flat_set.h"
Adem Derinel620520b42024-11-04 15:45:4424#include "base/containers/span.h"
Elly450ad9fe2025-07-09 17:13:2225#include "base/containers/to_vector.h"
Nina Satragno13c509e2023-10-27 17:19:3526#include "base/feature_list.h"
Avi Drissmanadac21992023-01-11 23:46:3927#include "base/functional/bind.h"
Adem Derineldae9eff2025-01-23 12:16:0028#include "base/functional/callback.h"
Avi Drissmanadac21992023-01-11 23:46:3929#include "base/functional/callback_helpers.h"
Adem Derineldae9eff2025-01-23 12:16:0030#include "base/location.h"
Adam Langleyf59b55602023-07-05 19:51:2031#include "base/memory/raw_ptr.h"
Adem Derineldae9eff2025-01-23 12:16:0032#include "base/memory/scoped_refptr.h"
Nina Satragno6e0f1ab2024-06-13 22:28:1133#include "base/memory/weak_ptr.h"
Adem Derineldae9eff2025-01-23 12:16:0034#include "base/metrics/histogram_functions.h"
Lei Zhang4c778c5e82024-05-02 19:34:0735#include "base/metrics/histogram_macros.h"
Adem Derineldc2d52f2024-09-19 08:06:5636#include "base/metrics/user_metrics.h"
Adem Derineldae9eff2025-01-23 12:16:0037#include "base/metrics/user_metrics_action.h"
Hans Wennborg0917de892020-04-28 20:21:1538#include "base/notreached.h"
Nina Satragnoa11a1ff2025-07-07 21:05:3039#include "base/stl_util.h"
Daniel Cheng17390fd2025-06-07 06:38:2640#include "base/strings/string_view_util.h"
Adem Derinelbe67ee72025-06-16 07:01:3041#include "base/task/sequenced_task_runner.h"
Adem Derineldae9eff2025-01-23 12:16:0042#include "base/time/time.h"
Manas Verma2ff0cc572019-03-19 23:42:5643#include "base/timer/timer.h"
Martin Kreichgauer263f6d82020-03-10 05:46:4544#include "build/build_config.h"
Adam Langley0c166212023-07-19 21:19:2445#include "components/webauthn/json/value_conversions.h"
Fergal Daly23b8ae62021-03-30 05:43:4646#include "content/browser/renderer_host/back_forward_cache_disable.h"
Jonathan Njeunjeb769687652023-06-15 16:46:3047#include "content/browser/renderer_host/render_frame_host_impl.h"
Adam Langley1e03fb02023-03-16 23:02:0348#include "content/browser/webauth/authenticator_environment.h"
Ken Buchanan1ea549c12024-10-10 21:31:0549#include "content/browser/webauth/authenticator_request_outcome_enums.h"
Adem Derineldae9eff2025-01-23 12:16:0050#include "content/browser/webauth/client_data_json.h"
Adem Derinel620520b42024-11-04 15:45:4451#include "content/browser/webauth/common_utils.h"
Nina Satragnocb0406e2024-09-09 19:47:4852#include "content/browser/webauth/virtual_authenticator.h"
Nina Satragno8cf72fc2022-05-12 19:04:5653#include "content/browser/webauth/virtual_authenticator_manager_impl.h"
Nina Satragnof585eca2019-07-29 20:05:3254#include "content/browser/webauth/virtual_fido_discovery_factory.h"
Ken Buchanan3889e2b2020-02-11 04:26:5355#include "content/browser/webauth/webauth_request_security_checker.h"
Adem Derineldae9eff2025-01-23 12:16:0056#include "content/public/browser/authenticator_common.h"
Nina Satragno2e8237f2024-09-16 17:25:5057#include "content/public/browser/authenticator_request_client_delegate.h"
Adem Derineldae9eff2025-01-23 12:16:0058#include "content/public/browser/back_forward_cache.h"
Manas Verma2ff0cc572019-03-19 23:42:5659#include "content/public/browser/browser_context.h"
60#include "content/public/browser/content_browser_client.h"
Manas Verma2ff0cc572019-03-19 23:42:5661#include "content/public/browser/render_frame_host.h"
Adem Derineldae9eff2025-01-23 12:16:0062#include "content/public/browser/web_authentication_delegate.h"
63#include "content/public/browser/web_authentication_request_proxy.h"
Martin Kreichgauerfefb3772021-04-12 23:02:4864#include "content/public/browser/web_contents.h"
Manas Verma2ff0cc572019-03-19 23:42:5665#include "content/public/common/content_client.h"
Ellyac2e8242025-05-02 00:11:4966#include "crypto/hash.h"
Andrii Natiahlyie480a492024-09-18 15:20:3767#include "device/bluetooth/bluetooth_adapter.h"
68#include "device/bluetooth/bluetooth_adapter_factory.h"
Adem Derineldae9eff2025-01-23 12:16:0069#include "device/fido/attestation_object.h"
Manas Verma2ff0cc572019-03-19 23:42:5670#include "device/fido/attestation_statement.h"
Martin Kreichgauerb3689d062022-07-12 09:36:5171#include "device/fido/authenticator_data.h"
72#include "device/fido/authenticator_get_assertion_response.h"
Adem Derineldae9eff2025-01-23 12:16:0073#include "device/fido/authenticator_make_credential_response.h"
74#include "device/fido/authenticator_selection_criteria.h"
75#include "device/fido/cable/cable_discovery_data.h"
Adem Derinele4777b62024-09-04 07:26:5476#include "device/fido/ctap_get_assertion_request.h"
Manas Verma2ff0cc572019-03-19 23:42:5677#include "device/fido/ctap_make_credential_request.h"
Adam Langleyafd522f2023-01-27 21:12:3378#include "device/fido/features.h"
Manas Verma2ff0cc572019-03-19 23:42:5679#include "device/fido/fido_authenticator.h"
80#include "device/fido/fido_constants.h"
Nina Satragno6e0f1ab2024-06-13 22:28:1181#include "device/fido/fido_request_handler_base.h"
Manas Verma2ff0cc572019-03-19 23:42:5682#include "device/fido/fido_transport_protocol.h"
Martin Kreichgauer930f341a2022-01-07 20:20:4683#include "device/fido/fido_types.h"
Adam Langley051b97932021-01-11 19:11:2384#include "device/fido/filter.h"
Manas Verma2ff0cc572019-03-19 23:42:5685#include "device/fido/get_assertion_request_handler.h"
Adem Derineldae9eff2025-01-23 12:16:0086#include "device/fido/json_request.h"
Manas Verma2ff0cc572019-03-19 23:42:5687#include "device/fido/make_credential_request_handler.h"
Adem Derineldae9eff2025-01-23 12:16:0088#include "device/fido/prf_input.h"
Adam Langleya09284eb2020-06-11 18:58:2989#include "device/fido/public_key.h"
Manas Verma2ff0cc572019-03-19 23:42:5690#include "device/fido/public_key_credential_descriptor.h"
91#include "device/fido/public_key_credential_params.h"
Adem Derineldae9eff2025-01-23 12:16:0092#include "mojo/public/cpp/bindings/message.h"
Manas Verma2ff0cc572019-03-19 23:42:5693#include "net/cert/asn1_util.h"
Ken Buchanan23dce912024-07-11 16:41:2794#include "services/metrics/public/cpp/ukm_builders.h"
95#include "services/metrics/public/cpp/ukm_recorder.h"
Adem Derineldae9eff2025-01-23 12:16:0096#include "services/metrics/public/cpp/ukm_source_id.h"
Adem Derineldae9eff2025-01-23 12:16:0097#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
Bob Beck8b76aac2023-11-03 14:18:1298#include "third_party/boringssl/src/pki/input.h"
99#include "third_party/boringssl/src/pki/parse_values.h"
100#include "third_party/boringssl/src/pki/parser.h"
Manas Verma2ff0cc572019-03-19 23:42:56101
Xiaohan Wang2ba85e32022-01-15 17:19:40102#if BUILDFLAG(IS_MAC)
Martin Kreichgauer263f6d82020-03-10 05:46:45103#include "device/fido/mac/credential_metadata.h"
104#endif
105
Howard Yang72a1412b2022-04-13 00:16:02106#if BUILDFLAG(IS_CHROMEOS)
Martin Kreichgauer46fec9c2021-02-02 07:19:21107#include "device/fido/cros/authenticator.h"
Nina Satragnoc3444e8f2022-08-04 22:43:00108#endif
109
110#if BUILDFLAG(IS_WIN)
Nina Satragnoc3444e8f2022-08-04 22:43:00111#include "device/fido/win/authenticator.h"
Adam Langley30375442023-06-19 17:20:26112#include "device/fido/win/webauthn_api.h"
Nina Satragnoc3444e8f2022-08-04 22:43:00113#endif
114
115#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
116#include "content/browser/webauth/is_uvpaa.h"
Martin Kreichgauer46fec9c2021-02-02 07:19:21117#endif
118
Manas Verma2ff0cc572019-03-19 23:42:56119namespace content {
120
Martin Kreichgauer3b3a6572020-09-24 08:49:48121// RequestExtension is a type of extension in a WebAuthn request that might
122// yield an extension output in the response.
123enum class RequestExtension {
124 kAppID,
125 kHMACSecret,
126 kPRF,
127 kCredProps,
Nina Satragno9de1788d2020-10-08 21:00:51128 kLargeBlobEnable,
129 kLargeBlobRead,
130 kLargeBlobWrite,
Adam Langley77bbb1f2021-04-16 16:44:10131 kCredBlob,
132 kGetCredBlob,
Adam Langley4b0ca3e22021-11-29 20:51:18133 kMinPINLength,
Adam Langley70a24152022-08-30 02:01:04134};
135
136enum class AttestationErasureOption {
137 kIncludeAttestation,
138 kEraseAttestationButIncludeAaguid,
139 kEraseAttestationAndAaguid,
Martin Kreichgauer3b3a6572020-09-24 08:49:48140};
141
Adem Derinele4777b62024-09-04 07:26:54142using MakeCredentialCallback =
143 blink::mojom::Authenticator::MakeCredentialCallback;
Adem Derinel72e11db2025-02-11 15:58:00144using GetCredentialCallback =
145 blink::mojom::Authenticator::GetCredentialCallback;
Adem Derinele4777b62024-09-04 07:26:54146using ReportCallback = blink::mojom::Authenticator::ReportCallback;
Martin Kreichgauer77def30f02024-11-11 20:16:58147using UIPresentation = AuthenticatorRequestClientDelegate::UIPresentation;
Adem Derineldb926582025-02-13 09:04:29148using Mediation = blink::mojom::Mediation;
Ken Buchanan23dce912024-07-11 16:41:27149
Manas Verma2ff0cc572019-03-19 23:42:56150namespace {
151
Adem Derinel38626c22025-05-22 13:31:58152const char kImmediateTimeoutWhileWaitingForUi[] =
153 "WebAuthentication.GetAssertion.Immediate.TimeoutWhileWaitingForUi";
154
Martin Kreichgauer37ace492021-04-08 23:36:46155WebAuthenticationDelegate* GetWebAuthenticationDelegate() {
156 return GetContentClient()->browser()->GetWebAuthenticationDelegate();
157}
158
Manas Verma2ff0cc572019-03-19 23:42:56159// The application parameter is the SHA-256 hash of the UTF-8 encoding of
160// the application identity (i.e. relying_party_id) of the application
161// requesting the registration.
Ellyac2e8242025-05-02 00:11:49162std::array<uint8_t, crypto::hash::kSha256Size> CreateApplicationParameter(
Manas Verma2ff0cc572019-03-19 23:42:56163 const std::string& relying_party_id) {
Ellyac2e8242025-05-02 00:11:49164 return crypto::hash::Sha256(relying_party_id);
Manas Verma2ff0cc572019-03-19 23:42:56165}
166
Adam Langleya4095902019-04-23 18:31:39167device::CtapGetAssertionRequest CreateCtapGetAssertionRequest(
168 const std::string& client_data_json,
169 const blink::mojom::PublicKeyCredentialRequestOptionsPtr& options,
Arthur Sonzognic686e8f2024-01-11 08:36:37170 std::optional<std::string> app_id) {
Adam Langleya4095902019-04-23 18:31:39171 device::CtapGetAssertionRequest request_parameter(options->relying_party_id,
172 client_data_json);
173
Ken Buchanan4a33ef0d2019-06-20 17:34:04174 request_parameter.allow_list = options->allow_credentials;
Adam Langleya4095902019-04-23 18:31:39175
Ken Buchanan4a33ef0d2019-06-20 17:34:04176 request_parameter.user_verification = options->user_verification;
Adam Langleya4095902019-04-23 18:31:39177
178 if (app_id) {
179 request_parameter.alternative_application_parameter =
180 CreateApplicationParameter(*app_id);
181 request_parameter.app_id = std::move(*app_id);
182 }
183
Slobodan Pejicc8ce88b2023-06-19 15:41:12184 if (!options->extensions->cable_authentication_data.empty()) {
185 request_parameter.cable_extension =
186 options->extensions->cable_authentication_data;
Adam Langleya4095902019-04-23 18:31:39187 }
Adam Langleya4095902019-04-23 18:31:39188 return request_parameter;
189}
190
Manas Verma2ff0cc572019-03-19 23:42:56191// Parses the FIDO transport types extension from the DER-encoded, X.509
Adam Langley9095b4182022-07-20 16:14:50192// certificate in |der_cert| and adds any transport types found to
193// |out_transports|. Returns true if any transports were added.
194bool AddTransportsFromCertificate(
Manas Verma2ff0cc572019-03-19 23:42:56195 base::span<const uint8_t> der_cert,
Adam Langley9095b4182022-07-20 16:14:50196 base::flat_set<device::FidoTransportProtocol>* out_transports) {
Manas Verma2ff0cc572019-03-19 23:42:56197 // See
198 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-authenticator-transports-extension-v1.2-ps-20170411.html#fido-u2f-certificate-transports-extension
Adem Derinel620520b42024-11-04 15:45:44199 static constexpr std::array<uint8_t, 11> kTransportTypesOID = {
Manas Verma2ff0cc572019-03-19 23:42:56200 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01};
201 bool present, critical;
Md Hasibul Hasana963a9342024-04-03 10:15:14202 std::string_view contents;
Manas Verma2ff0cc572019-03-19 23:42:56203 if (!net::asn1::ExtractExtensionFromDERCert(
Adem Derinel620520b42024-11-04 15:45:44204 base::as_string_view(der_cert),
205 base::as_string_view(kTransportTypesOID), &present, &critical,
206 &contents) ||
Manas Verma2ff0cc572019-03-19 23:42:56207 !present) {
Adam Langley9095b4182022-07-20 16:14:50208 return false;
Manas Verma2ff0cc572019-03-19 23:42:56209 }
210
Bob Beck8b76aac2023-11-03 14:18:12211 const bssl::der::Input contents_der(contents);
212 bssl::der::Parser contents_parser(contents_der);
Arthur Sonzognic686e8f2024-01-11 08:36:37213 std::optional<bssl::der::BitString> transport_bits =
Hubert Chaoef833572022-04-21 20:58:39214 contents_parser.ReadBitString();
215 if (!transport_bits) {
Adam Langley9095b4182022-07-20 16:14:50216 return false;
Manas Verma2ff0cc572019-03-19 23:42:56217 }
218
219 // The certificate extension contains a BIT STRING where different bits
220 // indicate support for different transports. The following array maps
221 // between these bit indexes and the FidoTransportProtocol enum.
222 static constexpr struct {
223 uint8_t bit_index;
224 device::FidoTransportProtocol transport;
225 } kTransportMapping[] = {
226 // Bit 0 is "Bluetooth Classic", not BLE. Since webauthn doesn't define a
227 // transport type for this we ignore it.
228 {1, device::FidoTransportProtocol::kBluetoothLowEnergy},
229 {2, device::FidoTransportProtocol::kUsbHumanInterfaceDevice},
230 {3, device::FidoTransportProtocol::kNearFieldCommunication},
231 {4, device::FidoTransportProtocol::kInternal},
232 };
233
Adam Langley9095b4182022-07-20 16:14:50234 bool ret = false;
Manas Verma2ff0cc572019-03-19 23:42:56235 for (const auto& mapping : kTransportMapping) {
Adam Langley9095b4182022-07-20 16:14:50236 if (transport_bits->AssertsBit(mapping.bit_index)) {
237 out_transports->insert(mapping.transport);
238 ret |= true;
Manas Verma2ff0cc572019-03-19 23:42:56239 }
240 }
Adam Langley9095b4182022-07-20 16:14:50241
242 return ret;
Manas Verma2ff0cc572019-03-19 23:42:56243}
244
Arthur Sonzognic686e8f2024-01-11 08:36:37245base::TimeDelta AdjustTimeout(std::optional<base::TimeDelta> timeout,
Nina Satragno2aab5c72020-03-03 23:04:05246 RenderFrameHost* render_frame_host) {
247 // Time to wait for an authenticator to successfully complete an operation.
Adem Derinel7b596362024-05-26 14:29:01248 base::TimeDelta adjusted_timeout_lower = base::Minutes(3);
249 base::TimeDelta adjusted_timeout_upper = base::Hours(20);
Martin Kreichgauer0b24720b2020-08-17 19:58:23250 if (!timeout) {
Nina Satragno13c509e2023-10-27 17:19:35251 return adjusted_timeout_upper;
Martin Kreichgauer0b24720b2020-08-17 19:58:23252 }
253 const bool testing_api_enabled =
Adam Langley1e03fb02023-03-16 23:02:03254 AuthenticatorEnvironment::GetInstance()->IsVirtualAuthenticatorEnabledFor(
255 static_cast<RenderFrameHostImpl*>(render_frame_host)
Nina Satragno13c509e2023-10-27 17:19:35256 ->frame_tree_node()) ||
257 AuthenticatorEnvironment::GetInstance()
258 ->MaybeGetDiscoveryFactoryTestOverride();
Nina Satragno2aab5c72020-03-03 23:04:05259 if (testing_api_enabled) {
260 return *timeout;
261 }
Nina Satragno13c509e2023-10-27 17:19:35262 return std::max(adjusted_timeout_lower,
263 std::min(adjusted_timeout_upper, *timeout));
Nina Satragno2aab5c72020-03-03 23:04:05264}
265
Martin Kreichgauer0904dd22021-06-29 09:37:56266bool UsesDiscoverableCreds(const device::MakeCredentialOptions& options) {
Adam Langleybd369fab2021-05-25 00:37:20267 return options.resident_key == device::ResidentKeyRequirement::kRequired;
268}
269
270bool UsesDiscoverableCreds(const device::CtapGetAssertionRequest& request) {
271 return request.allow_list.empty();
272}
273
Martin Kreichgauer0b1f5592021-07-02 22:28:25274// GetWebAuthnTransports returns the set of transports that should be passed to
275// a FidoRequestHandler for a WebAuthn request. This determines for which
Martin Kreichgauer263f6d82020-03-10 05:46:45276// transports the request handler will attempt to obtain FidoDiscovery
Martin Kreichgauer4ce13be2022-10-28 23:20:21277// instances.
Martin Kreichgauer0b1f5592021-07-02 22:28:25278base::flat_set<device::FidoTransportProtocol> GetWebAuthnTransports(
Martin Kreichgauer37ace492021-04-08 23:36:46279 RenderFrameHost* render_frame_host,
Martin Kreichgauer0b24720b2020-08-17 19:58:23280 device::FidoDiscoveryFactory* discovery_factory,
Ken Buchanan90fe29552024-04-26 21:15:48281 bool uses_discoverable_creds,
Adem Derineldb926582025-02-13 09:04:29282 std::optional<bool> is_uvpaa_override,
283 bool is_immediate_mediation = false) {
Manas Verma2ff0cc572019-03-19 23:42:56284 base::flat_set<device::FidoTransportProtocol> transports;
Adem Derineldb926582025-02-13 09:04:29285 if (!is_immediate_mediation) {
286 transports.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice);
287 transports.insert(device::FidoTransportProtocol::kHybrid);
288 }
Martin Kreichgauer193d120c2020-12-15 04:58:02289
Martin Kreichgauer012f0802021-01-21 02:28:30290 // Only instantiate platform discovery if the embedder hasn't chosen to
291 // override IsUserVerifyingPlatformAuthenticatorAvailable() to be false.
292 // Chrome disables platform authenticators in Guest modes this way.
Adem Derineldb926582025-02-13 09:04:29293 if (is_uvpaa_override.value_or(true)) {
Martin Kreichgauer193d120c2020-12-15 04:58:02294 transports.insert(device::FidoTransportProtocol::kInternal);
295 }
Manas Verma2ff0cc572019-03-19 23:42:56296
Martin Kreichgauer0b24720b2020-08-17 19:58:23297 if (discovery_factory->IsTestOverride()) {
Adem Derineldb926582025-02-13 09:04:29298 if (!is_immediate_mediation) {
299 // The desktop implementation does not support BLE or NFC, but we emulate
300 // them if the testing API is enabled.
301 transports.insert(device::FidoTransportProtocol::kBluetoothLowEnergy);
302 transports.insert(device::FidoTransportProtocol::kNearFieldCommunication);
303 }
Martin Kreichgauer012f0802021-01-21 02:28:30304
305 // Ensure virtual platform authenticators can be instantiated even if they
306 // are not-user-verifying, i.e. IsUVPAA() returns false.
307 transports.insert(device::FidoTransportProtocol::kInternal);
Martin Kreichgauer263f6d82020-03-10 05:46:45308 }
Manas Verma2ff0cc572019-03-19 23:42:56309 return transports;
310}
311
Martin Kreichgauer0b24720b2020-08-17 19:58:23312// Returns a new FidoDiscoveryFactory for the current request. This may be
313// a factory for virtual authenticators if the testing API is enabled for the
314// given frame.
315std::unique_ptr<device::FidoDiscoveryFactory> MakeDiscoveryFactory(
Martin Kreichgauer4ce13be2022-10-28 23:20:21316 RenderFrameHost* render_frame_host) {
Martin Kreichgauer0b24720b2020-08-17 19:58:23317 VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
Adam Langley1e03fb02023-03-16 23:02:03318 AuthenticatorEnvironment::GetInstance()
Nina Satragno1c4c1fe2022-08-31 21:31:22319 ->MaybeGetVirtualAuthenticatorManager(
320 static_cast<RenderFrameHostImpl*>(render_frame_host)
321 ->frame_tree_node());
Martin Kreichgauer0b24720b2020-08-17 19:58:23322 if (virtual_authenticator_manager) {
323 return virtual_authenticator_manager->MakeDiscoveryFactory();
324 }
325
326 auto discovery_factory = std::make_unique<device::FidoDiscoveryFactory>();
Martin Kreichgauer37ace492021-04-08 23:36:46327
Xiaohan Wang2ba85e32022-01-15 17:19:40328#if BUILDFLAG(IS_MAC)
Martin Kreichgauer0b24720b2020-08-17 19:58:23329 discovery_factory->set_mac_touch_id_info(
Martin Kreichgauer37ace492021-04-08 23:36:46330 GetWebAuthenticationDelegate()->GetTouchIdAuthenticatorConfig(
331 render_frame_host->GetBrowserContext()));
Xiaohan Wang2ba85e32022-01-15 17:19:40332#endif // BUILDFLAG(IS_MAC)
Martin Kreichgauer0b24720b2020-08-17 19:58:23333
Howard Yang72a1412b2022-04-13 00:16:02334#if BUILDFLAG(IS_CHROMEOS)
Martin Kreichgauer4ce13be2022-10-28 23:20:21335 // Ignore the ChromeOS u2fd virtual U2F HID device so that it doesn't collide
336 // with the ChromeOS platform authenticator, also implemented in u2fd.
Nina Satragnoa17896b62023-11-17 17:47:22337 // There are two possible PIDs the virtual U2F HID device could use, with or
338 // without corp protocol functionality.
339 constexpr device::VidPid kChromeOsU2fdVidPid{0x18d1, 0x502c};
340 constexpr device::VidPid kChromeOsU2fdCorpVidPid{0x18d1, 0x5212};
341 discovery_factory->set_hid_ignore_list(
342 {kChromeOsU2fdVidPid, kChromeOsU2fdCorpVidPid});
343 discovery_factory->set_generate_request_id_callback(
344 GetWebAuthenticationDelegate()->GetGenerateRequestIdCallback(
345 render_frame_host));
Howard Yang72a1412b2022-04-13 00:16:02346#endif // BUILDFLAG(IS_CHROMEOS)
Martin Kreichgauer211385fc2020-08-19 02:14:26347
Martin Kreichgauer0b24720b2020-08-17 19:58:23348 return discovery_factory;
349}
350
Arthur Sonzognic686e8f2024-01-11 08:36:37351std::optional<device::CredProtectRequest> ProtectionPolicyToCredProtect(
Adam Langleyafd522f2023-01-27 21:12:33352 blink::mojom::ProtectionPolicy protection_policy,
353 const device::MakeCredentialOptions& make_credential_options) {
354 switch (protection_policy) {
355 case blink::mojom::ProtectionPolicy::UNSPECIFIED:
356 // Some platform authenticators have the behaviour that uv=required
357 // demands a local reauthentication but uv=preferred can be satisfied by
358 // just clicking a button. Since the device has to be unlocked by the
359 // user, this seems to balance the demands of uv=required against the
360 // fact that quite a number of (non-mobile) devices lack biometrics and
361 // thus full UV requires entering the local password. Since password
362 // autofill doesn't demand entering the local password all the time, it
363 // would be sad if WebAuthn was much worse in that respect.
364 //
365 // Also, some sites have (or will) implement a sign-in flow where the
366 // user enters their username and then the site makes a WebAuthn
367 // request, with an allowlist, where completing that request is
368 // sufficient to sign-in. I.e. there's no additional password challenge.
369 // Since these sites are trying to replace passwords, we expect them to
370 // set uv=preferred in order to work well with the platform behaviour
371 // detailed in the first paragraph.
372 //
373 // If such sites remembered the UV flag from the registration and enforced
374 // it at assertion time, that would break situations where closing a
375 // laptop lid covers the biometric sensor and makes entering a password
376 // preferable. But without any enforcement of the UV flag, someone could
377 // pick a security key off the ground and do a uv=false request to get a
378 // sufficient assertion.
379 //
380 // Thus if rk=required and uv=preferred, credProtect level three is set
381 // to tell security keys to only create an assertion after UV for this
382 // credential. (Sites can still override this by setting a specific
383 // credProtect level.)
384 //
385 // If a site sets rk=preferred then we assume that they're doing something
386 // unusual and will only set credProtect level two.
387 //
388 // See also
389 // https://chromium.googlesource.com/chromium/src/+/main/content/browser/webauth/cred_protect.md
390 if (make_credential_options.resident_key ==
391 device::ResidentKeyRequirement::kRequired &&
392 make_credential_options.user_verification ==
Adam Langleyc9e7acef2023-05-26 22:21:48393 device::UserVerificationRequirement::kPreferred) {
Adam Langleyafd522f2023-01-27 21:12:33394 return device::CredProtectRequest::kUVRequired;
395 }
Adam Langley04a063d2024-04-12 22:21:39396#if BUILDFLAG(IS_WIN)
397 // On Windows, if webauthn.dll is version two or below, rk=preferred
398 // cannot be expressed and will be mapped to rk=false. Some security keys
399 // have a bug where they'll return credProtect=1 when credProtect=2 is
400 // requested for non-discoverable credentials. Thus, for these versions
401 // of webauthn.dll, treat rk=preferred as rk=discouraged for the purposes
402 // of credProtect, because that's what will ultimately be sent to the
403 // security key.
404 //
405 // If a site explicitly requests a credProtect level, we'll still respect
406 // that because they are presumably going to check the response.
Adem Derinelc536cd32025-03-20 16:42:27407 if (make_credential_options.resident_key ==
Adam Langley04a063d2024-04-12 22:21:39408 device::ResidentKeyRequirement::kPreferred &&
409 device::WinWebAuthnApi::GetDefault() &&
410 device::WinWebAuthnApi::GetDefault()->Version() < 3) {
411 return std::nullopt;
412 }
413#endif
Adam Langleyafd522f2023-01-27 21:12:33414 if (make_credential_options.resident_key !=
415 device::ResidentKeyRequirement::kDiscouraged) {
416 // Otherwise, kUVOrCredIDRequired is made the default unless
417 // the authenticator defaults to something better.
418 return device::CredProtectRequest::kUVOrCredIDRequiredOrBetter;
419 }
Arthur Sonzognic686e8f2024-01-11 08:36:37420 return std::nullopt;
Adam Langleyafd522f2023-01-27 21:12:33421 case blink::mojom::ProtectionPolicy::NONE:
422 return device::CredProtectRequest::kUVOptional;
423 case blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED:
424 return device::CredProtectRequest::kUVOrCredIDRequired;
425 case blink::mojom::ProtectionPolicy::UV_REQUIRED:
426 return device::CredProtectRequest::kUVRequired;
427 }
428}
429
Arthur Sonzognic686e8f2024-01-11 08:36:37430std::optional<device::PRFInput> ParsePRFInputForMakeCredential(
Adam Langley5c0c8ab72023-10-10 23:06:12431 const blink::mojom::PRFValuesPtr& prf_input_from_renderer) {
432 // The input cannot be credential-specific because we haven't created the
433 // credential yet.
434 if (prf_input_from_renderer->id) {
Arthur Sonzognic686e8f2024-01-11 08:36:37435 return std::nullopt;
Adam Langley5c0c8ab72023-10-10 23:06:12436 }
437
438 device::PRFInput prf_input;
Adam Langley337cf3d82024-10-24 14:17:17439 prf_input.input1 = prf_input_from_renderer->first;
Adam Langley5c0c8ab72023-10-10 23:06:12440 if (prf_input_from_renderer->second) {
Adam Langley337cf3d82024-10-24 14:17:17441 prf_input.input2 = prf_input_from_renderer->second;
Adam Langley5c0c8ab72023-10-10 23:06:12442 }
Alexis Hetu0a7e11252025-01-09 19:28:11443 prf_input.HashInputsIntoSalts();
Adam Langley5c0c8ab72023-10-10 23:06:12444
445 return prf_input;
446}
447
Arthur Sonzognic686e8f2024-01-11 08:36:37448std::optional<std::vector<device::PRFInput>> ParsePRFInputsForGetAssertion(
Adam Langley1d1adaf2023-07-12 22:55:26449 base::span<const blink::mojom::PRFValuesPtr> inputs) {
450 std::vector<device::PRFInput> ret;
451 bool is_first = true;
Arthur Sonzognic686e8f2024-01-11 08:36:37452 std::optional<std::vector<uint8_t>> last_id;
Adam Langley1d1adaf2023-07-12 22:55:26453
454 // TODO(agl): should match the credential IDs from the allow list, which
455 // will also limit the size to the size of the allow list.
456 for (const auto& prf_input_from_renderer : inputs) {
457 device::PRFInput prf_input;
458
459 // This statement enforces invariants that should be established by the
460 // renderer.
461 if (
462 // Only the first element in the vector may be the default.
463 (!is_first && !prf_input_from_renderer->id) ||
464 // The PRF inputs must be sorted by credential ID to show that there
465 // are no duplicates.
466 (last_id.has_value() && prf_input_from_renderer->id.has_value() &&
467 *last_id >= *prf_input_from_renderer->id)) {
Arthur Sonzognic686e8f2024-01-11 08:36:37468 return std::nullopt;
Adam Langley1d1adaf2023-07-12 22:55:26469 }
470 is_first = false;
471 last_id = prf_input_from_renderer->id;
472
473 if (prf_input_from_renderer->id) {
474 prf_input.credential_id = std::move(*prf_input_from_renderer->id);
475 }
476
Adam Langley0d424242024-11-05 18:46:35477 prf_input.input1 = prf_input_from_renderer->first;
Adam Langley1d1adaf2023-07-12 22:55:26478 if (prf_input_from_renderer->second) {
Adam Langley0d424242024-11-05 18:46:35479 prf_input.input2 = prf_input_from_renderer->second;
Adam Langley1d1adaf2023-07-12 22:55:26480 }
Alexis Hetu0a7e11252025-01-09 19:28:11481 prf_input.HashInputsIntoSalts();
Adam Langley1d1adaf2023-07-12 22:55:26482
483 ret.emplace_back(std::move(prf_input));
484 }
485
486 return ret;
487}
488
Adam Langley5c0c8ab72023-10-10 23:06:12489blink::mojom::PRFValuesPtr PRFResultsToValues(
490 base::span<const uint8_t> results) {
491 auto prf_values = blink::mojom::PRFValues::New();
492 DCHECK(results.size() == 32 || results.size() == 64);
Elly450ad9fe2025-07-09 17:13:22493 const auto [first, second] = results.split_at<32>();
494 prf_values->first = base::ToVector(first);
Peter Kasting1b56352f2024-11-17 20:47:33495 if (!second.empty()) {
Elly450ad9fe2025-07-09 17:13:22496 prf_values->second = base::ToVector(second);
Adam Langley5c0c8ab72023-10-10 23:06:12497 }
498
499 return prf_values;
500}
501
Adam Langleya65976912023-12-14 22:13:30502void SetHints(AuthenticatorRequestClientDelegate* request_delegate,
Tom Sepez1c93c272024-10-03 21:39:24503 const base::flat_set<blink::mojom::Hint>& hints) {
Adam Langleya65976912023-12-14 22:13:30504 // The first recognised transport takes priority.
Arthur Sonzognic686e8f2024-01-11 08:36:37505 std::optional<device::FidoTransportProtocol> transport;
Adam Langleya65976912023-12-14 22:13:30506 for (const auto hint : hints) {
507 switch (hint) {
508 case blink::mojom::Hint::SECURITY_KEY:
509 transport = transport.value_or(
510 device::FidoTransportProtocol::kUsbHumanInterfaceDevice);
511 break;
512 case blink::mojom::Hint::CLIENT_DEVICE:
513 transport =
514 transport.value_or(device::FidoTransportProtocol::kInternal);
515 break;
516 case blink::mojom::Hint::HYBRID:
517 transport = transport.value_or(device::FidoTransportProtocol::kHybrid);
518 break;
519 }
520 }
521
522 if (transport) {
523 AuthenticatorRequestClientDelegate::Hints delegate_hints;
524 delegate_hints.transport = transport;
525 request_delegate->SetHints(delegate_hints);
526 }
527}
528
Adam Langleyae4001f12024-06-06 17:03:41529bool IsPlatformAuthenticatorForInvalidStateError(
530 const device::FidoAuthenticator* authenticator) {
531 switch (authenticator->GetType()) {
532 case device::AuthenticatorType::kTouchID:
533 case device::AuthenticatorType::kChromeOS:
534 case device::AuthenticatorType::kICloudKeychain:
535 case device::AuthenticatorType::kEnclave:
Adam Langleyae4001f12024-06-06 17:03:41536 return true;
537 // kWinNative can be a platform authenticator but, in the context where this
538 // function is used, Windows returns a specific error when InvalidStateError
539 // should be returned. Thus, if it didn't return that, then we shouldn't
540 // consider it a platform authenticator.
541 case device::AuthenticatorType::kWinNative:
542 case device::AuthenticatorType::kOther:
543 case device::AuthenticatorType::kPhone:
544 return false;
545 }
546}
547
Ken Buchanand5edc0782024-06-10 22:01:22548AuthenticatorCommonImpl::CredentialRequestResult
549CredentialRequestResultFromCode(bool success, device::AuthenticatorType type) {
550 switch (type) {
551 case device::AuthenticatorType::kChromeOS:
Ken Buchanand5edc0782024-06-10 22:01:22552 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
553 kChromeOSSuccess
554 : AuthenticatorCommonImpl::CredentialRequestResult::
555 kChromeOSError;
556 case device::AuthenticatorType::kEnclave:
557 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
558 kEnclaveSuccess
559 : AuthenticatorCommonImpl::CredentialRequestResult::
560 kEnclaveError;
561 case device::AuthenticatorType::kICloudKeychain:
562 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
563 kICloudKeychainSuccess
564 : AuthenticatorCommonImpl::CredentialRequestResult::
565 kICloudKeychainError;
566 case device::AuthenticatorType::kOther:
567 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
568 kOtherSuccess
569 : AuthenticatorCommonImpl::CredentialRequestResult::
570 kOtherError;
571 case device::AuthenticatorType::kPhone:
572 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
573 kPhoneSuccess
574 : AuthenticatorCommonImpl::CredentialRequestResult::
575 kPhoneError;
576 case device::AuthenticatorType::kTouchID:
577 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
578 kTouchIDSuccess
579 : AuthenticatorCommonImpl::CredentialRequestResult::
580 kTouchIDError;
581 case device::AuthenticatorType::kWinNative:
582 return success ? AuthenticatorCommonImpl::CredentialRequestResult::
583 kWinNativeSuccess
584 : AuthenticatorCommonImpl::CredentialRequestResult::
585 kWinNativeError;
586 }
587}
588
Ken Buchanan1ea549c12024-10-10 21:31:05589void RecordRegisterOutcomeMetric(std::optional<AuthenticationRequestMode> mode,
Ken Buchanan23dce912024-07-11 16:41:27590 ukm::SourceId source_id,
591 MakeCredentialOutcome outcome) {
592 CHECK(mode.has_value());
Ken Buchanan1ea549c12024-10-10 21:31:05593 CHECK(*mode != AuthenticationRequestMode::kConditional);
Ken Buchanan23dce912024-07-11 16:41:27594 ukm::builders::WebAuthn_RegisterCompletion(source_id)
595 .SetRegisterCompletionResult(static_cast<int>(outcome))
596 .SetRequestMode(static_cast<int>(*mode))
597 .Record(ukm::UkmRecorder::Get());
598}
599
Ken Buchanan1ea549c12024-10-10 21:31:05600void RecordSignOutcomeMetric(std::optional<AuthenticationRequestMode> mode,
Ken Buchanan23dce912024-07-11 16:41:27601 ukm::SourceId source_id,
602 GetAssertionOutcome outcome) {
603 CHECK(mode.has_value());
604 ukm::builders::WebAuthn_SignCompletion(source_id)
605 .SetSignCompletionResult(static_cast<int>(outcome))
606 .SetRequestMode(static_cast<int>(*mode))
607 .Record(ukm::UkmRecorder::Get());
608}
609
Andrii Natiahlyi6b2f4b12024-09-03 14:58:42610blink::mojom::WebAuthnClientCapabilityPtr MakeCapability(std::string name,
611 bool available) {
612 return blink::mojom::WebAuthnClientCapability::New(std::move(name),
613 available);
614}
615
616inline bool HasSupportedCapability(
617 const std::vector<blink::mojom::WebAuthnClientCapabilityPtr>& capabilities,
618 std::string_view capability_name) {
619 auto capability_it =
620 std::find_if(capabilities.begin(), capabilities.end(),
621 [&capability_name](const auto& capability) {
622 return capability->name == capability_name;
623 });
624
625 CHECK(capability_it != capabilities.end())
626 << "Capability " << capability_name << " not found.";
627 return (*capability_it)->supported;
628}
629
630std::vector<blink::mojom::WebAuthnClientCapabilityPtr> InsertIsPPAACapability(
631 std::vector<blink::mojom::WebAuthnClientCapabilityPtr> capabilities) {
632 bool isUVPAA = HasSupportedCapability(
633 capabilities, client_capabilities::kUserVerifyingPlatformAuthenticator);
634 bool hybridTransport = HasSupportedCapability(
635 capabilities, client_capabilities::kHybridTransport);
636
637 capabilities.push_back(
638 MakeCapability(client_capabilities::kPasskeyPlatformAuthenticator,
639 isUVPAA || hybridTransport));
Martin Kreichgauera57d2f12025-03-12 16:47:45640
Andrii Natiahlyi6b2f4b12024-09-03 14:58:42641 return capabilities;
642}
643
Nina Satragnocb0406e2024-09-09 19:47:48644void DeleteUnacceptedVirtualAuthenticatorCreds(
645 RenderFrameHost* render_frame_host,
646 std::string_view relying_party_id,
647 base::span<uint8_t> user_id,
648 base::span<std::vector<uint8_t>> all_accepted_credentials_ids) {
649 FrameTreeNode* frame_tree_node =
650 static_cast<RenderFrameHostImpl*>(render_frame_host)->frame_tree_node();
651 VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
652 AuthenticatorEnvironment::GetInstance()
653 ->MaybeGetVirtualAuthenticatorManager(frame_tree_node);
654 if (!virtual_authenticator_manager) {
655 return;
656 }
657 for (VirtualAuthenticator* authenticator :
658 virtual_authenticator_manager->GetAuthenticators()) {
659 std::vector<std::vector<uint8_t>> credential_ids_to_remove;
660 for (const auto& registration : authenticator->registrations()) {
661 if (registration.second.user && registration.second.rp &&
662 registration.second.rp->id == relying_party_id &&
663 registration.second.user->id == user_id &&
664 !base::Contains(all_accepted_credentials_ids, registration.first)) {
665 credential_ids_to_remove.push_back(registration.first);
666 }
667 }
668 for (const std::vector<uint8_t>& credential_id : credential_ids_to_remove) {
669 authenticator->RemoveRegistration(credential_id);
670 }
671 }
672}
673
674void UpdateVirtualAuthenticatorUserCreds(RenderFrameHost* render_frame_host,
675 std::string_view relying_party_id,
676 base::span<uint8_t> user_id,
677 std::string_view name,
678 std::string_view display_name) {
679 FrameTreeNode* frame_tree_node =
680 static_cast<RenderFrameHostImpl*>(render_frame_host)->frame_tree_node();
681 VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
682 AuthenticatorEnvironment::GetInstance()
683 ->MaybeGetVirtualAuthenticatorManager(frame_tree_node);
684 if (!virtual_authenticator_manager) {
685 return;
686 }
687 for (VirtualAuthenticator* authenticator :
688 virtual_authenticator_manager->GetAuthenticators()) {
Nina Satragno1bfcc3f2024-10-02 15:54:31689 authenticator->UpdateUserDetails(relying_party_id, user_id, name,
690 display_name);
Nina Satragnocb0406e2024-09-09 19:47:48691 }
692}
693
694void DeleteVirtualAuthenticatorCreds(
695 RenderFrameHost* render_frame_host,
696 const std::vector<uint8_t>& passkey_credential_id,
697 std::string_view relying_party_id) {
698 FrameTreeNode* frame_tree_node =
699 static_cast<RenderFrameHostImpl*>(render_frame_host)->frame_tree_node();
700 VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
701 AuthenticatorEnvironment::GetInstance()
702 ->MaybeGetVirtualAuthenticatorManager(frame_tree_node);
703 if (!virtual_authenticator_manager) {
704 return;
705 }
706 for (VirtualAuthenticator* authenticator :
707 virtual_authenticator_manager->GetAuthenticators()) {
708 for (const auto& registration : authenticator->registrations()) {
709 if (registration.second.rp &&
710 registration.second.rp->id == relying_party_id &&
711 registration.first == passkey_credential_id) {
712 authenticator->RemoveRegistration(passkey_credential_id);
713 return;
714 }
715 }
716 }
717}
718
Adem Derinel72e11db2025-02-11 15:58:00719auto GetCallbackForAssertion(GetCredentialCallback callback) {
720 return base::BindOnce(
721 [](blink::mojom::Authenticator::GetCredentialCallback callback,
722 blink::mojom::AuthenticatorStatus status,
723 blink::mojom::GetAssertionAuthenticatorResponsePtr credential,
724 blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
725 blink::mojom::GetAssertionResponsePtr assertion_response =
726 blink::mojom::GetAssertionResponse::New(
727 status, std::move(credential),
728 std::move(dom_exception_details));
729 blink::mojom::GetCredentialResponsePtr response =
730 blink::mojom::GetCredentialResponse::NewGetAssertionResponse(
731 std::move(assertion_response));
732 std::move(callback).Run(std::move(response));
733 },
734 std::move(callback));
735}
736
Nina Satragnoa11a1ff2025-07-07 21:05:30737base::flat_set<device::FidoTransportProtocol> GetTransportsAllowedByRP(
738 const device::CtapGetAssertionRequest& request) {
739 const base::flat_set<device::FidoTransportProtocol> kAllTransports = {
740 device::FidoTransportProtocol::kInternal,
741 device::FidoTransportProtocol::kNearFieldCommunication,
742 device::FidoTransportProtocol::kUsbHumanInterfaceDevice,
743 device::FidoTransportProtocol::kBluetoothLowEnergy,
744 device::FidoTransportProtocol::kHybrid,
745 };
746
747 const auto& allowed_list = request.allow_list;
748 if (allowed_list.empty()) {
749 return kAllTransports;
750 }
751
752 base::flat_set<device::FidoTransportProtocol> transports;
753 for (const auto& credential : allowed_list) {
754 if (credential.transports.empty()) {
755 return kAllTransports;
756 }
757 transports.insert(credential.transports.begin(),
758 credential.transports.end());
759 }
760
761 return transports;
762}
763
Manas Verma2ff0cc572019-03-19 23:42:56764} // namespace
765
Adam Langleyf59b55602023-07-05 19:51:20766// RequestState contains all state that is specific to a single WebAuthn call.
767// Since `AuthenticatorCommonImpl` can service multiple calls, it's important
768// that this state be reset after processing each one and collecting it into
769// this structure makes that easier to enforce.
770struct AuthenticatorCommonImpl::RequestState {
Adam Langleye8ba2cc2024-08-06 13:53:23771 // Uniquely identifies this request in the scope of its owning
772 // `AuthenticatorCommonImpl`.
773 RequestKey request_key;
774
Adam Langleyf59b55602023-07-05 19:51:20775 std::unique_ptr<AuthenticatorRequestClientDelegate> request_delegate;
776 std::unique_ptr<device::FidoRequestHandlerBase> request_handler;
777 std::unique_ptr<device::FidoDiscoveryFactory> discovery_factory;
778 // This dangling raw_ptr occurred in:
779 // interactive_ui_tests:
780 // WebAuthnDevtoolsAutofillIntegrationTest.SelectAccountWithAllowCredentials
781 // https://ci.chromium.org/ui/p/chromium/builders/try/mac-rel/1357012/test-results?q=ExactID%3Aninja%3A%2F%2Fchrome%2Ftest%3Ainteractive_ui_tests%2FWebAuthnDevtoolsAutofillIntegrationTest.SelectAccountWithAllowCredentials+VHash%3A81d118f1ad0b63a6
782 raw_ptr<device::FidoDiscoveryFactory,
Pârise6361d02023-07-19 09:00:43783 FlakyDanglingUntriaged | AcrossTasksDanglingUntriaged>
Adam Langleyf59b55602023-07-05 19:51:20784 discovery_factory_testing_override = nullptr;
Victor Hugo Vianna Silva00692722025-03-18 19:51:48785 std::variant<std::monostate,
786 MakeCredentialCallback,
787 GetCredentialCallback,
788 ReportCallback>
Adem Derinele4777b62024-09-04 07:26:54789 response_callback;
Adam Langleyf59b55602023-07-05 19:51:20790 std::string client_data_json;
Adam Langleyf59b55602023-07-05 19:51:20791 url::Origin caller_origin;
792 std::string relying_party_id;
793 std::unique_ptr<base::OneShotTimer> timer =
794 std::make_unique<base::OneShotTimer>();
Adem Derinel38626c22025-05-22 13:31:58795 std::unique_ptr<base::OneShotTimer> immediate_timer =
796 std::make_unique<base::OneShotTimer>();
Arthur Sonzognic686e8f2024-01-11 08:36:37797 std::optional<std::string> app_id;
Victor Hugo Vianna Silva00692722025-03-18 19:51:48798 std::variant<std::monostate,
799 device::CtapMakeCredentialRequest,
800 device::CtapGetAssertionRequest>
Adem Derinele4777b62024-09-04 07:26:54801 ctap_request;
Victor Hugo Vianna Silva00692722025-03-18 19:51:48802 std::variant<std::monostate,
803 device::MakeCredentialOptions,
804 device::CtapGetAssertionOptions>
Adem Derinele4777b62024-09-04 07:26:54805 request_options;
Adam Langleyf59b55602023-07-05 19:51:20806 blink::mojom::AuthenticatorStatus error_awaiting_user_acknowledgement =
807 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR;
808 bool discoverable_credential_request = false;
Ken Buchanan23dce912024-07-11 16:41:27809 // Indicates whether the current request is a modal WebAuthn call, a
810 // conditional UI WebAuthn call, or a payment-related request.
Ken Buchanan1ea549c12024-10-10 21:31:05811 std::optional<AuthenticationRequestMode> mode;
Adam Langleya65976912023-12-14 22:13:30812 // The hints set by the request, if any.
813 base::flat_set<blink::mojom::Hint> hints;
Adem Derinele4777b62024-09-04 07:26:54814 std::optional<CredentialRequestResult> request_result;
Victor Hugo Vianna Silva00692722025-03-18 19:51:48815 std::variant<std::monostate, MakeCredentialOutcome, GetAssertionOutcome>
Adem Derinele4777b62024-09-04 07:26:54816 request_outcome;
Adam Langleyf59b55602023-07-05 19:51:20817
818 base::flat_set<RequestExtension> requested_extensions;
819
820 // The request ID of a pending proxied MakeCredential or GetAssertion request.
Arthur Sonzognic686e8f2024-01-11 08:36:37821 std::optional<WebAuthenticationRequestProxy::RequestId>
Adam Langleyf59b55602023-07-05 19:51:20822 pending_proxied_request_id;
Adam Langley6d16761b2023-11-01 21:39:51823
824 // A pending remote validation of an RP ID.
825 std::unique_ptr<WebAuthRequestSecurityChecker::RemoteValidation>
826 remote_rp_id_validation;
Adem Derineldb926582025-02-13 09:04:29827
828 std::optional<Mediation> mediation_;
Adam Langleyf59b55602023-07-05 19:51:20829};
830
Amos Lim12696e5e32022-09-16 07:37:58831// static
832std::unique_ptr<AuthenticatorCommon> AuthenticatorCommon::Create(
833 RenderFrameHost* render_frame_host) {
Adam Langley3ec44c22023-08-10 01:04:01834 return std::make_unique<AuthenticatorCommonImpl>(
835 render_frame_host,
836 AuthenticatorCommonImpl::ServingRequestsFor::kInternalUses);
Amos Lim12696e5e32022-09-16 07:37:58837}
838
839AuthenticatorCommonImpl::AuthenticatorCommonImpl(
Adam Langley3ec44c22023-08-10 01:04:01840 RenderFrameHost* render_frame_host,
841 ServingRequestsFor serving_requests_for)
Alexander Timin8690530c2021-06-19 00:34:32842 : render_frame_host_id_(render_frame_host->GetGlobalId()),
Adam Langley3ec44c22023-08-10 01:04:01843 serving_requests_for_(serving_requests_for),
Ken Buchanan3889e2b2020-02-11 04:26:53844 security_checker_(static_cast<RenderFrameHostImpl*>(render_frame_host)
Adem Derinel6329d722025-08-08 08:35:59845 ->GetWebAuthRequestSecurityChecker()) {}
Manas Verma2ff0cc572019-03-19 23:42:56846
Amos Lim12696e5e32022-09-16 07:37:58847AuthenticatorCommonImpl::~AuthenticatorCommonImpl() = default;
Manas Verma2ff0cc572019-03-19 23:42:56848
Nina Satragnof3b63e72019-08-20 16:44:38849std::unique_ptr<AuthenticatorRequestClientDelegate>
Amos Lim12696e5e32022-09-16 07:37:58850AuthenticatorCommonImpl::MaybeCreateRequestDelegate() {
Adam Langleyb0385822021-03-19 23:34:00851 RenderFrameHostImpl* const render_frame_host_impl =
852 static_cast<RenderFrameHostImpl*>(GetRenderFrameHost());
Nina Satragno8cf72fc2022-05-12 19:04:56853 std::unique_ptr<AuthenticatorRequestClientDelegate> delegate =
854 GetContentClient()->browser()->GetWebAuthenticationRequestDelegate(
855 render_frame_host_impl);
856 if (!delegate) {
857 return nullptr;
Nina Satragnof3b63e72019-08-20 16:44:38858 }
Nina Satragno8cf72fc2022-05-12 19:04:56859 VirtualAuthenticatorManagerImpl* virtual_authenticator_manager =
Adam Langley1e03fb02023-03-16 23:02:03860 AuthenticatorEnvironment::GetInstance()
Nina Satragno1c4c1fe2022-08-31 21:31:22861 ->MaybeGetVirtualAuthenticatorManager(
862 render_frame_host_impl->frame_tree_node());
Nina Satragno8cf72fc2022-05-12 19:04:56863 if (virtual_authenticator_manager) {
864 delegate->SetVirtualEnvironment(true);
865 if (!virtual_authenticator_manager->is_ui_enabled()) {
Martin Kreichgauer77def30f02024-11-11 20:16:58866 DisableUI();
Nina Satragno8cf72fc2022-05-12 19:04:56867 }
868 }
869 return delegate;
Manas Verma2ff0cc572019-03-19 23:42:56870}
871
Amos Lim12696e5e32022-09-16 07:37:58872void AuthenticatorCommonImpl::StartMakeCredentialRequest(
Nina Satragno70589ab2019-10-02 16:40:04873 bool allow_skipping_pin_touch) {
Adem Derinele4777b62024-09-04 07:26:54874 req_state_->request_result.reset();
Martin Kreichgauer4ce13be2022-10-28 23:20:21875 InitDiscoveryFactory();
Nina Satragnof585eca2019-07-29 20:05:32876
Adem Derinele4777b62024-09-04 07:26:54877 auto* ctap_make_credential_request =
Victor Hugo Vianna Silva00692722025-03-18 19:51:48878 &std::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request);
Adem Derinele4777b62024-09-04 07:26:54879 auto* make_credential_options =
Victor Hugo Vianna Silva00692722025-03-18 19:51:48880 &std::get<device::MakeCredentialOptions>(req_state_->request_options);
Nina Satragnoa11a1ff2025-07-07 21:05:30881 bool discover_enclave = browser_passkeys_available_ &&
882 make_credential_options->authenticator_attachment !=
883 device::AuthenticatorAttachment::kCrossPlatform;
Adam Langley8ca11472023-07-15 19:02:05884 req_state_->request_delegate->ConfigureDiscoveries(
Adam Langley3ec44c22023-08-10 01:04:01885 req_state_->caller_origin, req_state_->relying_party_id, RequestSource(),
Ken Buchanan2d78521d2023-07-18 20:24:49886 device::FidoRequestType::kMakeCredential,
Adem Derinele4777b62024-09-04 07:26:54887 make_credential_options->resident_key,
888 make_credential_options->user_verification,
889 ctap_make_credential_request->user.name,
Nina Satragnoa11a1ff2025-07-07 21:05:30890 base::span<const device::CableDiscoveryData>(), discover_enclave,
891 discovery_factory());
Adam Langleya65976912023-12-14 22:13:30892 SetHints(req_state_->request_delegate.get(), req_state_->hints);
Adam Langley68367e32019-08-30 18:49:25893
Adem Derinele4777b62024-09-04 07:26:54894 make_credential_options->allow_skipping_pin_touch = allow_skipping_pin_touch;
Adam Langley6f8b030d2020-04-06 20:10:57895
Martin Kreichgauer0b1f5592021-07-02 22:28:25896 base::flat_set<device::FidoTransportProtocol> transports =
Adem Derinele4777b62024-09-04 07:26:54897 GetWebAuthnTransports(GetRenderFrameHost(), discovery_factory(),
898 UsesDiscoverableCreds(*make_credential_options),
899 is_uvpaa_override_);
Martin Kreichgauer0b1f5592021-07-02 22:28:25900
Martin Kreichgauer88f0c722024-05-13 23:45:30901 auto platform_discoveries =
902 discovery_factory()->IsTestOverride()
903 ? std::vector<std::unique_ptr<device::FidoDiscoveryBase>>()
904 : req_state_->request_delegate->CreatePlatformDiscoveries();
Adam Langleyf59b55602023-07-05 19:51:20905 req_state_->request_handler =
906 std::make_unique<device::MakeCredentialRequestHandler>(
Martin Kreichgauer88f0c722024-05-13 23:45:30907 discovery_factory(), std::move(platform_discoveries), transports,
Adem Derinele4777b62024-09-04 07:26:54908 *ctap_make_credential_request, *make_credential_options,
Adam Langleyf59b55602023-07-05 19:51:20909 base::BindOnce(&AuthenticatorCommonImpl::OnRegisterResponse,
910 weak_factory_.GetWeakPtr()));
Nina Satragno31e3fa42019-06-03 21:44:28911
Adam Langleyf59b55602023-07-05 19:51:20912 req_state_->request_delegate->RegisterActionCallbacks(
Amos Lim12696e5e32022-09-16 07:37:58913 base::BindOnce(&AuthenticatorCommonImpl::OnCancelFromUI,
Nina Satragno31e3fa42019-06-03 21:44:28914 weak_factory_.GetWeakPtr()) /* cancel_callback */,
Adem Derinel2154d4b12025-02-04 12:29:55915 base::DoNothing() /* immediate_not_found_callback */,
Nina Satragno70589ab2019-10-02 16:40:04916 base::BindRepeating(
Amos Lim12696e5e32022-09-16 07:37:58917 &AuthenticatorCommonImpl::StartMakeCredentialRequest,
Nina Satragno70589ab2019-10-02 16:40:04918 weak_factory_.GetWeakPtr(),
919 /*allow_skipping_pin_touch=*/false) /* start_over_callback */,
Nina Satragnofe6e52ad72022-06-01 14:04:14920 base::DoNothing() /* account_preselected_callback */,
Adem Derinel72e11db2025-02-11 15:58:00921 base::DoNothing() /*password_selected_callback */,
Nina Satragno31e3fa42019-06-03 21:44:28922 base::BindRepeating(
923 &device::FidoRequestHandlerBase::StartAuthenticatorRequest,
Adam Langleyf59b55602023-07-05 19:51:20924 req_state_->request_handler->GetWeakPtr()) /* request_callback */,
Adem Derinel38626c22025-05-22 13:31:58925 base::DoNothing() /* cancel_ui_timeout_callback */,
Nina Satragno31e3fa42019-06-03 21:44:28926 base::BindRepeating(
927 &device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
Adam Langleyf59b55602023-07-05 19:51:20928 req_state_->request_handler
Nina Satragno6e0f1ab2024-06-13 22:28:11929 ->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */,
930 base::BindRepeating(
Nina Satragno15a940d432024-06-24 23:14:59931 &device::FidoRequestHandlerBase::RequestBluetoothPermission,
Nina Satragno6e0f1ab2024-06-13 22:28:11932 req_state_->request_handler
933 ->GetWeakPtr()) /* request_ble_permission_callback */);
Nina Satragnoa11a1ff2025-07-07 21:05:30934 // `set_observer` can destroy `this`. It is not safe to refer to local state
935 // after this call.
Adam Langleyf59b55602023-07-05 19:51:20936 req_state_->request_handler->set_observer(req_state_->request_delegate.get());
Nina Satragno31e3fa42019-06-03 21:44:28937}
938
Amos Lim12696e5e32022-09-16 07:37:58939void AuthenticatorCommonImpl::StartGetAssertionRequest(
Nina Satragno70589ab2019-10-02 16:40:04940 bool allow_skipping_pin_touch) {
Adem Derinele4777b62024-09-04 07:26:54941 req_state_->request_result.reset();
Martin Kreichgauer4ce13be2022-10-28 23:20:21942 InitDiscoveryFactory();
Nina Satragnof585eca2019-07-29 20:05:32943
Adam Langley3c5180c22020-08-04 21:32:55944 base::span<const device::CableDiscoveryData> cable_pairings;
Adem Derinele4777b62024-09-04 07:26:54945 auto* ctap_get_assertion_request =
Victor Hugo Vianna Silva00692722025-03-18 19:51:48946 &std::get<device::CtapGetAssertionRequest>(req_state_->ctap_request);
Adem Derinele4777b62024-09-04 07:26:54947 auto* ctap_get_assertion_options =
Victor Hugo Vianna Silva00692722025-03-18 19:51:48948 &std::get<device::CtapGetAssertionOptions>(req_state_->request_options);
Adem Derinele4777b62024-09-04 07:26:54949 if (ctap_get_assertion_request->cable_extension && IsFocused()) {
950 cable_pairings = *ctap_get_assertion_request->cable_extension;
Adam Langley68367e32019-08-30 18:49:25951 }
Nina Satragnoa11a1ff2025-07-07 21:05:30952 bool is_immediate_mediation =
953 req_state_->mediation_.value_or(Mediation::MODAL) == Mediation::IMMEDIATE;
954 base::flat_set<device::FidoTransportProtocol> transports =
955 base::STLSetIntersection<base::flat_set<device::FidoTransportProtocol>>(
956 GetWebAuthnTransports(
957 GetRenderFrameHost(), discovery_factory(),
958 UsesDiscoverableCreds(*ctap_get_assertion_request),
959 is_uvpaa_override_, is_immediate_mediation),
960 GetTransportsAllowedByRP(*ctap_get_assertion_request));
961 bool discover_enclave =
962 browser_passkeys_available_ &&
963 transports.contains(device::FidoTransportProtocol::kInternal);
Adam Langley8ca11472023-07-15 19:02:05964 req_state_->request_delegate->ConfigureDiscoveries(
Adam Langley3ec44c22023-08-10 01:04:01965 req_state_->caller_origin, req_state_->relying_party_id, RequestSource(),
Ken Buchanan2d78521d2023-07-18 20:24:49966 device::FidoRequestType::kGetAssertion,
Ken Buchananc4ae130ac2024-02-12 19:49:25967 /*resident_key_requirement=*/std::nullopt,
Adem Derinele4777b62024-09-04 07:26:54968 ctap_get_assertion_request->user_verification,
Nina Satragnoa11a1ff2025-07-07 21:05:30969 /*user_name=*/std::nullopt, cable_pairings, discover_enclave,
Adam Langleyf2f838a62024-05-03 20:37:09970 discovery_factory());
Howard Yang72a1412b2022-04-13 00:16:02971#if BUILDFLAG(IS_CHROMEOS)
Martin Kreichgauer46fec9c2021-02-02 07:19:21972 discovery_factory()->set_get_assertion_request_for_legacy_credential_check(
Adem Derinele4777b62024-09-04 07:26:54973 *ctap_get_assertion_request);
Martin Kreichgauer46fec9c2021-02-02 07:19:21974#endif
Adam Langleya65976912023-12-14 22:13:30975 SetHints(req_state_->request_delegate.get(), req_state_->hints);
Adam Langley8bf3e4f2019-08-29 21:08:11976
Martin Kreichgauer88f0c722024-05-13 23:45:30977 auto platform_discoveries =
978 discovery_factory()->IsTestOverride()
979 ? std::vector<std::unique_ptr<device::FidoDiscoveryBase>>()
980 : req_state_->request_delegate->CreatePlatformDiscoveries();
Nina Satragnofe6e52ad72022-06-01 14:04:14981 auto request_handler = std::make_unique<device::GetAssertionRequestHandler>(
Nina Satragnoa11a1ff2025-07-07 21:05:30982 discovery_factory(), std::move(platform_discoveries),
983 std::move(transports), *ctap_get_assertion_request,
984 *ctap_get_assertion_options, allow_skipping_pin_touch,
Amos Lim12696e5e32022-09-16 07:37:58985 base::BindOnce(&AuthenticatorCommonImpl::OnSignResponse,
Nina Satragno31e3fa42019-06-03 21:44:28986 weak_factory_.GetWeakPtr()));
Adem Derinelf7d43b62025-05-25 09:37:55987 request_handler->transport_availability_info()
988 .autoselect_in_immediate_mediation =
989 is_immediate_mediation &&
990 base::FeatureList::IsEnabled(device::kWebAuthnImmediateGetAutoselect) &&
991 req_state_->timer->GetCurrentDelay().InMilliseconds() % 1000 == 42;
992
Adem Derinel38626c22025-05-22 13:31:58993 auto cancel_ui_timeout_callback =
994 is_immediate_mediation
995 ? base::BindOnce(&AuthenticatorCommonImpl::CancelImmediateTimeout,
996 weak_factory_.GetWeakPtr())
997 : base::DoNothing();
Adam Langleyf59b55602023-07-05 19:51:20998 req_state_->request_delegate->RegisterActionCallbacks(
Amos Lim12696e5e32022-09-16 07:37:58999 base::BindOnce(&AuthenticatorCommonImpl::OnCancelFromUI,
Nina Satragno31e3fa42019-06-03 21:44:281000 weak_factory_.GetWeakPtr()) /* cancel_callback */,
Adem Derinel2154d4b12025-02-04 12:29:551001 base::BindOnce(
Adem Derinelbe67ee72025-06-16 07:01:301002 &AuthenticatorCommonImpl::CancelRequestForImmediateMediation,
1003 weak_factory_.GetWeakPtr()) /* immediate_not_found_callback */,
Nina Satragno70589ab2019-10-02 16:40:041004 base::BindRepeating(
Amos Lim12696e5e32022-09-16 07:37:581005 &AuthenticatorCommonImpl::StartGetAssertionRequest,
Nina Satragno70589ab2019-10-02 16:40:041006 weak_factory_.GetWeakPtr(),
1007 /*allow_skipping_pin_touch=*/false) /* start_over_callback */,
Nina Satragno31e3fa42019-06-03 21:44:281008 base::BindRepeating(
Nina Satragnofe6e52ad72022-06-01 14:04:141009 &device::GetAssertionRequestHandler::PreselectAccount,
1010 request_handler->GetWeakPtr()) /* account_preselected_callback */,
1011 base::BindRepeating(
Adem Derinel72e11db2025-02-11 15:58:001012 &AuthenticatorCommonImpl::HandlePasswordResponse,
1013 weak_factory_.GetWeakPtr()) /*password_selected_callback */,
1014 base::BindRepeating(
Nina Satragnofe6e52ad72022-06-01 14:04:141015 &device::GetAssertionRequestHandler::StartAuthenticatorRequest,
1016 request_handler->GetWeakPtr()) /* request_callback */,
Adem Derinel38626c22025-05-22 13:31:581017 std::move(cancel_ui_timeout_callback),
Nina Satragno31e3fa42019-06-03 21:44:281018 base::BindRepeating(
1019 &device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
Nina Satragnofe6e52ad72022-06-01 14:04:141020 request_handler
Nina Satragno6e0f1ab2024-06-13 22:28:111021 ->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */,
1022 base::BindRepeating(
Nina Satragno15a940d432024-06-24 23:14:591023 &device::FidoRequestHandlerBase::RequestBluetoothPermission,
Nina Satragno6e0f1ab2024-06-13 22:28:111024 request_handler->GetWeakPtr()) /* request_ble_permission_callback */);
Dominic Battréf6f1ec692019-07-27 01:59:261025
Adam Langleyf59b55602023-07-05 19:51:201026 req_state_->request_handler = std::move(request_handler);
Nina Satragnoa11a1ff2025-07-07 21:05:301027 // `set_observer` can destroy `this`. It is not safe to refer to local state
1028 // after this call.
1029 req_state_->request_handler->set_observer(req_state_->request_delegate.get());
Nina Satragno31e3fa42019-06-03 21:44:281030}
1031
Amos Lim12696e5e32022-09-16 07:37:581032bool AuthenticatorCommonImpl::IsFocused() const {
Sreeja Kamishettye49854f82021-06-02 00:52:031033 return GetRenderFrameHost()->IsActive() &&
Martin Kreichgauerfefb3772021-04-12 23:02:481034 GetWebAuthenticationDelegate()->IsFocused(
1035 WebContents::FromRenderFrameHost(GetRenderFrameHost()));
Manas Verma2ff0cc572019-03-19 23:42:561036}
1037
Manas Verma2ff0cc572019-03-19 23:42:561038// mojom::Authenticator
Amos Lim12696e5e32022-09-16 07:37:581039void AuthenticatorCommonImpl::MakeCredential(
Manas Verma9ba13692019-03-21 21:01:001040 url::Origin caller_origin,
Manas Verma2ff0cc572019-03-19 23:42:561041 blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
Adem Derinele4777b62024-09-04 07:26:541042 MakeCredentialCallback callback) {
Adem Derineldc2d52f2024-09-19 08:06:561043 base::RecordAction(base::UserMetricsAction("WebAuthn.MakeCredential.Start"));
1044 callback = base::BindOnce(
1045 &AuthenticatorCommonImpl::GetMetricsWrappedMakeCredentialCallback,
1046 weak_factory_.GetWeakPtr(), std::move(callback));
1047
Adam Langleyf59b55602023-07-05 19:51:201048 if (req_state_) {
Martin Kreichgauer4ce13be2022-10-28 23:20:211049 std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
1050 nullptr, nullptr);
1051 return;
Manas Verma2ff0cc572019-03-19 23:42:561052 }
Adam Langleyf59b55602023-07-05 19:51:201053 req_state_ = std::make_unique<RequestState>();
Adam Langleye8ba2cc2024-08-06 13:53:231054 req_state_->request_key = RequestKey(next_request_key_);
Martin Kreichgauer8c97189a2022-01-10 20:31:431055
Adem Derinele4777b62024-09-04 07:26:541056 req_state_->response_callback = std::move(callback);
Adam Langleya65976912023-12-14 22:13:301057 req_state_->hints.insert(options->hints.begin(), options->hints.end());
Manas Verma2ff0cc572019-03-19 23:42:561058
Ken Buchanan23dce912024-07-11 16:41:271059 if (options->is_payment_credential_creation) {
Ken Buchanan1ea549c12024-10-10 21:31:051060 req_state_->mode = AuthenticationRequestMode::kPayment;
Martin Kreichgauer980dcfb2024-11-19 22:22:261061 } else if (options->is_conditional) {
1062 if (!base::FeatureList::IsEnabled(device::kWebAuthnPasskeyUpgrade)) {
1063 // The renderer runtime flag should enforce this.
1064 mojo::ReportBadMessage("kWebAuthnPasskeyUpgrade flag must be enabled");
1065 return;
1066 }
1067 req_state_->mode = AuthenticationRequestMode::kPasskeyUpgrade;
Ken Buchanan23dce912024-07-11 16:41:271068 } else {
Ken Buchanan1ea549c12024-10-10 21:31:051069 req_state_->mode = AuthenticationRequestMode::kModalWebAuthn;
Ken Buchanan23dce912024-07-11 16:41:271070 }
1071
Martin Kreichgauer8c97189a2022-01-10 20:31:431072 BeginRequestTimeout(options->timeout);
1073
Liquan (Max) Gu082fa472021-08-13 02:48:271074 WebAuthRequestSecurityChecker::RequestType request_type =
1075 options->is_payment_credential_creation
1076 ? WebAuthRequestSecurityChecker::RequestType::kMakePaymentCredential
1077 : WebAuthRequestSecurityChecker::RequestType::kMakeCredential;
Martin Kreichgauerbf776d72022-04-18 23:38:161078 bool is_cross_origin_iframe = false;
Ken Buchanan3889e2b2020-02-11 04:26:531079 blink::mojom::AuthenticatorStatus status =
Liquan (Max) Gu082fa472021-08-13 02:48:271080 security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
Martin Kreichgauere255af062022-04-18 19:40:561081 &is_cross_origin_iframe);
Ken Buchanan3889e2b2020-02-11 04:26:531082 if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541083 req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
Martin Kreichgauer393f5ef2021-03-30 23:57:571084 CompleteMakeCredentialRequest(status);
Ken Buchanancb3122f12019-11-21 14:10:461085 return;
1086 }
1087
Martin Kreichgauer8d55b452021-10-04 20:51:111088 if (!security_checker_->DeduplicateCredentialDescriptorListAndValidateLength(
1089 &options->exclude_credentials)) {
1090 mojo::ReportBadMessage("invalid exclude_credentials length");
Adem Derinele4777b62024-09-04 07:26:541091 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Martin Kreichgauer8d55b452021-10-04 20:51:111092 CompleteMakeCredentialRequest(
1093 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1094 return;
1095 }
1096
Adam Langley6d16761b2023-11-01 21:39:511097 const std::string relying_party_id = options->relying_party.id;
1098 const blink::mojom::RemoteDesktopClientOverridePtr&
1099 remote_desktop_client_override = options->remote_desktop_client_override;
Andrii Natiahlyia700f91f2025-03-21 18:37:391100 std::optional<url::Origin> remote_desktop_override_origin;
1101 if (remote_desktop_client_override) {
1102 // SECURITY: RemoteDesktopClientOverride comes from the renderer process and
1103 // is untrusted. This `remote_desktop_override_origin` is only used after
1104 // ValidateDomainAndRelyingPartyID verifies that the `caller_origin` is
1105 // explicitly allowlisted via enterprise policy in
1106 // WebAuthenticationDelegateBase::OriginMayUseRemoteDesktopClientOverride().
1107 remote_desktop_override_origin = remote_desktop_client_override->origin;
1108 }
Adam Langley6d16761b2023-11-01 21:39:511109 std::unique_ptr<WebAuthRequestSecurityChecker::RemoteValidation>
1110 remote_validation = security_checker_->ValidateDomainAndRelyingPartyID(
1111 caller_origin, relying_party_id, request_type,
Andrii Natiahlyia700f91f2025-03-21 18:37:391112 remote_desktop_override_origin,
Adam Langley6d16761b2023-11-01 21:39:511113 base::BindOnce(
1114 &AuthenticatorCommonImpl::ContinueMakeCredentialAfterRpIdCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231115 weak_factory_.GetWeakPtr(), GetRequestKey(), caller_origin,
1116 std::move(options), is_cross_origin_iframe));
Adam Langley6d16761b2023-11-01 21:39:511117
1118 // If `remote_validation` is nullptr then the request may already have
1119 // completed.
1120 if (remote_validation) {
1121 req_state_->remote_rp_id_validation = std::move(remote_validation);
1122 }
1123}
1124
1125void AuthenticatorCommonImpl::ContinueMakeCredentialAfterRpIdCheck(
Adam Langleye8ba2cc2024-08-06 13:53:231126 RequestKey request_key,
Adam Langley6d16761b2023-11-01 21:39:511127 url::Origin caller_origin,
1128 blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
1129 bool is_cross_origin_iframe,
1130 blink::mojom::AuthenticatorStatus rp_id_validation_result) {
Adam Langleye8ba2cc2024-08-06 13:53:231131 if (!CheckRequestKey(request_key)) {
1132 return;
1133 }
Adam Langley6d16761b2023-11-01 21:39:511134 req_state_->remote_rp_id_validation.reset();
1135
1136 if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541137 req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
Adam Langley6d16761b2023-11-01 21:39:511138 CompleteMakeCredentialRequest(rp_id_validation_result);
Martin Kreichgauer05a33fb2022-02-18 23:31:291139 return;
1140 }
1141
Adam Langleyf59b55602023-07-05 19:51:201142 req_state_->request_delegate = MaybeCreateRequestDelegate();
1143 if (!req_state_->request_delegate) {
Adem Derinele4777b62024-09-04 07:26:541144 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571145 CompleteMakeCredentialRequest(
1146 blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
Adam Langleyeeac87e2019-04-13 22:58:221147 return;
1148 }
1149
Adam Langleyf59b55602023-07-05 19:51:201150 if (!req_state_->request_delegate->IsVirtualEnvironmentEnabled() &&
Nina Satragno8d67dec32023-04-18 22:10:441151 !disable_tls_check_ &&
Ken Buchanan6e9929372023-10-31 14:05:431152 !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
Nina Satragno52163de2022-11-23 23:03:101153 GetRenderFrameHost(), caller_origin)) {
Adem Derinele4777b62024-09-04 07:26:541154 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Nina Satragno52163de2022-11-23 23:03:101155 CompleteMakeCredentialRequest(
1156 blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
1157 return;
1158 }
1159
Adam Langleyf59b55602023-07-05 19:51:201160 req_state_->caller_origin = caller_origin;
1161 req_state_->relying_party_id = options->relying_party.id;
Adam Langley5f3963f12020-01-21 19:10:331162
Arthur Sonzognic686e8f2024-01-11 08:36:371163 std::optional<std::string> appid_exclude;
Martin Kreichgauer2d878b072022-05-02 18:33:111164 if (options->appid_exclude) {
Martin Kreichgauer829d7662022-05-02 22:23:261165 appid_exclude = "";
Lei Zhangbdbf57a2022-07-14 18:40:291166 auto add_id_status = security_checker_->ValidateAppIdExtension(
Martin Kreichgauer829d7662022-05-02 22:23:261167 *options->appid_exclude, caller_origin,
1168 options->remote_desktop_client_override, &appid_exclude.value());
Lei Zhangbdbf57a2022-07-14 18:40:291169 if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541170 req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
Lei Zhangbdbf57a2022-07-14 18:40:291171 CompleteMakeCredentialRequest(add_id_status);
Martin Kreichgauer2d878b072022-05-02 18:33:111172 return;
1173 }
Martin Kreichgauer829d7662022-05-02 22:23:261174 // `ValidateAppidExtension` must have set a value to use. If not, it would
1175 // be a security bug, so crashing seems appropriate here.
1176 CHECK(!appid_exclude->empty());
Martin Kreichgauer2d878b072022-05-02 18:33:111177 }
1178
Martin Kreichgauer26c2cec2022-01-26 01:00:501179 // If there is an active webAuthenticationProxy extension, let it handle the
1180 // request.
Martin Kreichgauer1f4aa592023-01-06 18:39:371181 WebAuthenticationRequestProxy* proxy =
1182 GetWebAuthnRequestProxyIfActive(caller_origin);
Martin Kreichgauer26c2cec2022-01-26 01:00:501183 if (proxy) {
Martin Kreichgauerbf776d72022-04-18 23:38:161184 if (options->remote_desktop_client_override) {
1185 // Don't allow proxying of an already proxied request.
Adem Derinele4777b62024-09-04 07:26:541186 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Martin Kreichgauerbf776d72022-04-18 23:38:161187 CompleteMakeCredentialRequest(
1188 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1189 return;
1190 }
1191 options->remote_desktop_client_override =
1192 blink::mojom::RemoteDesktopClientOverride::New(
Adam Langleyf59b55602023-07-05 19:51:201193 /*origin=*/req_state_->caller_origin,
Martin Kreichgauerbf776d72022-04-18 23:38:161194 /*same_origin_with_ancestors=*/!is_cross_origin_iframe);
Adam Langleyf59b55602023-07-05 19:51:201195 req_state_->pending_proxied_request_id = proxy->SignalCreateRequest(
Martin Kreichgauer26c2cec2022-01-26 01:00:501196 options,
Amos Lim12696e5e32022-09-16 07:37:581197 base::BindOnce(&AuthenticatorCommonImpl::OnMakeCredentialProxyResponse,
Adam Langleye8ba2cc2024-08-06 13:53:231198 weak_factory_.GetWeakPtr(), GetRequestKey()));
Martin Kreichgauer26c2cec2022-01-26 01:00:501199 return;
1200 }
1201
Martin Kreichgauer05a33fb2022-02-18 23:31:291202 // Let the embedder override the RP ID to use for the request. In practice
1203 // this rewrites the RP ID that Chrome extensions use.
Arthur Sonzognic686e8f2024-01-11 08:36:371204 std::optional<std::string> rp_id_override =
Martin Kreichgauer05a33fb2022-02-18 23:31:291205 GetWebAuthenticationDelegate()->MaybeGetRelyingPartyIdOverride(
1206 options->relying_party.id, caller_origin);
1207 if (rp_id_override) {
1208 options->relying_party.id = *rp_id_override;
Adam Langleyf59b55602023-07-05 19:51:201209 req_state_->relying_party_id = *rp_id_override;
Martin Kreichgauer05a33fb2022-02-18 23:31:291210 }
Adam Langleyf59b55602023-07-05 19:51:201211 req_state_->request_delegate->SetRelyingPartyId(req_state_->relying_party_id);
1212 req_state_->request_delegate->SetUserEntityForMakeCredentialRequest(
1213 options->user);
Martin Kreichgauer05a33fb2022-02-18 23:31:291214
Adam Langley051b97932021-01-11 19:11:231215 device::fido_filter::MaybeInitialize();
1216 switch (device::fido_filter::Evaluate(
Adam Langleyf59b55602023-07-05 19:51:201217 device::fido_filter::Operation::MAKE_CREDENTIAL,
1218 req_state_->relying_party_id,
Arthur Sonzognic686e8f2024-01-11 08:36:371219 /*device=*/std::nullopt,
1220 /*id=*/std::nullopt)) {
Adam Langley051b97932021-01-11 19:11:231221 case device::fido_filter::Action::ALLOW:
1222 break;
1223 case device::fido_filter::Action::NO_ATTESTATION:
1224 // This will be handled by the request handler.
1225 break;
1226 case device::fido_filter::Action::BLOCK:
Adem Derinele4777b62024-09-04 07:26:541227 req_state_->request_outcome = MakeCredentialOutcome::kFilterBlock;
Martin Kreichgauer393f5ef2021-03-30 23:57:571228 CompleteMakeCredentialRequest(
1229 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langley051b97932021-01-11 19:11:231230 return;
1231 }
1232
Manas Verma2ff0cc572019-03-19 23:42:561233 if (!IsFocused()) {
Adem Derinele4777b62024-09-04 07:26:541234 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571235 CompleteMakeCredentialRequest(
1236 blink::mojom::AuthenticatorStatus::NOT_FOCUSED);
Manas Verma2ff0cc572019-03-19 23:42:561237 return;
1238 }
1239
Martin Kreichgauer2e9d8072020-08-31 15:20:471240 const device::AuthenticatorSelectionCriteria
1241 authenticator_selection_criteria =
1242 options->authenticator_selection
1243 ? *options->authenticator_selection
1244 : device::AuthenticatorSelectionCriteria();
Manas Verma2ff0cc572019-03-19 23:42:561245
Adem Derinele4777b62024-09-04 07:26:541246 req_state_->request_options =
1247 device::MakeCredentialOptions(authenticator_selection_criteria);
1248 auto* make_credential_options =
Victor Hugo Vianna Silva00692722025-03-18 19:51:481249 &std::get<device::MakeCredentialOptions>(req_state_->request_options);
Adem Derinele4777b62024-09-04 07:26:541250 make_credential_options->json =
1251 base::MakeRefCounted<device::JSONRequest>(webauthn::ToValue(options));
Martin Kreichgauer3ac83382024-12-09 22:42:421252 make_credential_options->is_passkey_upgrade_request = options->is_conditional;
Martin Kreichgauer2e9d8072020-08-31 15:20:471253 const bool might_create_resident_key =
Adem Derinele4777b62024-09-04 07:26:541254 make_credential_options->resident_key !=
Martin Kreichgauer2e9d8072020-08-31 15:20:471255 device::ResidentKeyRequirement::kDiscouraged;
Martin Kreichgauerfefb3772021-04-12 23:02:481256 if (might_create_resident_key &&
1257 !GetWebAuthenticationDelegate()->SupportsResidentKeys(
1258 GetRenderFrameHost())) {
Adem Derinele4777b62024-09-04 07:26:541259 if (make_credential_options->resident_key ==
Martin Kreichgauer2e9d8072020-08-31 15:20:471260 device::ResidentKeyRequirement::kRequired) {
Adem Derinele4777b62024-09-04 07:26:541261 req_state_->request_outcome = MakeCredentialOutcome::kRkNotSupported;
Martin Kreichgauer393f5ef2021-03-30 23:57:571262 CompleteMakeCredentialRequest(
Martin Kreichgauer2e9d8072020-08-31 15:20:471263 blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
1264 return;
1265 }
1266 // Downgrade 'preferred' to 'discouraged'.
Adem Derinele4777b62024-09-04 07:26:541267 make_credential_options->resident_key =
Martin Kreichgauer2e9d8072020-08-31 15:20:471268 device::ResidentKeyRequirement::kDiscouraged;
1269 }
Adam Langleyaa789952019-05-01 19:20:271270
1271 // Reject any non-sensical credProtect extension values.
1272 if ( // Can't require the default policy (or no policy).
1273 (options->enforce_protection_policy &&
1274 (options->protection_policy ==
1275 blink::mojom::ProtectionPolicy::UNSPECIFIED ||
1276 options->protection_policy == blink::mojom::ProtectionPolicy::NONE)) ||
Adam Langley446b03c2019-05-07 21:49:161277 // For non-resident keys, NONE doesn't make sense. (UV_OR_CRED_ID_REQUIRED
1278 // does because, with CTAP 2.0, just because a resident key isn't
1279 // _required_ doesn't mean that one won't be created and an RP might want
1280 // credProtect to take effect if that happens.)
Martin Kreichgauer2e9d8072020-08-31 15:20:471281 (!might_create_resident_key &&
Adam Langley446b03c2019-05-07 21:49:161282 options->protection_policy == blink::mojom::ProtectionPolicy::NONE) ||
Adam Langleyaa789952019-05-01 19:20:271283 // UV_REQUIRED only makes sense if UV is required overall.
1284 (options->protection_policy ==
1285 blink::mojom::ProtectionPolicy::UV_REQUIRED &&
Martin Kreichgauerbece642a2021-12-07 21:02:461286 authenticator_selection_criteria.user_verification_requirement !=
Adam Langleyaa789952019-05-01 19:20:271287 device::UserVerificationRequirement::kRequired)) {
Adem Derinele4777b62024-09-04 07:26:541288 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571289 CompleteMakeCredentialRequest(
Adam Langleyaa789952019-05-01 19:20:271290 blink::mojom::AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT);
1291 return;
1292 }
1293
Arthur Sonzognic686e8f2024-01-11 08:36:371294 std::optional<device::CredProtectRequest> cred_protect_request =
Adam Langleyafd522f2023-01-27 21:12:331295 ProtectionPolicyToCredProtect(options->protection_policy,
Adem Derinele4777b62024-09-04 07:26:541296 *make_credential_options);
Martin Kreichgauer2e9d8072020-08-31 15:20:471297 if (cred_protect_request) {
Adem Derinele4777b62024-09-04 07:26:541298 make_credential_options->cred_protect_request = {
Martin Kreichgauer2e9d8072020-08-31 15:20:471299 {*cred_protect_request, options->enforce_protection_policy}};
1300 }
Adam Langleyaa789952019-05-01 19:20:271301
Martin Kreichgauer980dcfb2024-11-19 22:22:261302 auto ui_presentation = UIPresentation::kModal;
1303 if (disable_ui_) {
1304 ui_presentation = UIPresentation::kDisabled;
1305 } else if (options->is_conditional) {
1306 ui_presentation = UIPresentation::kPasskeyUpgrade;
1307 }
Martin Kreichgauer77def30f02024-11-11 20:16:581308 req_state_->request_delegate->SetUIPresentation(ui_presentation);
Manas Vermaca015f92020-01-23 23:26:351309
Martin Kreichgauere255af062022-04-18 19:40:561310 // Assemble clientDataJSON.
1311 ClientDataJsonParams client_data_json_params(
Adam Langleyf59b55602023-07-05 19:51:201312 ClientDataRequestType::kWebAuthnCreate, req_state_->caller_origin,
Ken Buchanan4fb3ef12024-08-19 20:19:321313 GetRenderFrameHost()->GetOutermostMainFrame()->GetLastCommittedOrigin(),
Martin Kreichgauere255af062022-04-18 19:40:561314 options->challenge, is_cross_origin_iframe);
Martin Kreichgauer4ce13be2022-10-28 23:20:211315 if (options->remote_desktop_client_override) {
Martin Kreichgauere255af062022-04-18 19:40:561316 client_data_json_params.origin =
1317 options->remote_desktop_client_override->origin;
1318 client_data_json_params.is_cross_origin_iframe =
1319 !options->remote_desktop_client_override->same_origin_with_ancestors;
Martin Kreichgauer0b1f5592021-07-02 22:28:251320 }
Adam Langleyf59b55602023-07-05 19:51:201321 req_state_->client_data_json =
1322 BuildClientDataJson(std::move(client_data_json_params));
Martin Kreichgauer0b1f5592021-07-02 22:28:251323
Adem Derinele4777b62024-09-04 07:26:541324 req_state_->ctap_request = device::CtapMakeCredentialRequest(
Adam Langleyf59b55602023-07-05 19:51:201325 req_state_->client_data_json, options->relying_party, options->user,
Adam Langley0616cd42019-08-08 22:31:101326 device::PublicKeyCredentialParams(options->public_key_parameters));
Adem Derinele4777b62024-09-04 07:26:541327 auto* ctap_make_credential_request =
Victor Hugo Vianna Silva00692722025-03-18 19:51:481328 &std::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request);
Martin Kreichgauer0b1f5592021-07-02 22:28:251329
Adem Derinele4777b62024-09-04 07:26:541330 ctap_make_credential_request->exclude_list = options->exclude_credentials;
Martin Kreichgauer3b3a6572020-09-24 08:49:481331 if (options->prf_enable) {
Adam Langleyf59b55602023-07-05 19:51:201332 req_state_->requested_extensions.insert(RequestExtension::kPRF);
Adem Derinele4777b62024-09-04 07:26:541333 ctap_make_credential_request->hmac_secret = true;
Adam Langley5c0c8ab72023-10-10 23:06:121334
Adem Derinel0543b5c2024-04-25 06:56:521335 if (options->prf_input) {
Arthur Sonzognic686e8f2024-01-11 08:36:371336 std::optional<device::PRFInput> prf_input =
Adam Langley5c0c8ab72023-10-10 23:06:121337 ParsePRFInputForMakeCredential(options->prf_input);
1338 if (!prf_input) {
1339 mojo::ReportBadMessage("invalid PRF inputs");
Adem Derinele4777b62024-09-04 07:26:541340 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Adam Langley5c0c8ab72023-10-10 23:06:121341 CompleteMakeCredentialRequest(
1342 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1343 return;
1344 }
Adem Derinele4777b62024-09-04 07:26:541345 ctap_make_credential_request->prf_input = std::move(*prf_input);
Adam Langley5c0c8ab72023-10-10 23:06:121346 }
Martin Kreichgauer3b3a6572020-09-24 08:49:481347 }
1348 if (options->hmac_create_secret) {
Adam Langleyf59b55602023-07-05 19:51:201349 req_state_->requested_extensions.insert(RequestExtension::kHMACSecret);
Adem Derinele4777b62024-09-04 07:26:541350 ctap_make_credential_request->hmac_secret = true;
Martin Kreichgauer3b3a6572020-09-24 08:49:481351 }
1352 if (options->cred_props) {
Adam Langleyf59b55602023-07-05 19:51:201353 req_state_->requested_extensions.insert(RequestExtension::kCredProps);
Martin Kreichgauer3b3a6572020-09-24 08:49:481354 }
Nina Satragno5ba78462020-10-02 17:25:151355 if (options->large_blob_enable != device::LargeBlobSupport::kNotRequested) {
Adam Langleyf59b55602023-07-05 19:51:201356 req_state_->requested_extensions.insert(RequestExtension::kLargeBlobEnable);
Nina Satragno5ba78462020-10-02 17:25:151357 }
Adam Langley77bbb1f2021-04-16 16:44:101358 if (options->cred_blob) {
Adam Langleyf59b55602023-07-05 19:51:201359 req_state_->requested_extensions.insert(RequestExtension::kCredBlob);
Adem Derinele4777b62024-09-04 07:26:541360 ctap_make_credential_request->cred_blob = *options->cred_blob;
Adam Langley77bbb1f2021-04-16 16:44:101361 }
Adam Langley4b0ca3e22021-11-29 20:51:181362 if (options->min_pin_length_requested) {
Adam Langleyf59b55602023-07-05 19:51:201363 req_state_->requested_extensions.insert(RequestExtension::kMinPINLength);
Adem Derinele4777b62024-09-04 07:26:541364 ctap_make_credential_request->min_pin_length_requested = true;
Adam Langley4b0ca3e22021-11-29 20:51:181365 }
Adem Derinele4777b62024-09-04 07:26:541366 make_credential_options->large_blob_support = options->large_blob_enable;
1367 ctap_make_credential_request->app_id_exclude = std::move(appid_exclude);
1368 make_credential_options->is_off_the_record_context =
Adam Langleyb0385822021-03-19 23:34:001369 GetBrowserContext()->IsOffTheRecord();
Manas Verma2ff0cc572019-03-19 23:42:561370
Adam Langleyf1c8b742020-07-22 19:36:061371 // Compute the effective attestation conveyance preference.
1372 device::AttestationConveyancePreference attestation = options->attestation;
1373 // Enterprise attestation should not have been approved by this point.
Nina Satragnoa8669812023-01-30 22:47:411374 DCHECK_NE(
1375 attestation,
1376 device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser);
Adam Langleyf1c8b742020-07-22 19:36:061377 if (attestation == device::AttestationConveyancePreference::
1378 kEnterpriseIfRPListedOnAuthenticator &&
Martin Kreichgauerfefb3772021-04-12 23:02:481379 GetWebAuthenticationDelegate()->ShouldPermitIndividualAttestation(
Adam Langleyf59b55602023-07-05 19:51:201380 GetBrowserContext(), caller_origin, req_state_->relying_party_id)) {
Adam Langleyf1c8b742020-07-22 19:36:061381 attestation =
1382 device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser;
Manas Verma2ff0cc572019-03-19 23:42:561383 }
Adem Derinele4777b62024-09-04 07:26:541384 ctap_make_credential_request->attestation_preference = attestation;
Martin Kreichgauer90c3da42024-05-09 18:34:261385 GetWebAuthenticationDelegate()->BrowserProvidedPasskeysAvailable(
Ken Buchanan90fe29552024-04-26 21:15:481386 GetBrowserContext(),
Martin Kreichgauer90c3da42024-05-09 18:34:261387 base::BindOnce(
1388 &AuthenticatorCommonImpl::
1389 ContinueMakeCredentialAfterBrowserPasskeysAvailabilityCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231390 weak_factory_.GetWeakPtr(), GetRequestKey()));
Ken Buchanan90fe29552024-04-26 21:15:481391}
1392
1393void AuthenticatorCommonImpl::
Martin Kreichgauer90c3da42024-05-09 18:34:261394 ContinueMakeCredentialAfterBrowserPasskeysAvailabilityCheck(
Adam Langleye8ba2cc2024-08-06 13:53:231395 RequestKey request_key,
Martin Kreichgauer90c3da42024-05-09 18:34:261396 bool available) {
Adam Langleye8ba2cc2024-08-06 13:53:231397 if (!CheckRequestKey(request_key)) {
1398 return;
1399 }
Martin Kreichgauer90c3da42024-05-09 18:34:261400 browser_passkeys_available_ = available;
Ken Buchanan90fe29552024-04-26 21:15:481401 GetWebAuthenticationDelegate()
1402 ->IsUserVerifyingPlatformAuthenticatorAvailableOverride(
1403 GetRenderFrameHost(),
1404 base::BindOnce(&AuthenticatorCommonImpl::
1405 ContinueMakeCredentialAfterIsUvpaaOverrideCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231406 weak_factory_.GetWeakPtr(), GetRequestKey()));
Ken Buchanan90fe29552024-04-26 21:15:481407}
1408
1409void AuthenticatorCommonImpl::ContinueMakeCredentialAfterIsUvpaaOverrideCheck(
Adam Langleye8ba2cc2024-08-06 13:53:231410 RequestKey request_key,
Ken Buchanan90fe29552024-04-26 21:15:481411 std::optional<bool> is_uvpaa_override) {
Adam Langleye8ba2cc2024-08-06 13:53:231412 if (!CheckRequestKey(request_key)) {
1413 return;
1414 }
Ken Buchanan90fe29552024-04-26 21:15:481415 is_uvpaa_override_ = is_uvpaa_override;
Nina Satragno70589ab2019-10-02 16:40:041416 StartMakeCredentialRequest(/*allow_skipping_pin_touch=*/true);
Manas Verma2ff0cc572019-03-19 23:42:561417}
1418
Adem Derinel72e11db2025-02-11 15:58:001419void AuthenticatorCommonImpl::GetCredential(
Manas Verma9ba13692019-03-21 21:01:001420 url::Origin caller_origin,
Manas Verma2ff0cc572019-03-19 23:42:561421 blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
Martin Kreichgauere255af062022-04-18 19:40:561422 blink::mojom::PaymentOptionsPtr payment_options,
Adem Derinel72e11db2025-02-11 15:58:001423 GetCredentialCallback callback) {
Adem Derineldb926582025-02-13 09:04:291424 if (options->mediation == Mediation::CONDITIONAL) {
Adem Derineldc2d52f2024-09-19 08:06:561425 base::RecordAction(
1426 base::UserMetricsAction("WebAuthn.GetAssertion.Conditional.Start"));
Adem Derineldb926582025-02-13 09:04:291427 } else if (options->mediation == Mediation::MODAL) {
Adem Derineldc2d52f2024-09-19 08:06:561428 base::RecordAction(base::UserMetricsAction("WebAuthn.GetAssertion.Start"));
Adem Derinel0fa39b2e2025-06-16 13:08:281429 } else if (options->mediation == Mediation::IMMEDIATE) {
1430 base::RecordAction(base::UserMetricsAction("WebAuthn.GetCredential.Start"));
Adem Derineldc2d52f2024-09-19 08:06:561431 }
1432 callback = base::BindOnce(
Adem Derinel72e11db2025-02-11 15:58:001433 &AuthenticatorCommonImpl::GetMetricsWrappedGetCredentialCallback,
Adem Derineldc2d52f2024-09-19 08:06:561434 weak_factory_.GetWeakPtr(), std::move(callback));
1435
Adam Langleyf59b55602023-07-05 19:51:201436 if (req_state_) {
Adem Derinel72e11db2025-02-11 15:58:001437 GetCallbackForAssertion(std::move(callback))
1438 .Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr,
1439 nullptr);
Martin Kreichgauer4ce13be2022-10-28 23:20:211440 return;
Manas Verma2ff0cc572019-03-19 23:42:561441 }
Ken Buchananbba66b42024-11-29 16:43:151442
Adam Langleyf59b55602023-07-05 19:51:201443 req_state_ = std::make_unique<RequestState>();
Adam Langleye8ba2cc2024-08-06 13:53:231444 req_state_->request_key = RequestKey(next_request_key_);
Martin Kreichgauer8c97189a2022-01-10 20:31:431445
Adem Derinele4777b62024-09-04 07:26:541446 req_state_->response_callback = std::move(callback);
Ken Buchanan23dce912024-07-11 16:41:271447 if (!payment_options.is_null()) {
Ken Buchanan1ea549c12024-10-10 21:31:051448 req_state_->mode = AuthenticationRequestMode::kPayment;
Adem Derineldb926582025-02-13 09:04:291449 } else if (options->mediation == Mediation::CONDITIONAL) {
Ken Buchanan1ea549c12024-10-10 21:31:051450 req_state_->mode = AuthenticationRequestMode::kConditional;
Ken Buchanan242a4622025-07-18 05:54:231451 } else if (options->mediation == Mediation::IMMEDIATE) {
1452 req_state_->mode = AuthenticationRequestMode::kImmediate;
1453 BeginImmediateRequestTimeout();
Ken Buchanan23dce912024-07-11 16:41:271454 } else {
Ken Buchanan1ea549c12024-10-10 21:31:051455 req_state_->mode = AuthenticationRequestMode::kModalWebAuthn;
Ken Buchanan23dce912024-07-11 16:41:271456 }
Adam Langleya65976912023-12-14 22:13:301457 req_state_->hints.insert(options->hints.begin(), options->hints.end());
Manas Verma2ff0cc572019-03-19 23:42:561458
Adem Derineldb926582025-02-13 09:04:291459 if (options->mediation != Mediation::CONDITIONAL) {
Martin Kreichgauer1beaff02022-02-02 18:58:421460 BeginRequestTimeout(options->timeout);
1461 }
Martin Kreichgauer1beaff02022-02-02 18:58:421462
Ken Buchananbe8629f2025-01-11 03:37:161463 if (options->challenge.has_value() == options->challenge_url.has_value()) {
1464 mojo::ReportBadMessage(
1465 "Exactly one of challenge and challenge_url must be provided");
1466 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
1467 CompleteGetAssertionRequest(
1468 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1469 return;
1470 }
1471
Adem Derineldb926582025-02-13 09:04:291472 if (options->mediation == Mediation::IMMEDIATE &&
Adem Derinel2154d4b12025-02-04 12:29:551473 !options->allow_credentials.empty()) {
1474 mojo::ReportBadMessage(
1475 "Immediate mediation cannot be used with an allow credential list");
1476 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
1477 CompleteGetAssertionRequest(
1478 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1479 return;
1480 }
Adem Derineldb926582025-02-13 09:04:291481 req_state_->mediation_ = options->mediation;
Adem Derinel2154d4b12025-02-04 12:29:551482
Ken Buchananbe8629f2025-01-11 03:37:161483 if (options->challenge_url.has_value() &&
1484 !options->challenge_url->is_valid()) {
1485 mojo::ReportBadMessage("challenge_url must contain a valid URL");
1486 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
1487 CompleteGetAssertionRequest(
1488 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1489 return;
1490 }
1491
Liquan (Max) Gu082fa472021-08-13 02:48:271492 WebAuthRequestSecurityChecker::RequestType request_type =
Martin Kreichgauere255af062022-04-18 19:40:561493 payment_options.is_null()
Liquan (Max) Gu082fa472021-08-13 02:48:271494 ? WebAuthRequestSecurityChecker::RequestType::kGetAssertion
1495 : WebAuthRequestSecurityChecker::RequestType::
1496 kGetPaymentCredentialAssertion;
Martin Kreichgauere255af062022-04-18 19:40:561497 if (!payment_options.is_null() && options->allow_credentials.empty()) {
Ken Buchananfbb74b52024-12-06 16:28:081498 mojo::ReportBadMessage(
1499 "PaymentOptions with empty allow_credentials is invalid");
Adem Derinele4777b62024-09-04 07:26:541500 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Liquan (Max) Gu082fa472021-08-13 02:48:271501 CompleteGetAssertionRequest(
1502 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Ken Buchananfbb74b52024-12-06 16:28:081503 return;
Liquan (Max) Gu082fa472021-08-13 02:48:271504 }
Martin Kreichgauerbf776d72022-04-18 23:38:161505 bool is_cross_origin_iframe = false;
Ken Buchanan3889e2b2020-02-11 04:26:531506 blink::mojom::AuthenticatorStatus status =
Liquan (Max) Gu082fa472021-08-13 02:48:271507 security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
Martin Kreichgauere255af062022-04-18 19:40:561508 &is_cross_origin_iframe);
Ken Buchanan3889e2b2020-02-11 04:26:531509 if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541510 req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
Martin Kreichgauer393f5ef2021-03-30 23:57:571511 CompleteGetAssertionRequest(status);
Ken Buchanancb3122f12019-11-21 14:10:461512 return;
1513 }
1514
Martin Kreichgauer8d55b452021-10-04 20:51:111515 if (!security_checker_->DeduplicateCredentialDescriptorListAndValidateLength(
1516 &options->allow_credentials)) {
1517 mojo::ReportBadMessage("invalid allow_credentials length");
Adem Derinele4777b62024-09-04 07:26:541518 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Martin Kreichgauer8d55b452021-10-04 20:51:111519 CompleteGetAssertionRequest(
1520 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1521 return;
1522 }
1523
Adam Langley6d16761b2023-11-01 21:39:511524 const std::string relying_party_id = options->relying_party_id;
1525 const blink::mojom::RemoteDesktopClientOverridePtr&
1526 remote_desktop_client_override =
1527 options->extensions->remote_desktop_client_override;
Andrii Natiahlyia700f91f2025-03-21 18:37:391528 std::optional<url::Origin> remote_desktop_override_origin;
1529 if (remote_desktop_client_override) {
1530 // SECURITY: RemoteDesktopClientOverride comes from the renderer process and
1531 // is untrusted. This `remote_desktop_override_origin` is only used after
1532 // ValidateDomainAndRelyingPartyID verifies that the `caller_origin` is
1533 // explicitly allowlisted via enterprise policy in
1534 // WebAuthenticationDelegateBase::OriginMayUseRemoteDesktopClientOverride().
1535 remote_desktop_override_origin = remote_desktop_client_override->origin;
1536 }
Adam Langley6d16761b2023-11-01 21:39:511537 std::unique_ptr<WebAuthRequestSecurityChecker::RemoteValidation>
1538 remote_validation = security_checker_->ValidateDomainAndRelyingPartyID(
1539 caller_origin, relying_party_id, request_type,
Andrii Natiahlyia700f91f2025-03-21 18:37:391540 remote_desktop_override_origin,
Adam Langley6d16761b2023-11-01 21:39:511541 base::BindOnce(
1542 &AuthenticatorCommonImpl::ContinueGetAssertionAfterRpIdCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231543 weak_factory_.GetWeakPtr(), GetRequestKey(), caller_origin,
1544 std::move(options), std::move(payment_options),
1545 is_cross_origin_iframe));
Adam Langley6d16761b2023-11-01 21:39:511546
1547 // If `remote_validation` is nullptr then the request may already have
1548 // completed.
1549 if (remote_validation) {
1550 req_state_->remote_rp_id_validation = std::move(remote_validation);
1551 }
1552}
1553
1554void AuthenticatorCommonImpl::ContinueGetAssertionAfterRpIdCheck(
Adam Langleye8ba2cc2024-08-06 13:53:231555 RequestKey request_key,
Adam Langley6d16761b2023-11-01 21:39:511556 url::Origin caller_origin,
1557 blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
1558 blink::mojom::PaymentOptionsPtr payment_options,
1559 bool is_cross_origin_iframe,
1560 blink::mojom::AuthenticatorStatus rp_id_validation_result) {
Adam Langleye8ba2cc2024-08-06 13:53:231561 if (!CheckRequestKey(request_key)) {
1562 return;
1563 }
Adam Langley6d16761b2023-11-01 21:39:511564 req_state_->remote_rp_id_validation.reset();
1565
1566 if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541567 req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
Adam Langley6d16761b2023-11-01 21:39:511568 CompleteGetAssertionRequest(rp_id_validation_result);
Martin Kreichgauer05a33fb2022-02-18 23:31:291569 return;
1570 }
1571
Adam Langleyf59b55602023-07-05 19:51:201572 req_state_->request_delegate = MaybeCreateRequestDelegate();
1573 if (!req_state_->request_delegate) {
Adem Derinele4777b62024-09-04 07:26:541574 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571575 CompleteGetAssertionRequest(
1576 blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
Manas Verma2ff0cc572019-03-19 23:42:561577 return;
1578 }
Adam Langleyf59b55602023-07-05 19:51:201579 if (!req_state_->request_delegate->IsVirtualEnvironmentEnabled() &&
Nina Satragno8d67dec32023-04-18 22:10:441580 !disable_tls_check_ &&
Ken Buchanan6e9929372023-10-31 14:05:431581 !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
Nina Satragno52163de2022-11-23 23:03:101582 GetRenderFrameHost(), caller_origin)) {
Adem Derinele4777b62024-09-04 07:26:541583 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Nina Satragno52163de2022-11-23 23:03:101584 CompleteGetAssertionRequest(
1585 blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
1586 return;
1587 }
Manas Verma2ff0cc572019-03-19 23:42:561588
Adam Langleyf59b55602023-07-05 19:51:201589 req_state_->caller_origin = caller_origin;
1590 req_state_->relying_party_id = options->relying_party_id;
Martin Kreichgauer2d878b072022-05-02 18:33:111591
Slobodan Pejicc8ce88b2023-06-19 15:41:121592 if (options->extensions->appid) {
Adam Langleyf59b55602023-07-05 19:51:201593 req_state_->requested_extensions.insert(RequestExtension::kAppID);
Martin Kreichgauer829d7662022-05-02 22:23:261594 std::string app_id;
Lei Zhangbdbf57a2022-07-14 18:40:291595 auto add_id_status = security_checker_->ValidateAppIdExtension(
Slobodan Pejicc8ce88b2023-06-19 15:41:121596 *options->extensions->appid, caller_origin,
1597 options->extensions->remote_desktop_client_override, &app_id);
Lei Zhangbdbf57a2022-07-14 18:40:291598 if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
Adem Derinele4777b62024-09-04 07:26:541599 req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
Lei Zhangbdbf57a2022-07-14 18:40:291600 CompleteGetAssertionRequest(add_id_status);
Martin Kreichgauer2d878b072022-05-02 18:33:111601 return;
1602 }
Martin Kreichgauer829d7662022-05-02 22:23:261603 // `ValidateAppidExtension` must have set a value to use. If not, it would
1604 // be a security bug, so crashing seems appropriate here.
1605 CHECK(!app_id.empty());
Adam Langleyf59b55602023-07-05 19:51:201606 req_state_->app_id = app_id;
Martin Kreichgauer2d878b072022-05-02 18:33:111607 }
1608
Martin Kreichgauer1f4aa592023-01-06 18:39:371609 WebAuthenticationRequestProxy* proxy =
1610 GetWebAuthnRequestProxyIfActive(caller_origin);
Martin Kreichgauer1beaff02022-02-02 18:58:421611 if (proxy) {
Adem Derineldb926582025-02-13 09:04:291612 if (options->mediation == Mediation::CONDITIONAL ||
Slobodan Pejicc8ce88b2023-06-19 15:41:121613 (options->extensions->remote_desktop_client_override)) {
Nina Satragno867bca52022-10-13 15:02:021614 // Don't allow proxying of an already proxied or conditional request.
Adem Derinele4777b62024-09-04 07:26:541615 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Nina Satragno867bca52022-10-13 15:02:021616 CompleteGetAssertionRequest(
Martin Kreichgauerbf776d72022-04-18 23:38:161617 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1618 return;
1619 }
Slobodan Pejicc8ce88b2023-06-19 15:41:121620 options->extensions->remote_desktop_client_override =
Martin Kreichgauerbf776d72022-04-18 23:38:161621 blink::mojom::RemoteDesktopClientOverride::New(
Adam Langleyf59b55602023-07-05 19:51:201622 /*origin=*/req_state_->caller_origin,
Martin Kreichgauerbf776d72022-04-18 23:38:161623 /*same_origin_with_ancestors=*/!is_cross_origin_iframe);
Adam Langleyf59b55602023-07-05 19:51:201624 req_state_->pending_proxied_request_id = proxy->SignalGetRequest(
Martin Kreichgauer1beaff02022-02-02 18:58:421625 options,
Amos Lim12696e5e32022-09-16 07:37:581626 base::BindOnce(&AuthenticatorCommonImpl::OnGetAssertionProxyResponse,
Adam Langleye8ba2cc2024-08-06 13:53:231627 weak_factory_.GetWeakPtr(), GetRequestKey()));
Martin Kreichgauer1beaff02022-02-02 18:58:421628 return;
1629 }
1630
Martin Kreichgauer05a33fb2022-02-18 23:31:291631 // Let the embedder override the RP ID to use for the request. In practice
1632 // this rewrites the RP ID that Chrome extension use.
Arthur Sonzognic686e8f2024-01-11 08:36:371633 std::optional<std::string> rp_id_override =
Martin Kreichgauer05a33fb2022-02-18 23:31:291634 GetWebAuthenticationDelegate()->MaybeGetRelyingPartyIdOverride(
1635 options->relying_party_id, caller_origin);
1636 if (rp_id_override) {
1637 options->relying_party_id = *rp_id_override;
Adam Langleyf59b55602023-07-05 19:51:201638 req_state_->relying_party_id = *rp_id_override;
Martin Kreichgauer05a33fb2022-02-18 23:31:291639 }
Adam Langleyf59b55602023-07-05 19:51:201640 req_state_->request_delegate->SetRelyingPartyId(req_state_->relying_party_id);
Martin Kreichgauer05a33fb2022-02-18 23:31:291641
Adam Langley051b97932021-01-11 19:11:231642 device::fido_filter::MaybeInitialize();
1643 if (device::fido_filter::Evaluate(
Adam Langleyf59b55602023-07-05 19:51:201644 device::fido_filter::Operation::GET_ASSERTION,
1645 req_state_->relying_party_id,
Arthur Sonzognic686e8f2024-01-11 08:36:371646 /*device=*/std::nullopt,
1647 /*id=*/std::nullopt) == device::fido_filter::Action::BLOCK) {
Adem Derinele4777b62024-09-04 07:26:541648 req_state_->request_outcome = GetAssertionOutcome::kFilterBlock;
Martin Kreichgauer393f5ef2021-03-30 23:57:571649 CompleteGetAssertionRequest(
Adam Langley051b97932021-01-11 19:11:231650 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1651 return;
1652 }
1653
Martin Kreichgauer77def30f02024-11-11 20:16:581654 auto ui_presentation = UIPresentation::kModal;
Martin Kreichgauer4ce13be2022-10-28 23:20:211655 if (disable_ui_) {
Martin Kreichgauer77def30f02024-11-11 20:16:581656 ui_presentation = UIPresentation::kDisabled;
Adem Derineldb926582025-02-13 09:04:291657 } else if (options->mediation == Mediation::CONDITIONAL) {
Martin Kreichgauer77def30f02024-11-11 20:16:581658 ui_presentation = UIPresentation::kAutofill;
Adem Derineldb926582025-02-13 09:04:291659 } else if (options->mediation == Mediation::IMMEDIATE) {
Adem Derinel2154d4b12025-02-04 12:29:551660 ui_presentation = UIPresentation::kModalImmediate;
Nina Satragnob93b8422021-02-04 21:50:441661 }
Martin Kreichgauer77def30f02024-11-11 20:16:581662 req_state_->request_delegate->SetUIPresentation(ui_presentation);
Nina Satragnob93b8422021-02-04 21:50:441663
Ken Buchananbe8629f2025-01-11 03:37:161664 // Assemble clientDataJSON.
1665 ClientDataJsonParams client_data_json_params(
1666 ClientDataRequestType::kWebAuthnGet, caller_origin,
1667 GetRenderFrameHost()->GetOutermostMainFrame()->GetLastCommittedOrigin(),
1668 options->challenge, is_cross_origin_iframe);
1669 if (payment_options) {
1670 client_data_json_params.type = ClientDataRequestType::kPaymentGet;
1671 client_data_json_params.payment_options = std::move(payment_options);
1672 client_data_json_params.payment_rp = req_state_->relying_party_id;
1673 } else if (options->extensions->remote_desktop_client_override) {
1674 client_data_json_params.origin =
1675 options->extensions->remote_desktop_client_override->origin;
1676 client_data_json_params.is_cross_origin_iframe =
1677 !options->extensions->remote_desktop_client_override
1678 ->same_origin_with_ancestors;
1679 }
1680
1681 if (options->challenge.has_value()) {
1682 req_state_->client_data_json =
1683 BuildClientDataJson(std::move(client_data_json_params));
1684 } else {
1685 req_state_->request_delegate->ProvideChallengeUrl(
1686 *options->challenge_url,
1687 base::BindOnce(&AuthenticatorCommonImpl::UpdateChallengeFromUrl,
1688 weak_factory_.GetWeakPtr(),
1689 std::move(client_data_json_params)));
1690 }
1691
Adem Derineldb926582025-02-13 09:04:291692 if (options->mediation == Mediation::CONDITIONAL ||
1693 options->mediation == Mediation::IMMEDIATE) {
Adem Derinel6d4cc7f2025-02-11 10:07:081694 req_state_->request_delegate->SetCredentialTypes(
Ken Buchanan278239e2024-09-17 17:39:091695 options->requested_credential_type_flags);
1696 }
Manas Vermaca015f92020-01-23 23:26:351697
Nina Satragnof09180c2023-07-20 16:57:321698 req_state_->request_delegate->SetCredentialIdFilter(
1699 options->allow_credentials);
Adem Derineldb926582025-02-13 09:04:291700 if (options->mediation == Mediation::CONDITIONAL) {
Ken Buchanan65125f82022-11-08 22:15:151701 // Conditional mediation requests can only be fulfilled by discoverable
1702 // credentials. The provided allowCredentials list is stripped and will be
1703 // used to filter returned passkeys
Ken Buchanan65125f82022-11-08 22:15:151704 options->allow_credentials =
1705 std::vector<device::PublicKeyCredentialDescriptor>();
1706 }
1707
Adam Langley10a207e692019-08-22 01:38:231708 if (options->allow_credentials.empty()) {
Martin Kreichgauerfefb3772021-04-12 23:02:481709 if (!GetWebAuthenticationDelegate()->SupportsResidentKeys(
1710 GetRenderFrameHost())) {
Adem Derinele4777b62024-09-04 07:26:541711 req_state_->request_outcome = GetAssertionOutcome::kRkNotSupported;
Martin Kreichgauer393f5ef2021-03-30 23:57:571712 CompleteGetAssertionRequest(
Adam Langley10a207e692019-08-22 01:38:231713 blink::mojom::AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED);
1714 return;
1715 }
Adam Langleyf59b55602023-07-05 19:51:201716 req_state_->discoverable_credential_request = true;
Manas Verma2ff0cc572019-03-19 23:42:561717 }
1718
Slobodan Pejicc8ce88b2023-06-19 15:41:121719 if (options->extensions->large_blob_read &&
1720 options->extensions->large_blob_write) {
Adem Derinele4777b62024-09-04 07:26:541721 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571722 CompleteGetAssertionRequest(
Nina Satragno9de1788d2020-10-08 21:00:511723 blink::mojom::AuthenticatorStatus::CANNOT_READ_AND_WRITE_LARGE_BLOB);
1724 return;
1725 }
1726
Slobodan Pejicc8ce88b2023-06-19 15:41:121727 if (options->extensions->large_blob_read) {
Adam Langleyf59b55602023-07-05 19:51:201728 req_state_->requested_extensions.insert(RequestExtension::kLargeBlobRead);
Slobodan Pejicc8ce88b2023-06-19 15:41:121729 } else if (options->extensions->large_blob_write) {
Nina Satragno9de1788d2020-10-08 21:00:511730 if (options->allow_credentials.size() != 1) {
Adem Derinele4777b62024-09-04 07:26:541731 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Martin Kreichgauer393f5ef2021-03-30 23:57:571732 CompleteGetAssertionRequest(blink::mojom::AuthenticatorStatus::
1733 INVALID_ALLOW_CREDENTIALS_FOR_LARGE_BLOB);
Nina Satragno9de1788d2020-10-08 21:00:511734 return;
1735 }
Adam Langleyf59b55602023-07-05 19:51:201736 req_state_->requested_extensions.insert(RequestExtension::kLargeBlobWrite);
Nina Satragno2bf834342020-10-06 22:05:501737 }
1738
Adem Derinele4777b62024-09-04 07:26:541739 req_state_->ctap_request = CreateCtapGetAssertionRequest(
Adam Langleyf59b55602023-07-05 19:51:201740 req_state_->client_data_json, options, req_state_->app_id);
Adem Derinele4777b62024-09-04 07:26:541741 auto* ctap_get_assertion_request =
Victor Hugo Vianna Silva00692722025-03-18 19:51:481742 &std::get<device::CtapGetAssertionRequest>(req_state_->ctap_request);
Adem Derinele4777b62024-09-04 07:26:541743
1744 req_state_->request_options.emplace<device::CtapGetAssertionOptions>();
1745 auto* ctap_get_assertion_options =
Victor Hugo Vianna Silva00692722025-03-18 19:51:481746 &std::get<device::CtapGetAssertionOptions>(req_state_->request_options);
Adem Derinele4777b62024-09-04 07:26:541747 ctap_get_assertion_options->is_off_the_record_context =
Nina Satragno8824f9a2023-02-02 15:54:461748 GetBrowserContext()->IsOffTheRecord();
Adem Derinele4777b62024-09-04 07:26:541749 ctap_get_assertion_options->json =
Nina Satragno7024e6b2023-11-20 22:52:241750 base::MakeRefCounted<device::JSONRequest>(webauthn::ToValue(options));
Adam Langleyc296f392020-07-16 03:55:241751
Slobodan Pejicc8ce88b2023-06-19 15:41:121752 if (options->extensions->prf) {
Adam Langleyf59b55602023-07-05 19:51:201753 req_state_->requested_extensions.insert(RequestExtension::kPRF);
Adam Langleydcd3e54b2023-01-17 20:44:241754
Arthur Sonzognic686e8f2024-01-11 08:36:371755 std::optional<std::vector<device::PRFInput>> prf_inputs =
Adam Langley5c0c8ab72023-10-10 23:06:121756 ParsePRFInputsForGetAssertion(options->extensions->prf_inputs);
Adam Langleyc296f392020-07-16 03:55:241757
Adam Langley1d1adaf2023-07-12 22:55:261758 // This should never happen for inputs from the renderer, which should sort
Adam Langley337cf3d82024-10-24 14:17:171759 // the values itself.
1760 if (!prf_inputs) {
Adam Langley1d1adaf2023-07-12 22:55:261761 mojo::ReportBadMessage("invalid PRF inputs");
Adem Derinele4777b62024-09-04 07:26:541762 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Adam Langley1d1adaf2023-07-12 22:55:261763 CompleteGetAssertionRequest(
1764 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
1765 return;
Adam Langleyc296f392020-07-16 03:55:241766 }
Adem Derinele4777b62024-09-04 07:26:541767 ctap_get_assertion_options->prf_inputs = std::move(*prf_inputs);
Adam Langleyc296f392020-07-16 03:55:241768 }
1769
Slobodan Pejicc8ce88b2023-06-19 15:41:121770 if (options->extensions->get_cred_blob) {
Adam Langleyf59b55602023-07-05 19:51:201771 req_state_->requested_extensions.insert(RequestExtension::kGetCredBlob);
Adem Derinele4777b62024-09-04 07:26:541772 ctap_get_assertion_request->get_cred_blob = true;
Adam Langley70a24152022-08-30 02:01:041773 }
1774
Adem Derinele4777b62024-09-04 07:26:541775 ctap_get_assertion_options->large_blob_read =
Slobodan Pejicc8ce88b2023-06-19 15:41:121776 options->extensions->large_blob_read;
Adem Derinele4777b62024-09-04 07:26:541777 ctap_get_assertion_options->large_blob_write =
Slobodan Pejicc8ce88b2023-06-19 15:41:121778 options->extensions->large_blob_write;
Martin Kreichgauer90c3da42024-05-09 18:34:261779 GetWebAuthenticationDelegate()->BrowserProvidedPasskeysAvailable(
Ken Buchanan90fe29552024-04-26 21:15:481780 GetBrowserContext(),
Martin Kreichgauer90c3da42024-05-09 18:34:261781 base::BindOnce(
1782 &AuthenticatorCommonImpl::
1783 ContinueGetAssertionAfterBrowserPasskeysAvailabilityCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231784 weak_factory_.GetWeakPtr(), GetRequestKey()));
Ken Buchanan90fe29552024-04-26 21:15:481785}
Adam Langley77bbb1f2021-04-16 16:44:101786
Martin Kreichgauer90c3da42024-05-09 18:34:261787void AuthenticatorCommonImpl::
Adam Langleye8ba2cc2024-08-06 13:53:231788 ContinueGetAssertionAfterBrowserPasskeysAvailabilityCheck(
1789 RequestKey request_key,
1790 bool available) {
1791 if (!CheckRequestKey(request_key)) {
1792 return;
1793 }
Martin Kreichgauer90c3da42024-05-09 18:34:261794 browser_passkeys_available_ = available;
Ken Buchanan90fe29552024-04-26 21:15:481795 GetWebAuthenticationDelegate()
1796 ->IsUserVerifyingPlatformAuthenticatorAvailableOverride(
1797 GetRenderFrameHost(),
1798 base::BindOnce(&AuthenticatorCommonImpl::
1799 ContinueGetAssertionAfterIsUvpaaOverrideCheck,
Adam Langleye8ba2cc2024-08-06 13:53:231800 weak_factory_.GetWeakPtr(), GetRequestKey()));
Ken Buchanan90fe29552024-04-26 21:15:481801}
1802
1803void AuthenticatorCommonImpl::ContinueGetAssertionAfterIsUvpaaOverrideCheck(
Adam Langleye8ba2cc2024-08-06 13:53:231804 RequestKey request_key,
Ken Buchanan90fe29552024-04-26 21:15:481805 std::optional<bool> is_uvpaa_override) {
Adam Langleye8ba2cc2024-08-06 13:53:231806 if (!CheckRequestKey(request_key)) {
1807 return;
1808 }
Ken Buchanan90fe29552024-04-26 21:15:481809 is_uvpaa_override_ = is_uvpaa_override;
Nina Satragno70589ab2019-10-02 16:40:041810 StartGetAssertionRequest(/*allow_skipping_pin_touch=*/true);
Manas Verma2ff0cc572019-03-19 23:42:561811}
1812
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421813void AuthenticatorCommonImpl::GetClientCapabilities(
1814 url::Origin caller_origin,
1815 blink::mojom::Authenticator::GetClientCapabilitiesCallback callback) {
1816 // IsPPAA is computed based on the results of IsUVPAA and HybridTransport.
1817 auto completion_callback =
1818 base::BindOnce(&InsertIsPPAACapability).Then(std::move(callback));
1819
Adem Derineld5640322025-04-24 09:14:061820 bool immediate_get_enabled =
1821 base::FeatureList::IsEnabled(device::kWebAuthnImmediateGet);
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421822 // IMPORTANT: If you add or remove a capability check below (and expect to
1823 // collect the results of the check with the `BarrierCallback`), update this
1824 // constant to match the number of `barrier_callback.Run()` calls. Otherwise,
1825 // the `GetClientCapabilities()` call will crash or timeout.
Adem Derineld5640322025-04-24 09:14:061826 const size_t kNumberOfComputedCapabilities = immediate_get_enabled ? 6 : 5;
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421827 auto barrier_callback =
1828 base::BarrierCallback<blink::mojom::WebAuthnClientCapabilityPtr>(
1829 kNumberOfComputedCapabilities, std::move(completion_callback));
1830
Nina Satragnocc810702024-10-11 20:39:111831 barrier_callback.Run(
1832 MakeCapability(client_capabilities::kRelatedOrigins, true));
Martin Kreichgauera57d2f12025-03-12 16:47:451833 barrier_callback.Run(MakeCapability(
1834 client_capabilities::kConditionalCreate,
1835 base::FeatureList::IsEnabled(device::kWebAuthnPasskeyUpgrade)));
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421836
Andrii Natiahlyie480a492024-09-18 15:20:371837 IsHybridTransportSupported(
1838 base::BindOnce(&MakeCapability, client_capabilities::kHybridTransport)
1839 .Then(barrier_callback));
1840
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421841 IsUvpaaAvailableInternal(
1842 caller_origin,
1843 base::BindOnce(&MakeCapability,
1844 client_capabilities::kUserVerifyingPlatformAuthenticator)
1845 .Then(barrier_callback),
1846 /*is_get_client_capabilities_call=*/true);
1847 IsConditionalMediationAvailable(
1848 caller_origin,
1849 base::BindOnce(&MakeCapability, client_capabilities::kConditionalGet)
1850 .Then(barrier_callback));
Adem Derineld5640322025-04-24 09:14:061851 if (immediate_get_enabled) {
1852 barrier_callback.Run(
1853 MakeCapability(client_capabilities::kImmediateGet, true));
1854 }
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421855}
1856
Andrii Natiahlyie480a492024-09-18 15:20:371857void AuthenticatorCommonImpl::IsHybridTransportSupported(
1858 base::OnceCallback<void(bool)> callback) {
Andrii Natiahlyie480a492024-09-18 15:20:371859 if (!device::BluetoothAdapterFactory::Get()->IsLowEnergySupported()) {
1860 std::move(callback).Run(false);
1861 return;
1862 }
1863
1864 device::BluetoothAdapterFactory::Get()->GetAdapter(
1865 base::BindOnce([](scoped_refptr<device::BluetoothAdapter> adapter) {
1866 return adapter && adapter->IsPresent();
1867 }).Then(std::move(callback)));
1868}
1869
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421870void AuthenticatorCommonImpl::IsUvpaaAvailableInternal(
Martin Kreichgauer1f4aa592023-01-06 18:39:371871 url::Origin caller_origin,
Manas Verma9ba13692019-03-21 21:01:001872 blink::mojom::Authenticator::
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421873 IsUserVerifyingPlatformAuthenticatorAvailableCallback callback,
1874 bool is_get_client_capabilities_call) {
Martin Kreichgauer1f4aa592023-01-06 18:39:371875 WebAuthenticationRequestProxy* proxy =
1876 GetWebAuthnRequestProxyIfActive(caller_origin);
Martin Kreichgauer165ff722021-08-26 01:33:521877 if (proxy) {
Martin Kreichgauer1beaff02022-02-02 18:58:421878 // Note that IsUvpaa requests can interleave with MakeCredential or
1879 // GetAssertion, and cannot be cancelled. Thus, we do not set
Adam Langleyf59b55602023-07-05 19:51:201880 // `req_state_->pending_proxied_request_id` here.
Martin Kreichgauer165ff722021-08-26 01:33:521881 proxy->SignalIsUvpaaRequest(std::move(callback));
1882 return;
1883 }
1884
Nina Satragno9c1c826d2023-01-30 21:41:511885 // Check for a delegate override. Chrome overrides IsUVPAA() in Guest mode.
Ken Buchanan90fe29552024-04-26 21:15:481886 GetWebAuthenticationDelegate()
1887 ->IsUserVerifyingPlatformAuthenticatorAvailableOverride(
1888 GetRenderFrameHost(),
1889 base::BindOnce(
1890 &AuthenticatorCommonImpl::ContinueIsUvpaaAfterOverrideCheck,
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421891 weak_factory_.GetWeakPtr(), std::move(callback),
1892 is_get_client_capabilities_call));
1893}
1894
1895void AuthenticatorCommonImpl::IsUserVerifyingPlatformAuthenticatorAvailable(
1896 url::Origin caller_origin,
1897 blink::mojom::Authenticator::
1898 IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
1899 IsUvpaaAvailableInternal(caller_origin, std::move(callback),
1900 /*is_get_client_capabilities_call=*/false);
Ken Buchanan90fe29552024-04-26 21:15:481901}
1902
1903void AuthenticatorCommonImpl::ContinueIsUvpaaAfterOverrideCheck(
1904 blink::mojom::Authenticator::
1905 IsUserVerifyingPlatformAuthenticatorAvailableCallback callback,
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421906 bool is_get_client_capabilities_call,
Ken Buchanan90fe29552024-04-26 21:15:481907 std::optional<bool> is_uvpaa_override) {
Martin Kreichgauerc5c5f3c2021-04-26 23:54:271908 if (is_uvpaa_override) {
1909 std::move(callback).Run(*is_uvpaa_override);
1910 return;
1911 }
Martin Kreichgauerb4b782092020-12-02 18:24:551912
Martin Kreichgauerc5c5f3c2021-04-26 23:54:271913 // Record IsUVPAA result in a UMA metric, but only if they're not the
1914 // WebAuthenticationDelegate override value, so that results from the testing
1915 // API and disabling in Guest/Off-The-Record profiles aren't counted.
1916 auto uma_decorated_callback =
Andrii Natiahlyi6b2f4b12024-09-03 14:58:421917 is_get_client_capabilities_call
1918 ? std::move(callback)
1919 : base::BindOnce([](bool available) {
1920 base::UmaHistogramBoolean(
1921 "WebAuthentication.IsUVPlatformAuthenticatorAvailable2",
1922 available);
1923 return available;
1924 }).Then(std::move(callback));
Martin Kreichgauerc5c5f3c2021-04-26 23:54:271925
Xiaohan Wang2ba85e32022-01-15 17:19:401926#if BUILDFLAG(IS_MAC)
Martin Kreichgauerc5c5f3c2021-04-26 23:54:271927 IsUVPlatformAuthenticatorAvailable(GetBrowserContext(),
1928 std::move(uma_decorated_callback));
Martin Kreichgauera4c7fcf42024-08-19 22:59:451929#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
Martin Kreichgauerc5c5f3c2021-04-26 23:54:271930 IsUVPlatformAuthenticatorAvailable(std::move(uma_decorated_callback));
1931#else
1932 std::move(uma_decorated_callback).Run(false);
1933#endif
Manas Verma2ff0cc572019-03-19 23:42:561934}
1935
Amos Lim12696e5e32022-09-16 07:37:581936void AuthenticatorCommonImpl::IsConditionalMediationAvailable(
Martin Kreichgauer1f4aa592023-01-06 18:39:371937 url::Origin caller_origin,
Nina Satragnoc3444e8f2022-08-04 22:43:001938 blink::mojom::Authenticator::IsConditionalMediationAvailableCallback
1939 callback) {
1940 // Conditional mediation is always supported if the virtual environment is
1941 // providing a platform authenticator.
Ken Buchanan90fe29552024-04-26 21:15:481942 GetWebAuthenticationDelegate()
1943 ->IsUserVerifyingPlatformAuthenticatorAvailableOverride(
1944 GetRenderFrameHost(),
1945 base::BindOnce(
1946 &AuthenticatorCommonImpl::
1947 ContinueIsConditionalMediationAvailableAfterOverrideCheck,
1948 weak_factory_.GetWeakPtr(), std::move(caller_origin),
1949 std::move(callback)));
1950}
1951
1952void AuthenticatorCommonImpl::
1953 ContinueIsConditionalMediationAvailableAfterOverrideCheck(
1954 url::Origin caller_origin,
1955 blink::mojom::Authenticator::IsConditionalMediationAvailableCallback
1956 callback,
1957 std::optional<bool> is_uvpaa_override) {
1958 if (is_uvpaa_override.has_value()) {
1959 std::move(callback).Run(*is_uvpaa_override);
Nina Satragnoc3444e8f2022-08-04 22:43:001960 return;
1961 }
Nina Satragnoea70117c2023-10-05 17:50:321962 // Conditional requests cannot be proxied, signal the feature as unavailable.
Martin Kreichgauer1f4aa592023-01-06 18:39:371963 if (GetWebAuthnRequestProxyIfActive(caller_origin)) {
Nina Satragno867bca52022-10-13 15:02:021964 std::move(callback).Run(false);
1965 return;
1966 }
Nina Satragno3abdd75f02024-11-08 15:36:081967 // Desktop Chrome can always show GPM passkeys through conditional mediation.
Nina Satragnoc3444e8f2022-08-04 22:43:001968 std::move(callback).Run(true);
Nina Satragnoc3444e8f2022-08-04 22:43:001969}
1970
Gabriel Viera7bc08f212024-07-10 15:42:331971void AuthenticatorCommonImpl::Report(
1972 url::Origin caller_origin,
1973 blink::mojom::PublicKeyCredentialReportOptionsPtr options,
1974 blink::mojom::Authenticator::ReportCallback callback) {
1975 if (req_state_) {
1976 std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
1977 nullptr);
1978 return;
1979 }
1980 req_state_ = std::make_unique<RequestState>();
Adam Langleye8ba2cc2024-08-06 13:53:231981 req_state_->request_key = RequestKey(next_request_key_);
1982
Adem Derinele4777b62024-09-04 07:26:541983 req_state_->response_callback = std::move(callback);
Gabriel Viera7bc08f212024-07-10 15:42:331984 req_state_->caller_origin = std::move(caller_origin);
1985 req_state_->relying_party_id = options->relying_party_id;
1986
1987 bool is_cross_origin_iframe = false;
1988 blink::mojom::AuthenticatorStatus status =
1989 security_checker_->ValidateAncestorOrigins(
1990 req_state_->caller_origin,
1991 WebAuthRequestSecurityChecker::RequestType::kReport,
1992 &is_cross_origin_iframe);
1993
1994 // TODO(crbug.com/347727501): Add test for ValidateAncestorOrigins's status.
1995 if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
1996 CompleteReportRequest(status);
1997 return;
1998 }
1999 std::unique_ptr<WebAuthRequestSecurityChecker::RemoteValidation>
2000 remote_validation = security_checker_->ValidateDomainAndRelyingPartyID(
2001 req_state_->caller_origin, req_state_->relying_party_id,
2002 WebAuthRequestSecurityChecker::RequestType::kReport,
Andrii Natiahlyia700f91f2025-03-21 18:37:392003 /*remote_desktop_client_override_origin=*/std::nullopt,
Gabriel Viera7bc08f212024-07-10 15:42:332004 base::BindOnce(&AuthenticatorCommonImpl::ContinueReportAfterRpIdCheck,
Adam Langleye8ba2cc2024-08-06 13:53:232005 weak_factory_.GetWeakPtr(), GetRequestKey(),
2006 std::move(options)));
Gabriel Viera7bc08f212024-07-10 15:42:332007
2008 // TODO(crbug.com/347727501): Add a test to cover the case when
2009 // remote_validation is not null. If `remote_validation` is nullptr then the
2010 // request may already have completed.
2011 if (remote_validation) {
2012 req_state_->remote_rp_id_validation = std::move(remote_validation);
2013 }
2014}
2015
2016void AuthenticatorCommonImpl::ContinueReportAfterRpIdCheck(
Adam Langleye8ba2cc2024-08-06 13:53:232017 RequestKey request_key,
Gabriel Viera7bc08f212024-07-10 15:42:332018 blink::mojom::PublicKeyCredentialReportOptionsPtr options,
2019 blink::mojom::AuthenticatorStatus rp_id_validation_result) {
Adam Langleye8ba2cc2024-08-06 13:53:232020 if (!CheckRequestKey(request_key)) {
2021 return;
2022 }
Gabriel Viera7bc08f212024-07-10 15:42:332023 req_state_->remote_rp_id_validation.reset();
Gabriel Viera7bc08f212024-07-10 15:42:332024 if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
2025 CompleteReportRequest(rp_id_validation_result);
2026 return;
2027 }
Nina Satragnocb0406e2024-09-09 19:47:482028 RenderFrameHost* render_frame_host = GetRenderFrameHost();
Gabriel Viera211af6362024-08-12 13:05:482029 if (options->all_accepted_credentials) {
Nina Satragnocb0406e2024-09-09 19:47:482030 DeleteUnacceptedVirtualAuthenticatorCreds(
2031 render_frame_host, req_state_->relying_party_id,
2032 options->all_accepted_credentials->user_id,
2033 options->all_accepted_credentials->all_accepted_credentials_ids);
Nina Satragnof7dc2fcb2025-03-07 19:46:402034 GetWebAuthenticationDelegate()->SignalAllAcceptedCredentials(
Nina Satragnocb0406e2024-09-09 19:47:482035 WebContents::FromRenderFrameHost(render_frame_host),
Nina Satragnof7dc2fcb2025-03-07 19:46:402036 req_state_->caller_origin, req_state_->relying_party_id,
Gabriel Viera211af6362024-08-12 13:05:482037 options->all_accepted_credentials->user_id,
2038 options->all_accepted_credentials->all_accepted_credentials_ids);
Nina Satragnobee9af12024-10-31 19:28:432039#if BUILDFLAG(IS_WIN)
2040 device::WinWebAuthnApiAuthenticator::SignalAllAcceptedCredentials(
2041 device::WinWebAuthnApi::GetDefault(), req_state_->relying_party_id,
2042 options->all_accepted_credentials->user_id,
2043 options->all_accepted_credentials->all_accepted_credentials_ids);
2044#endif // BUILDFLAG(IS_WIN)
Gabriel Viera61588b882024-08-12 13:05:582045 } else if (options->current_user_details) {
Nina Satragnocb0406e2024-09-09 19:47:482046 UpdateVirtualAuthenticatorUserCreds(
2047 render_frame_host, req_state_->relying_party_id,
2048 options->current_user_details->user_id,
2049 options->current_user_details->name,
2050 options->current_user_details->display_name);
Gabriel Viera61588b882024-08-12 13:05:582051 GetWebAuthenticationDelegate()->UpdateUserPasskeys(
Nina Satragnocb0406e2024-09-09 19:47:482052 WebContents::FromRenderFrameHost(render_frame_host),
Nina Satragno2e8237f2024-09-16 17:25:502053 req_state_->caller_origin, req_state_->relying_party_id,
2054 options->current_user_details->user_id,
Gabriel Viera61588b882024-08-12 13:05:582055 options->current_user_details->name,
2056 options->current_user_details->display_name);
Gabriel Viera48824162024-08-12 13:14:222057 } else if (options->unknown_credential_id) {
Nina Satragno03dab4d2024-10-31 19:28:182058#if BUILDFLAG(IS_WIN)
2059 device::WinWebAuthnApiAuthenticator::SignalUnknownCredential(
2060 device::WinWebAuthnApi::GetDefault(), *options->unknown_credential_id,
2061 req_state_->relying_party_id);
2062#endif // BUILDFLAG(IS_WIN)
Nina Satragnocb0406e2024-09-09 19:47:482063 DeleteVirtualAuthenticatorCreds(render_frame_host,
2064 *options->unknown_credential_id,
2065 req_state_->relying_party_id);
Nina Satragno4f459e22025-03-05 22:13:242066 GetWebAuthenticationDelegate()->PasskeyUnrecognized(
Nina Satragnocb0406e2024-09-09 19:47:482067 WebContents::FromRenderFrameHost(render_frame_host),
Nina Satragno4f459e22025-03-05 22:13:242068 req_state_->caller_origin, *options->unknown_credential_id,
2069 req_state_->relying_party_id);
Gabriel Viera211af6362024-08-12 13:05:482070 }
Gabriel Viera7bc08f212024-07-10 15:42:332071 CompleteReportRequest(blink::mojom::AuthenticatorStatus::SUCCESS, nullptr);
2072}
2073
Adem Derineldc2d52f2024-09-19 08:06:562074void AuthenticatorCommonImpl::GetMetricsWrappedMakeCredentialCallback(
2075 MakeCredentialCallback original_callback,
2076 blink::mojom::AuthenticatorStatus status,
2077 blink::mojom::MakeCredentialAuthenticatorResponsePtr authenticator_response,
2078 blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
2079 if (req_state_ &&
2080 req_state_->request_result == CredentialRequestResult::kTimeout) {
2081 base::RecordAction(
2082 base::UserMetricsAction("WebAuthn.MakeCredential.Timeout"));
2083 } else if (req_state_ && req_state_->request_result ==
2084 CredentialRequestResult::kUserCancelled) {
2085 base::RecordAction(
2086 base::UserMetricsAction("WebAuthn.MakeCredential.Cancelled"));
2087 } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
2088 base::RecordAction(
2089 base::UserMetricsAction("WebAuthn.MakeCredential.Success"));
2090 } else if (status == blink::mojom::AuthenticatorStatus::ABORT_ERROR) {
2091 base::RecordAction(
2092 base::UserMetricsAction("WebAuthn.MakeCredential.Aborted"));
2093 } else {
2094 base::RecordAction(
2095 base::UserMetricsAction("WebAuthn.MakeCredential.Failure"));
2096 }
2097 std::move(original_callback)
2098 .Run(status, std::move(authenticator_response),
2099 std::move(dom_exception_details));
2100}
2101
Adem Derinel72e11db2025-02-11 15:58:002102void AuthenticatorCommonImpl::GetMetricsWrappedGetCredentialCallback(
2103 blink::mojom::Authenticator::GetCredentialCallback callback,
2104 blink::mojom::GetCredentialResponsePtr response) {
Adem Derinel0fa39b2e2025-06-16 13:08:282105 if (response.is_null()) {
2106 std::move(callback).Run(std::move(response));
2107 return;
2108 }
2109 if (response->is_password_response()) {
2110 base::RecordAction(
2111 base::UserMetricsAction("WebAuthn.GetCredential.SuccessWithPassword"));
Adem Derinel72e11db2025-02-11 15:58:002112 std::move(callback).Run(std::move(response));
2113 return;
2114 }
2115 const auto status = response->get_get_assertion_response()->status;
Adem Derineldc2d52f2024-09-19 08:06:562116 if (req_state_ &&
2117 req_state_->request_result == CredentialRequestResult::kTimeout) {
2118 base::RecordAction(
2119 base::UserMetricsAction("WebAuthn.GetAssertion.Timeout"));
2120 } else if (req_state_ && req_state_->request_result ==
2121 CredentialRequestResult::kUserCancelled) {
2122 base::RecordAction(
2123 base::UserMetricsAction("WebAuthn.GetAssertion.Cancelled"));
2124 } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
2125 base::RecordAction(
2126 base::UserMetricsAction("WebAuthn.GetAssertion.Success"));
2127 } else if (status == blink::mojom::AuthenticatorStatus::ABORT_ERROR) {
2128 base::RecordAction(
2129 base::UserMetricsAction("WebAuthn.GetAssertion.Aborted"));
2130 } else {
2131 base::RecordAction(
2132 base::UserMetricsAction("WebAuthn.GetAssertion.Failure"));
2133 }
Adem Derinel72e11db2025-02-11 15:58:002134 std::move(callback).Run(std::move(response));
Adem Derineldc2d52f2024-09-19 08:06:562135}
2136
Amos Lim12696e5e32022-09-16 07:37:582137void AuthenticatorCommonImpl::Cancel() {
Suzy Lid4dda9c2019-05-10 17:36:422138 CancelWithStatus(blink::mojom::AuthenticatorStatus::ABORT_ERROR);
2139}
2140
Amos Lim12696e5e32022-09-16 07:37:582141void AuthenticatorCommonImpl::OnRegisterResponse(
Martin Kreichgauerabd93b92019-08-30 19:35:382142 device::MakeCredentialStatus status_code,
Arthur Sonzognic686e8f2024-01-11 08:36:372143 std::optional<device::AuthenticatorMakeCredentialResponse> response_data,
Martin Kreichgauer3676c7282019-07-19 19:27:222144 const device::FidoAuthenticator* authenticator) {
Adam Langley2cdc5972024-10-03 12:33:132145 CHECK(authenticator);
2146
Adam Langleyf59b55602023-07-05 19:51:202147 if (!req_state_->request_handler) {
2148 // Either the callback was called immediately and
2149 // |req_state_->request_handler| has not yet been assigned (this is a bug),
2150 // or a navigation caused the request to be canceled while a callback was
2151 // enqueued.
Manas Verma2ff0cc572019-03-19 23:42:562152 return;
2153 }
2154
Adem Derinele4777b62024-09-04 07:26:542155 req_state_->request_result = CredentialRequestResultFromCode(
Ken Buchanand5edc0782024-06-10 22:01:222156 status_code == device::MakeCredentialStatus::kSuccess,
2157 authenticator->GetType());
2158
Manas Verma2ff0cc572019-03-19 23:42:562159 switch (status_code) {
Martin Kreichgauerabd93b92019-08-30 19:35:382160 case device::MakeCredentialStatus::kUserConsentButCredentialExcluded:
Adam Langleyfaa43ab42021-09-30 18:12:092161 case device::MakeCredentialStatus::kWinInvalidStateError:
Manas Verma2ff0cc572019-03-19 23:42:562162 // Duplicate registration: the new credential would be created on an
2163 // authenticator that already contains one of the credentials in
Adam Langleyae4001f12024-06-06 17:03:412164 // |exclude_credentials|. If the target was a platform authenticator then
2165 // the RP learns of the result via the distinctive InvalidStateError
2166 // result. This tells them that the platform authenticator is already
2167 // registered with one of the credential IDs that they already know about.
Adam Langleyfaa43ab42021-09-30 18:12:092168 //
2169 // Windows already behaves like this and so its representation of
2170 // InvalidStateError is handled this way too.
Adem Derinele4777b62024-09-04 07:26:542171 req_state_->request_outcome = MakeCredentialOutcome::kCredentialExcluded;
Adam Langleyae4001f12024-06-06 17:03:412172 if ((authenticator &&
2173 IsPlatformAuthenticatorForInvalidStateError(authenticator)) ||
Adam Langleyfaa43ab42021-09-30 18:12:092174 status_code == device::MakeCredentialStatus::kWinInvalidStateError) {
2175 CompleteMakeCredentialRequest(
2176 blink::mojom::AuthenticatorStatus::CREDENTIAL_EXCLUDED, nullptr,
Martin Kreichgauer6119e842022-01-28 01:52:412177 nullptr, Focus::kDoCheck);
Adam Langleyfaa43ab42021-09-30 18:12:092178 } else {
2179 SignalFailureToRequestDelegate(
Adam Langleyfaa43ab42021-09-30 18:12:092180 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2181 kKeyAlreadyRegistered,
2182 blink::mojom::AuthenticatorStatus::CREDENTIAL_EXCLUDED);
2183 }
Manas Verma2ff0cc572019-03-19 23:42:562184 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382185 case device::MakeCredentialStatus::kAuthenticatorResponseInvalid:
Adem Derinele4777b62024-09-04 07:26:542186 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272187 MakeCredentialOutcome::kUnknownResponseFromAuthenticator;
Manas Verma2ff0cc572019-03-19 23:42:562188 // The response from the authenticator was corrupted.
Martin Kreichgauer393f5ef2021-03-30 23:57:572189 CompleteMakeCredentialRequest(
Manas Verma2ff0cc572019-03-19 23:42:562190 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
Martin Kreichgauer6119e842022-01-28 01:52:412191 nullptr, Focus::kDoCheck);
Manas Verma2ff0cc572019-03-19 23:42:562192 return;
Adam Langley63b34812023-06-05 23:08:192193 case device::MakeCredentialStatus::kHybridTransportError:
Adem Derinele4777b62024-09-04 07:26:542194 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272195 MakeCredentialOutcome::kHybridTransportError;
Adam Langley63b34812023-06-05 23:08:192196 SignalFailureToRequestDelegate(
2197 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2198 kHybridTransportError,
2199 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2200 return;
Adam Langleya561c272024-01-30 21:51:322201 case device::MakeCredentialStatus::kEnclaveError:
Adem Derinele4777b62024-09-04 07:26:542202 req_state_->request_outcome = MakeCredentialOutcome::kEnclaveError;
Adam Langleya561c272024-01-30 21:51:322203 SignalFailureToRequestDelegate(
2204 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2205 kEnclaveError,
2206 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2207 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382208 case device::MakeCredentialStatus::kUserConsentDenied:
Adem Derinele4777b62024-09-04 07:26:542209 req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
Nina Satragno5b764322019-07-04 14:40:162210 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442211 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2212 kUserConsentDenied,
2213 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562214 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382215 case device::MakeCredentialStatus::kSoftPINBlock:
Adem Derinele4777b62024-09-04 07:26:542216 req_state_->request_outcome = MakeCredentialOutcome::kSoftPinBlock;
Manas Verma2ff0cc572019-03-19 23:42:562217 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442218 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2219 kSoftPINBlock,
2220 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562221 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382222 case device::MakeCredentialStatus::kHardPINBlock:
Adem Derinele4777b62024-09-04 07:26:542223 req_state_->request_outcome = MakeCredentialOutcome::kHardPinBlock;
Manas Verma2ff0cc572019-03-19 23:42:562224 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442225 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2226 kHardPINBlock,
2227 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562228 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382229 case device::MakeCredentialStatus::kAuthenticatorRemovedDuringPINEntry:
Adem Derinele4777b62024-09-04 07:26:542230 req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
Manas Verma2ff0cc572019-03-19 23:42:562231 SignalFailureToRequestDelegate(
2232 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442233 kAuthenticatorRemovedDuringPINEntry,
2234 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562235 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382236 case device::MakeCredentialStatus::kAuthenticatorMissingResidentKeys:
Adem Derinele4777b62024-09-04 07:26:542237 req_state_->request_outcome = MakeCredentialOutcome::kRkNotSupported;
Adam Langleybf92fdc2019-03-27 21:13:542238 SignalFailureToRequestDelegate(
2239 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442240 kAuthenticatorMissingResidentKeys,
2241 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langleybf92fdc2019-03-27 21:13:542242 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382243 case device::MakeCredentialStatus::kAuthenticatorMissingUserVerification:
Adem Derinele4777b62024-09-04 07:26:542244 req_state_->request_outcome = MakeCredentialOutcome::kUvNotSupported;
Adam Langleybf92fdc2019-03-27 21:13:542245 SignalFailureToRequestDelegate(
2246 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442247 kAuthenticatorMissingUserVerification,
2248 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langleybf92fdc2019-03-27 21:13:542249 return;
Nina Satragno5ba78462020-10-02 17:25:152250 case device::MakeCredentialStatus::kAuthenticatorMissingLargeBlob:
Adem Derinele4777b62024-09-04 07:26:542251 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272252 MakeCredentialOutcome::kLargeBlobNotSupported;
Nina Satragno5ba78462020-10-02 17:25:152253 SignalFailureToRequestDelegate(
Nina Satragno5ba78462020-10-02 17:25:152254 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2255 kAuthenticatorMissingLargeBlob,
2256 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2257 return;
Adam Langleyb8fdd112020-06-15 21:44:322258 case device::MakeCredentialStatus::kNoCommonAlgorithms:
Adem Derinele4777b62024-09-04 07:26:542259 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272260 MakeCredentialOutcome::kAlgorithmNotSupported;
Adam Langleyb8fdd112020-06-15 21:44:322261 SignalFailureToRequestDelegate(
Adam Langleyb8fdd112020-06-15 21:44:322262 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2263 kNoCommonAlgorithms,
2264 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2265 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382266 case device::MakeCredentialStatus::kStorageFull:
Adem Derinele4777b62024-09-04 07:26:542267 req_state_->request_outcome = MakeCredentialOutcome::kStorageFull;
Adam Langley77fab612019-05-03 05:19:042268 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442269 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2270 kStorageFull,
2271 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langley77fab612019-05-03 05:19:042272 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382273 case device::MakeCredentialStatus::kWinNotAllowedError:
Adem Derinele4777b62024-09-04 07:26:542274 req_state_->request_outcome = MakeCredentialOutcome::kPlatformNotAllowed;
Adam Langley30b8bed2020-02-21 19:53:192275 SignalFailureToRequestDelegate(
Adam Langley30b8bed2020-02-21 19:53:192276 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2277 kWinUserCancelled,
2278 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Martin Kreichgauerf45542a2019-08-30 16:45:502279 return;
Adam Langleyf6b0b86e2024-05-13 20:03:582280 case device::MakeCredentialStatus::kEnclaveCancel:
Adem Derinele4777b62024-09-04 07:26:542281 req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
Adam Langleyf6b0b86e2024-05-13 20:03:582282 SignalFailureToRequestDelegate(
2283 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2284 kEnclaveCancel,
2285 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2286 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382287 case device::MakeCredentialStatus::kSuccess:
Adam Langley6d2245712023-08-02 23:36:272288 break;
Manas Verma2ff0cc572019-03-19 23:42:562289 }
Adam Langley6d2245712023-08-02 23:36:272290
2291 DCHECK(response_data.has_value());
2292 DCHECK(authenticator);
2293
Adam Langley3ec44c22023-08-10 01:04:012294 req_state_->request_delegate->OnTransactionSuccessful(
2295 RequestSource(), device::FidoRequestType::kMakeCredential,
2296 authenticator->GetType());
2297
Adam Langley32b8fd7d2024-08-29 19:27:292298 std::optional<device::FidoTransportProtocol> transport =
2299 authenticator->AuthenticatorTransport();
2300 bool is_transport_used_internal = false;
2301 bool is_transport_used_cable = false;
2302 if (transport) {
2303 is_transport_used_internal =
2304 *transport == device::FidoTransportProtocol::kInternal;
2305 is_transport_used_cable =
2306 *transport == device::FidoTransportProtocol::kHybrid;
2307 }
2308
Adam Langley6d2245712023-08-02 23:36:272309 const auto attestation =
Victor Hugo Vianna Silva00692722025-03-18 19:51:482310 std::get<device::CtapMakeCredentialRequest>(req_state_->ctap_request)
Adem Derinele4777b62024-09-04 07:26:542311 .attestation_preference;
Arthur Sonzognic686e8f2024-01-11 08:36:372312 std::optional<AttestationErasureOption> attestation_erasure;
Adam Langley6d2245712023-08-02 23:36:272313
2314 if (response_data->attestation_should_be_filtered &&
2315 !GetWebAuthenticationDelegate()->ShouldPermitIndividualAttestation(
2316 GetBrowserContext(), req_state_->caller_origin,
2317 req_state_->relying_party_id)) {
2318 attestation_erasure = AttestationErasureOption::kEraseAttestationAndAaguid;
Adam Langley193cb372024-09-13 20:09:052319 } else if (response_data->attestation_object
2320 .IsAttestationCertificateInappropriatelyIdentifying() &&
2321 !GetWebAuthenticationDelegate()->ShouldPermitIndividualAttestation(
2322 GetBrowserContext(), req_state_->caller_origin,
2323 req_state_->relying_party_id)) {
2324 // If the RP sees a "none" attestation with a zero AAGUID after requesting
2325 // "direct" attestation then they can reasonably conclude that it was one of
2326 // the tokens with inappropriate certs. But this is better than disclosing
2327 // the certificate itself, and these tokens are vanishingly rare now.
2328 attestation_erasure = AttestationErasureOption::kEraseAttestationAndAaguid;
Adam Langley6d2245712023-08-02 23:36:272329 } else if (attestation == device::AttestationConveyancePreference::
2330 kEnterpriseApprovedByBrowser) {
2331 // If enterprise attestation was approved by policy then it can be
2332 // returned immediately.
2333 attestation_erasure = AttestationErasureOption::kIncludeAttestation;
Adam Langley193cb372024-09-13 20:09:052334 } else if (response_data->attestation_object.IsSelfAttestation()) {
2335 // Self attestation is just a self-signature and carries no identifying
2336 // information.
2337 attestation_erasure = AttestationErasureOption::kIncludeAttestation;
2338 } else if (is_transport_used_internal || is_transport_used_cable) {
Adam Langley6d2245712023-08-02 23:36:272339 // Direct attestation from platform authenticators is known to be
Adam Langley193cb372024-09-13 20:09:052340 // privacy preserving, so we always return it when requested. We follow the
2341 // same rule when the platform authenticator is used over hybrid so that
2342 // sites see a consistent experience between syncing and using the phone.
2343 // Also, counter to what the WebAuthn spec says, we do not erase the AAGUID
Adam Langley6d2245712023-08-02 23:36:272344 // even when attestation wasn't requested.
2345 attestation_erasure =
2346 attestation != device::AttestationConveyancePreference::kNone
2347 ? AttestationErasureOption::kIncludeAttestation
2348 : AttestationErasureOption::kEraseAttestationButIncludeAaguid;
Adam Langley6d2245712023-08-02 23:36:272349 } else if (attestation == device::AttestationConveyancePreference::kNone) {
2350 attestation_erasure = AttestationErasureOption::kEraseAttestationAndAaguid;
Adam Langley6d2245712023-08-02 23:36:272351 } else {
Adam Langley193cb372024-09-13 20:09:052352 // The UI will have shown a notification that attestation was requested.
2353 attestation_erasure = AttestationErasureOption::kIncludeAttestation;
Manas Verma2ff0cc572019-03-19 23:42:562354 }
2355
Martin Kreichgauer393f5ef2021-03-30 23:57:572356 CompleteMakeCredentialRequest(
Martin Kreichgauer3a23be62020-03-27 00:23:452357 blink::mojom::AuthenticatorStatus::SUCCESS,
Adam Langley193cb372024-09-13 20:09:052358 CreateMakeCredentialResponse(std::move(*response_data),
2359 *attestation_erasure),
Martin Kreichgauer6119e842022-01-28 01:52:412360 nullptr, Focus::kDoCheck);
Manas Verma2ff0cc572019-03-19 23:42:562361}
2362
Amos Lim12696e5e32022-09-16 07:37:582363void AuthenticatorCommonImpl::OnSignResponse(
Martin Kreichgauerabd93b92019-08-30 19:35:382364 device::GetAssertionStatus status_code,
Arthur Sonzognic686e8f2024-01-11 08:36:372365 std::optional<std::vector<device::AuthenticatorGetAssertionResponse>>
Adam Langley3ec44c22023-08-10 01:04:012366 response_data,
2367 device::FidoAuthenticator* authenticator) {
Adam Langley2cdc5972024-10-03 12:33:132368 CHECK(authenticator);
Manas Verma2ff0cc572019-03-19 23:42:562369 DCHECK(!response_data || !response_data->empty()); // empty vector is invalid
Adam Langley2cdc5972024-10-03 12:33:132370
Adam Langleyf59b55602023-07-05 19:51:202371 if (!req_state_->request_handler) {
2372 // Either the callback was called immediately and
2373 // |req_state_->request_handler| has not yet been assigned (this is a bug),
2374 // or a navigation caused the request to be canceled while a callback was
2375 // enqueued.
Manas Verma2ff0cc572019-03-19 23:42:562376 return;
2377 }
2378
Adem Derinele4777b62024-09-04 07:26:542379 req_state_->request_result = CredentialRequestResultFromCode(
Ken Buchanand5edc0782024-06-10 22:01:222380 status_code == device::GetAssertionStatus::kSuccess,
2381 authenticator->GetType());
Nina Satragno129251c2023-10-23 21:50:402382
Manas Verma2ff0cc572019-03-19 23:42:562383 switch (status_code) {
Martin Kreichgauerabd93b92019-08-30 19:35:382384 case device::GetAssertionStatus::kUserConsentButCredentialNotRecognized:
Adem Derinele4777b62024-09-04 07:26:542385 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272386 GetAssertionOutcome::kCredentialNotRecognized;
Manas Verma2ff0cc572019-03-19 23:42:562387 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442388 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2389 kKeyNotRegistered,
2390 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562391 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382392 case device::GetAssertionStatus::kAuthenticatorResponseInvalid:
Adem Derinele4777b62024-09-04 07:26:542393 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272394 GetAssertionOutcome::kUnknownResponseFromAuthenticator;
Manas Verma2ff0cc572019-03-19 23:42:562395 // The response from the authenticator was corrupted.
Martin Kreichgauer393f5ef2021-03-30 23:57:572396 CompleteGetAssertionRequest(
Manas Verma9ba13692019-03-21 21:01:002397 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562398 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382399 case device::GetAssertionStatus::kUserConsentDenied:
Adem Derinele4777b62024-09-04 07:26:542400 req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
Nina Satragno5b764322019-07-04 14:40:162401 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442402 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2403 kUserConsentDenied,
2404 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562405 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382406 case device::GetAssertionStatus::kSoftPINBlock:
Adem Derinele4777b62024-09-04 07:26:542407 req_state_->request_outcome = GetAssertionOutcome::kSoftPinBlock;
Manas Verma2ff0cc572019-03-19 23:42:562408 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442409 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2410 kSoftPINBlock,
2411 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562412 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382413 case device::GetAssertionStatus::kHardPINBlock:
Adem Derinele4777b62024-09-04 07:26:542414 req_state_->request_outcome = GetAssertionOutcome::kHardPinBlock;
Manas Verma2ff0cc572019-03-19 23:42:562415 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442416 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2417 kHardPINBlock,
2418 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562419 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382420 case device::GetAssertionStatus::kAuthenticatorRemovedDuringPINEntry:
Adem Derinele4777b62024-09-04 07:26:542421 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
Manas Verma2ff0cc572019-03-19 23:42:562422 SignalFailureToRequestDelegate(
2423 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442424 kAuthenticatorRemovedDuringPINEntry,
2425 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562426 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382427 case device::GetAssertionStatus::kAuthenticatorMissingResidentKeys:
Adem Derinele4777b62024-09-04 07:26:542428 req_state_->request_outcome = GetAssertionOutcome::kRkNotSupported;
Adam Langleybf92fdc2019-03-27 21:13:542429 SignalFailureToRequestDelegate(
2430 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442431 kAuthenticatorMissingResidentKeys,
2432 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langleybf92fdc2019-03-27 21:13:542433 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382434 case device::GetAssertionStatus::kAuthenticatorMissingUserVerification:
Adem Derinele4777b62024-09-04 07:26:542435 req_state_->request_outcome = GetAssertionOutcome::kUvNotSupported;
Adam Langleybf92fdc2019-03-27 21:13:542436 SignalFailureToRequestDelegate(
2437 AuthenticatorRequestClientDelegate::InterestingFailureReason::
Martin Kreichgauer86faed32019-08-29 18:44:442438 kAuthenticatorMissingUserVerification,
2439 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Adam Langleybf92fdc2019-03-27 21:13:542440 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382441 case device::GetAssertionStatus::kWinNotAllowedError:
Adem Derinele4777b62024-09-04 07:26:542442 req_state_->request_outcome = GetAssertionOutcome::kPlatformNotAllowed;
Adam Langley30b8bed2020-02-21 19:53:192443 SignalFailureToRequestDelegate(
Adam Langley30b8bed2020-02-21 19:53:192444 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2445 kWinUserCancelled,
Martin Kreichgauer20c37812019-04-25 20:39:282446 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2447 return;
Adam Langley63b34812023-06-05 23:08:192448 case device::GetAssertionStatus::kHybridTransportError:
Adem Derinele4777b62024-09-04 07:26:542449 req_state_->request_outcome = GetAssertionOutcome::kHybridTransportError;
Adam Langley63b34812023-06-05 23:08:192450 SignalFailureToRequestDelegate(
2451 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2452 kHybridTransportError,
2453 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2454 return;
Adam Langley5bad30a2023-08-29 19:49:132455 case device::GetAssertionStatus::kICloudKeychainNoCredentials:
Adem Derinele4777b62024-09-04 07:26:542456 req_state_->request_outcome =
Ken Buchanan23dce912024-07-11 16:41:272457 GetAssertionOutcome::kCredentialNotRecognized;
Adam Langley5bad30a2023-08-29 19:49:132458 SignalFailureToRequestDelegate(
2459 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2460 kNoPasskeys,
2461 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2462 return;
Adam Langleya561c272024-01-30 21:51:322463 case device::GetAssertionStatus::kEnclaveError:
Adem Derinele4777b62024-09-04 07:26:542464 req_state_->request_outcome = GetAssertionOutcome::kEnclaveError;
Adam Langleya561c272024-01-30 21:51:322465 SignalFailureToRequestDelegate(
2466 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2467 kEnclaveError,
2468 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2469 return;
Adam Langleyf6b0b86e2024-05-13 20:03:582470 case device::GetAssertionStatus::kEnclaveCancel:
Adem Derinele4777b62024-09-04 07:26:542471 req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
Adam Langleyf6b0b86e2024-05-13 20:03:582472 SignalFailureToRequestDelegate(
2473 AuthenticatorRequestClientDelegate::InterestingFailureReason::
2474 kEnclaveCancel,
2475 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2476 return;
Martin Kreichgauerabd93b92019-08-30 19:35:382477 case device::GetAssertionStatus::kSuccess:
Adam Langley8abb4722022-02-01 02:06:482478 break;
2479 }
2480
2481 DCHECK_EQ(status_code, device::GetAssertionStatus::kSuccess);
2482 DCHECK(response_data.has_value());
Adam Langleye7a4c4682022-01-19 19:23:552483
Adam Langley3ec44c22023-08-10 01:04:012484 req_state_->request_delegate->OnTransactionSuccessful(
2485 RequestSource(), device::FidoRequestType::kGetAssertion,
2486 authenticator->GetType());
2487
Martin Kreichgauerb3689d062022-07-12 09:36:512488 // Show an account picker for discoverable credential requests (empty allow
2489 // lists). Responses with a single credential are considered pre-selected if
2490 // one of the following is true:
2491 // - The authenticator omitted user entity information because only one
2492 // credential matched (only valid in CTAP 2.0).
2493 // - The `userSelected` flag is set, because the user chose an account on an
2494 // integrated authenticator UI (CTAP 2.1).
2495 // - The user already pre-selected a platform authenticator credential from
2496 // browser UI prior to the actual GetAssertion request. (The request handler
2497 // set the `userSelected` flag in this case.)
2498 if (response_data->size() == 1) {
2499 const device::AuthenticatorGetAssertionResponse& response =
2500 response_data->at(0);
Adam Langleyf59b55602023-07-05 19:51:202501 if (!req_state_->discoverable_credential_request ||
2502 response.user_selected || !response.user_entity ||
2503 !response.user_entity->name || !response.user_entity->display_name) {
Martin Kreichgauerb3689d062022-07-12 09:36:512504 OnAccountSelected(std::move(response_data->at(0)));
2505 return;
Adam Langley8abb4722022-02-01 02:06:482506 }
Manas Verma2ff0cc572019-03-19 23:42:562507 }
Martin Kreichgauerb3689d062022-07-12 09:36:512508
2509 // Discoverable credential request without preselection UI. Show an account
2510 // picker.
2511 std::vector<device::PublicKeyCredentialUserEntity> users_list;
2512 users_list.reserve(response_data->size());
2513 for (const auto& response : *response_data) {
2514 if (response.user_entity) {
2515 users_list.push_back(*response.user_entity);
2516 }
2517 }
Adam Langleyf59b55602023-07-05 19:51:202518 req_state_->request_delegate->SelectAccount(
Martin Kreichgauerb3689d062022-07-12 09:36:512519 std::move(*response_data),
Amos Lim12696e5e32022-09-16 07:37:582520 base::BindOnce(&AuthenticatorCommonImpl::OnAccountSelected,
Martin Kreichgauerb3689d062022-07-12 09:36:512521 weak_factory_.GetWeakPtr()));
Manas Verma2ff0cc572019-03-19 23:42:562522}
2523
Amos Lim12696e5e32022-09-16 07:37:582524void AuthenticatorCommonImpl::OnAccountSelected(
Adam Langleyf72000b2019-03-27 16:04:242525 device::AuthenticatorGetAssertionResponse response) {
Zakaria Ridouhae30c6012021-09-15 17:30:132526 CompleteGetAssertionRequest(blink::mojom::AuthenticatorStatus::SUCCESS,
2527 CreateGetAssertionResponse(std::move(response)));
Adam Langleyf72000b2019-03-27 16:04:242528}
2529
Amos Lim12696e5e32022-09-16 07:37:582530void AuthenticatorCommonImpl::SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442531 AuthenticatorRequestClientDelegate::InterestingFailureReason reason,
2532 blink::mojom::AuthenticatorStatus status) {
Adam Langleyf59b55602023-07-05 19:51:202533 req_state_->error_awaiting_user_acknowledgement = status;
Manas Verma2ff0cc572019-03-19 23:42:562534
Adam Langley39ca22f2022-01-19 01:15:322535 // The UI may decide to end the request immediately, or after user
2536 // confirmation. Either way stop discoveries and authenticators now.
Adam Langleyf59b55602023-07-05 19:51:202537 if (req_state_->request_handler) {
2538 req_state_->request_handler->StopDiscoveries();
2539 req_state_->request_handler->CancelActiveAuthenticators();
Martin Kreichgauer8c97189a2022-01-10 20:31:432540 }
Adam Langley39ca22f2022-01-19 01:15:322541
Adam Langleyf59b55602023-07-05 19:51:202542 if (req_state_->request_delegate->DoesBlockRequestOnFailure(reason)) {
Adam Langley39ca22f2022-01-19 01:15:322543 // The UI may have decided to start the request over. Thus do not assume
2544 // anything about the state here.
2545 return;
2546 }
2547
2548 // The UI wishes the end the request immediately.
Adam Langleyf59b55602023-07-05 19:51:202549 CancelWithStatus(req_state_->error_awaiting_user_acknowledgement);
Martin Kreichgauer8c97189a2022-01-10 20:31:432550}
2551
Amos Lim12696e5e32022-09-16 07:37:582552void AuthenticatorCommonImpl::BeginRequestTimeout(
Arthur Sonzognic686e8f2024-01-11 08:36:372553 std::optional<base::TimeDelta> timeout) {
Adam Langleyf59b55602023-07-05 19:51:202554 req_state_->timer->Start(FROM_HERE,
2555 AdjustTimeout(timeout, GetRenderFrameHost()),
2556 base::BindOnce(&AuthenticatorCommonImpl::OnTimeout,
2557 weak_factory_.GetWeakPtr()));
Martin Kreichgauer8c97189a2022-01-10 20:31:432558}
Manas Verma2ff0cc572019-03-19 23:42:562559
Alison Gale923a33e2024-04-22 23:34:282560// TODO(crbug.com/41371792): Add web tests to verify timeouts are
Manas Verma2ff0cc572019-03-19 23:42:562561// indistinguishable from NOT_ALLOWED_ERROR cases.
Amos Lim12696e5e32022-09-16 07:37:582562void AuthenticatorCommonImpl::OnTimeout() {
Adam Langley70376e42023-11-02 18:56:352563 if (!req_state_->request_delegate) {
2564 // If no UI has been shown yet (likely because we timed out waiting for RP
2565 // ID validation) then simply cancel the request.
2566 CancelWithStatus(blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
2567 return;
2568 }
2569
Adem Derinele4777b62024-09-04 07:26:542570 req_state_->request_result = CredentialRequestResult::kTimeout;
Victor Hugo Vianna Silva00692722025-03-18 19:51:482571 if (std::holds_alternative<GetCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:542572 req_state_->response_callback)) {
2573 req_state_->request_outcome = GetAssertionOutcome::kUiTimeout;
Ken Buchanand5edc0782024-06-10 22:01:222574 } else {
Adem Derinele4777b62024-09-04 07:26:542575 req_state_->request_outcome = MakeCredentialOutcome::kUiTimeout;
Nina Satragno129251c2023-10-23 21:50:402576 }
Manas Verma2ff0cc572019-03-19 23:42:562577 SignalFailureToRequestDelegate(
Martin Kreichgauer86faed32019-08-29 18:44:442578 AuthenticatorRequestClientDelegate::InterestingFailureReason::kTimeout,
2579 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
Manas Verma2ff0cc572019-03-19 23:42:562580}
2581
Adem Derinel38626c22025-05-22 13:31:582582void AuthenticatorCommonImpl::BeginImmediateRequestTimeout() {
2583 base::TimeDelta timeout_duration = base::Milliseconds(
2584 device::kWebAuthnImmediateMediationTimeoutMilliseconds.Get());
2585 if (timeout_duration.is_negative()) {
2586 return;
2587 }
2588 req_state_->immediate_timer->Start(
2589 FROM_HERE, timeout_duration,
2590 base::BindOnce(&AuthenticatorCommonImpl::OnImmediateTimeout,
2591 weak_factory_.GetWeakPtr()));
2592}
2593
2594void AuthenticatorCommonImpl::OnImmediateTimeout() {
2595 base::UmaHistogramBoolean(kImmediateTimeoutWhileWaitingForUi, true);
Adem Derinel4baab402025-06-16 14:39:512596 base::UmaHistogramEnumeration(
2597 "WebAuthentication.GetAssertion.Immediate.RejectionReason",
2598 ImmediateMediationRejectionReason::kTimeout);
Adem Derinelbe67ee72025-06-16 07:01:302599 CancelRequestForImmediateMediation();
Adem Derinel38626c22025-05-22 13:31:582600}
2601
2602void AuthenticatorCommonImpl::CancelImmediateTimeout() {
2603 if (!req_state_ || !req_state_->immediate_timer ||
2604 !req_state_->immediate_timer->IsRunning()) {
2605 return;
2606 }
2607 base::UmaHistogramBoolean(kImmediateTimeoutWhileWaitingForUi, false);
2608 req_state_->immediate_timer->Stop();
2609}
2610
Adem Derinelbe67ee72025-06-16 07:01:302611void AuthenticatorCommonImpl::CancelRequestForImmediateMediation() {
2612 // Post a task to defer the cancellation, otherwise a reentrancy may occur.
2613 // For example all authenticators may report no credentials; but the
2614 // discoveries may still be active. See crbug.com/424491613 for an example.
2615 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
2616 FROM_HERE,
2617 base::BindOnce(
2618 [](base::WeakPtr<AuthenticatorCommonImpl> auth_ptr) {
2619 if (!auth_ptr) {
2620 return;
2621 }
2622
2623 auth_ptr->CancelWithStatus(
2624 blink::mojom::AuthenticatorStatus::IMMEDIATE_NOT_FOUND);
2625 },
2626 weak_factory_.GetWeakPtr()));
2627}
2628
Amos Lim12696e5e32022-09-16 07:37:582629void AuthenticatorCommonImpl::CancelWithStatus(
Suzy Lid4dda9c2019-05-10 17:36:422630 blink::mojom::AuthenticatorStatus status) {
Adam Langleyf59b55602023-07-05 19:51:202631 // Callers may attempt to cancel whether there is a request or not.
2632 if (!req_state_) {
2633 return;
2634 }
2635 if (req_state_->pending_proxied_request_id) {
Martin Kreichgauerb27f6312022-01-25 00:03:322636 WebAuthenticationRequestProxy* proxy =
2637 GetWebAuthenticationDelegate()->MaybeGetRequestProxy(
Adam Langleyf59b55602023-07-05 19:51:202638 GetBrowserContext(), req_state_->caller_origin);
2639 // As long as `req_state_->pending_proxied_request_id` is set, there should
2640 // be an active request proxy. Deactivation of the proxy would have invoked
Martin Kreichgauer1beaff02022-02-02 18:58:422641 // `OnMakeCredentialProxyResponse()` or `OnGetAssertionProxyResponse()`, and
Adam Langleyf59b55602023-07-05 19:51:202642 // cleared `req_state_->pending_proxied_request_id`
Martin Kreichgauerb27f6312022-01-25 00:03:322643 DCHECK(proxy);
Adam Langleyf59b55602023-07-05 19:51:202644 proxy->CancelRequest(*req_state_->pending_proxied_request_id);
Martin Kreichgauerb27f6312022-01-25 00:03:322645 }
Adam Langleyf59b55602023-07-05 19:51:202646
Adem Derinele4777b62024-09-04 07:26:542647 DCHECK(
Victor Hugo Vianna Silva00692722025-03-18 19:51:482648 !std::holds_alternative<std::monostate>(req_state_->response_callback));
2649 if (std::holds_alternative<MakeCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:542650 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482651 std::get<MakeCredentialCallback>(req_state_->response_callback)) {
Martin Kreichgauer393f5ef2021-03-30 23:57:572652 CompleteMakeCredentialRequest(status);
Adem Derinel72e11db2025-02-11 15:58:002653 return;
2654 }
Victor Hugo Vianna Silva00692722025-03-18 19:51:482655 if (std::holds_alternative<GetCredentialCallback>(
Adem Derinel72e11db2025-02-11 15:58:002656 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482657 std::get<GetCredentialCallback>(req_state_->response_callback)) {
Martin Kreichgauer393f5ef2021-03-30 23:57:572658 CompleteGetAssertionRequest(status);
Suzy Lid4dda9c2019-05-10 17:36:422659 }
2660}
Manas Verma2ff0cc572019-03-19 23:42:562661
Amos Lim12696e5e32022-09-16 07:37:582662void AuthenticatorCommonImpl::OnCancelFromUI() {
Adem Derinele4777b62024-09-04 07:26:542663 if (!req_state_->request_result &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482664 std::holds_alternative<GetCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:542665 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482666 std::get<GetCredentialCallback>(req_state_->response_callback)) {
Nina Satragno129251c2023-10-23 21:50:402667 // The user cancelled before the request finished.
Adem Derinele4777b62024-09-04 07:26:542668 req_state_->request_result = CredentialRequestResult::kUserCancelled;
2669 req_state_->request_outcome = GetAssertionOutcome::kUserCancellation;
2670 } else if (!req_state_->request_result &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482671 std::holds_alternative<MakeCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:542672 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482673 std::get<MakeCredentialCallback>(req_state_->response_callback)) {
Adem Derinele4777b62024-09-04 07:26:542674 req_state_->request_result = CredentialRequestResult::kUserCancelled;
2675 req_state_->request_outcome = MakeCredentialOutcome::kUserCancellation;
Nina Satragno129251c2023-10-23 21:50:402676 }
Adam Langleyf59b55602023-07-05 19:51:202677 CancelWithStatus(req_state_->error_awaiting_user_acknowledgement);
Manas Verma2ff0cc572019-03-19 23:42:562678}
2679
zakaria ridouh15ce79e12021-09-24 20:20:142680blink::mojom::MakeCredentialAuthenticatorResponsePtr
Amos Lim12696e5e32022-09-16 07:37:582681AuthenticatorCommonImpl::CreateMakeCredentialResponse(
zakaria ridouh15ce79e12021-09-24 20:20:142682 device::AuthenticatorMakeCredentialResponse response_data,
2683 AttestationErasureOption attestation_erasure) {
2684 auto response = blink::mojom::MakeCredentialAuthenticatorResponse::New();
2685 auto common_info = blink::mojom::CommonCredentialInfo::New();
Adam Langleyf59b55602023-07-05 19:51:202686 common_info->client_data_json.assign(req_state_->client_data_json.begin(),
2687 req_state_->client_data_json.end());
Adam Langleybfe49b82022-09-02 00:21:462688 common_info->raw_id = response_data.attestation_object.GetCredentialId();
Slobodan Pejic435c11ef2024-12-09 18:28:242689 common_info->id = Base64UrlEncodeOmitPadding(common_info->raw_id);
zakaria ridouh15ce79e12021-09-24 20:20:142690
Martin Kreichgauer930f341a2022-01-07 20:20:462691 response->authenticator_attachment =
Adam Langleybfe49b82022-09-02 00:21:462692 response_data.transport_used
Martin Kreichgauer930f341a2022-01-07 20:20:462693 ? device::AuthenticatorAttachmentFromTransport(
Adam Langleybfe49b82022-09-02 00:21:462694 *response_data.transport_used)
Martin Kreichgauer930f341a2022-01-07 20:20:462695 : device::AuthenticatorAttachment::kAny;
2696
Adam Langley9095b4182022-07-20 16:14:502697 base::flat_set<device::FidoTransportProtocol> transports;
2698 // transports_authoritative tracks whether the contents of `transports` are
2699 // considered to be sufficient complete to report back to the website.
2700 bool transports_authoritative = false;
2701
Adam Langleybfe49b82022-09-02 00:21:462702 if (response_data.transport_used) {
2703 transports.insert(*response_data.transport_used);
zakaria ridouh15ce79e12021-09-24 20:20:142704 }
Adam Langley9095b4182022-07-20 16:14:502705 if (response_data.transports) {
2706 transports.insert(response_data.transports->begin(),
2707 response_data.transports->end());
2708 transports_authoritative = true;
2709 }
2710 // Also include any transports from the attestation certificate.
Arthur Sonzognic686e8f2024-01-11 08:36:372711 std::optional<base::span<const uint8_t>> leaf_cert =
Adam Langleybfe49b82022-09-02 00:21:462712 response_data.attestation_object.attestation_statement()
zakaria ridouh15ce79e12021-09-24 20:20:142713 .GetLeafCertificate();
2714 if (leaf_cert) {
Adam Langley9095b4182022-07-20 16:14:502715 transports_authoritative |=
2716 AddTransportsFromCertificate(*leaf_cert, &transports);
zakaria ridouh15ce79e12021-09-24 20:20:142717 }
2718
Adam Langley0abb556a2023-06-08 22:39:542719 if (!transports_authoritative &&
2720 response_data.transport_used == device::FidoTransportProtocol::kHybrid) {
2721 // Windows doesn't provide transport data, but can provide the transport
2722 // used. If the transport was hybrid then we assume that ['hybrid',
2723 // 'internal'] is a reasonable set of transports.
2724 transports.insert(device::FidoTransportProtocol::kHybrid);
2725 transports.insert(device::FidoTransportProtocol::kInternal);
2726 transports_authoritative = true;
2727 }
2728
Adam Langley9095b4182022-07-20 16:14:502729 // The order of transports doesn't matter because Blink will sort the
2730 // resulting strings before returning them.
2731 if (transports_authoritative) {
2732 response->transports.assign(transports.begin(), transports.end());
2733 }
zakaria ridouh15ce79e12021-09-24 20:20:142734
Adam Langleyf2ba2ce52022-09-05 23:18:302735 switch (attestation_erasure) {
2736 case AttestationErasureOption::kIncludeAttestation:
2737 break;
2738 case AttestationErasureOption::kEraseAttestationButIncludeAaguid:
Adam Langleyf2f838a62024-05-03 20:37:092739 response_data.attestation_object.EraseAttestationStatement(
2740 device::AttestationObject::AAGUID::kInclude);
Adam Langleyf2ba2ce52022-09-05 23:18:302741 break;
2742 case AttestationErasureOption::kEraseAttestationAndAaguid:
Adam Langleyf2f838a62024-05-03 20:37:092743 response_data.attestation_object.EraseAttestationStatement(
2744 device::AttestationObject::AAGUID::kErase);
Adam Langleyf2ba2ce52022-09-05 23:18:302745 break;
2746 }
2747
Adam Langleyd27d7db2023-02-08 16:45:372748 bool did_create_hmac_secret = response_data.prf_enabled;
zakaria ridouh15ce79e12021-09-24 20:20:142749 bool did_store_cred_blob = false;
Arthur Sonzognic686e8f2024-01-11 08:36:372750 const std::optional<cbor::Value>& maybe_extensions =
Adam Langleybfe49b82022-09-02 00:21:462751 response_data.attestation_object.authenticator_data().extensions();
zakaria ridouh15ce79e12021-09-24 20:20:142752 if (maybe_extensions) {
2753 DCHECK(maybe_extensions->is_map());
2754 const cbor::Value::MapValue& extensions = maybe_extensions->GetMap();
2755
Adam Langleyd27d7db2023-02-08 16:45:372756 if (!did_create_hmac_secret) {
2757 const auto hmac_secret_it =
2758 extensions.find(cbor::Value(device::kExtensionHmacSecret));
2759 if (hmac_secret_it != extensions.end() &&
2760 hmac_secret_it->second.is_bool() &&
2761 hmac_secret_it->second.GetBool()) {
2762 did_create_hmac_secret = true;
2763 }
zakaria ridouh15ce79e12021-09-24 20:20:142764 }
2765
2766 const auto cred_blob_it =
2767 extensions.find(cbor::Value(device::kExtensionCredBlob));
2768 if (cred_blob_it != extensions.end() && cred_blob_it->second.is_bool() &&
2769 cred_blob_it->second.GetBool()) {
2770 did_store_cred_blob = true;
2771 }
2772 }
2773
Adam Langleyf59b55602023-07-05 19:51:202774 for (const RequestExtension ext : req_state_->requested_extensions) {
zakaria ridouh15ce79e12021-09-24 20:20:142775 switch (ext) {
2776 case RequestExtension::kPRF:
2777 response->echo_prf = true;
2778 response->prf = did_create_hmac_secret;
Adam Langley5c0c8ab72023-10-10 23:06:122779 if (response_data.prf_results) {
2780 response->prf_results =
2781 PRFResultsToValues(*response_data.prf_results);
2782 }
zakaria ridouh15ce79e12021-09-24 20:20:142783 break;
2784 case RequestExtension::kHMACSecret:
2785 response->echo_hmac_create_secret = true;
2786 response->hmac_create_secret = did_create_hmac_secret;
2787 break;
2788 case RequestExtension::kCredProps:
2789 response->echo_cred_props = true;
2790 if (response_data.is_resident_key) {
2791 response->has_cred_props_rk = true;
2792 response->cred_props_rk = *response_data.is_resident_key;
2793 }
2794 break;
2795 case RequestExtension::kLargeBlobEnable:
2796 response->echo_large_blob = true;
2797 response->supports_large_blob =
Adam Langleya254f0b52023-02-14 03:38:032798 response_data.large_blob_type.has_value();
zakaria ridouh15ce79e12021-09-24 20:20:142799 break;
2800 case RequestExtension::kCredBlob:
2801 response->echo_cred_blob = true;
2802 response->cred_blob = did_store_cred_blob;
2803 break;
Adam Langley4b0ca3e22021-11-29 20:51:182804 case RequestExtension::kMinPINLength:
2805 // Ignore. The spec says[1] that there's no client (i.e. browser)
2806 // extension output (as opposed to the output in the returned
2807 // authenticator data). This may have been a mistake but it can always
2808 // be added later.
2809 // [1]
2810 // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-minpinlength-extension
2811 break;
zakaria ridouh15ce79e12021-09-24 20:20:142812 case RequestExtension::kAppID:
2813 case RequestExtension::kLargeBlobRead:
2814 case RequestExtension::kLargeBlobWrite:
2815 case RequestExtension::kGetCredBlob:
Peter Boströmfc7ddc182024-10-31 19:37:212816 NOTREACHED();
zakaria ridouh15ce79e12021-09-24 20:20:142817 }
2818 }
2819
zakaria ridouh15ce79e12021-09-24 20:20:142820 response->attestation_object =
2821 response_data.GetCBOREncodedAttestationObject();
Adam Langleybfe49b82022-09-02 00:21:462822 common_info->authenticator_data =
2823 response_data.attestation_object.authenticator_data()
2824 .SerializeToByteArray();
Adam Langley1ab57462022-08-23 03:29:002825 response->info = std::move(common_info);
zakaria ridouh15ce79e12021-09-24 20:20:142826
Adam Langleybfe49b82022-09-02 00:21:462827 const device::PublicKey* public_key =
2828 response_data.attestation_object.authenticator_data()
2829 .attested_data()
2830 ->public_key();
zakaria ridouh15ce79e12021-09-24 20:20:142831 response->public_key_algo = public_key->algorithm;
Arthur Sonzognic686e8f2024-01-11 08:36:372832 const std::optional<std::vector<uint8_t>>& public_key_der =
zakaria ridouh15ce79e12021-09-24 20:20:142833 public_key->der_bytes;
2834 if (public_key_der) {
2835 response->public_key_der.emplace(public_key_der.value());
2836 }
2837
2838 return response;
2839}
2840
Amos Lim12696e5e32022-09-16 07:37:582841void AuthenticatorCommonImpl::CompleteMakeCredentialRequest(
Manas Verma2ff0cc572019-03-19 23:42:562842 blink::mojom::AuthenticatorStatus status,
2843 blink::mojom::MakeCredentialAuthenticatorResponsePtr response,
Martin Kreichgauer6119e842022-01-28 01:52:412844 blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details,
Manas Verma2ff0cc572019-03-19 23:42:562845 Focus check_focus) {
Victor Hugo Vianna Silva00692722025-03-18 19:51:482846 DCHECK(std::holds_alternative<MakeCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:542847 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482848 std::get<MakeCredentialCallback>(req_state_->response_callback));
Adem Derinele4777b62024-09-04 07:26:542849 auto make_credential_response_callback = std::move(
Victor Hugo Vianna Silva00692722025-03-18 19:51:482850 std::get<MakeCredentialCallback>(req_state_->response_callback));
Ken Buchanand5edc0782024-06-10 22:01:222851
Adem Derinele4777b62024-09-04 07:26:542852 if (req_state_->request_result) {
Ken Buchanand5edc0782024-06-10 22:01:222853 UMA_HISTOGRAM_ENUMERATION("WebAuthentication.MakeCredential.Result",
Adem Derinele4777b62024-09-04 07:26:542854 *req_state_->request_result);
Ken Buchanand5edc0782024-06-10 22:01:222855 }
2856
Victor Hugo Vianna Silva00692722025-03-18 19:51:482857 if (std::holds_alternative<MakeCredentialOutcome>(
Adem Derinele4777b62024-09-04 07:26:542858 req_state_->request_outcome)) {
2859 RecordRegisterOutcomeMetric(
2860 req_state_->mode, GetRenderFrameHost()->GetPageUkmSourceId(),
Victor Hugo Vianna Silva00692722025-03-18 19:51:482861 std::get<MakeCredentialOutcome>(req_state_->request_outcome));
Ken Buchanan23dce912024-07-11 16:41:272862 } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
2863 RecordRegisterOutcomeMetric(req_state_->mode,
2864 GetRenderFrameHost()->GetPageUkmSourceId(),
2865 MakeCredentialOutcome::kSuccess);
2866 }
2867
Adam Langleyf59b55602023-07-05 19:51:202868 if (check_focus != Focus::kDontCheck &&
2869 !(req_state_->request_delegate && IsFocused())) {
Adem Derinele4777b62024-09-04 07:26:542870 std::move(make_credential_response_callback)
Martin Kreichgauer6119e842022-01-28 01:52:412871 .Run(blink::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr, nullptr);
Manas Verma2ff0cc572019-03-19 23:42:562872 } else {
Adem Derinele4777b62024-09-04 07:26:542873 std::move(make_credential_response_callback)
Martin Kreichgauer6119e842022-01-28 01:52:412874 .Run(status, std::move(response), std::move(dom_exception_details));
Manas Verma2ff0cc572019-03-19 23:42:562875 }
2876
2877 Cleanup();
2878}
2879
zakaria ridouh15ce79e12021-09-24 20:20:142880blink::mojom::GetAssertionAuthenticatorResponsePtr
Amos Lim12696e5e32022-09-16 07:37:582881AuthenticatorCommonImpl::CreateGetAssertionResponse(
Nina Satragno7f33f9b2023-01-31 22:09:032882 device::AuthenticatorGetAssertionResponse response_data) {
zakaria ridouh15ce79e12021-09-24 20:20:142883 auto response = blink::mojom::GetAssertionAuthenticatorResponse::New();
2884 auto common_info = blink::mojom::CommonCredentialInfo::New();
Slobodan Pejica0f2b9d2023-09-28 01:56:252885 auto response_extensions =
2886 blink::mojom::AuthenticationExtensionsClientOutputs::New();
Adam Langleyf59b55602023-07-05 19:51:202887 common_info->client_data_json.assign(req_state_->client_data_json.begin(),
2888 req_state_->client_data_json.end());
Martin Kreichgauerbece642a2021-12-07 21:02:462889 common_info->raw_id = response_data.credential->id;
Slobodan Pejic435c11ef2024-12-09 18:28:242890 common_info->id = Base64UrlEncodeOmitPadding(common_info->raw_id);
zakaria ridouh15ce79e12021-09-24 20:20:142891 response->info = std::move(common_info);
2892 response->info->authenticator_data =
2893 response_data.authenticator_data.SerializeToByteArray();
2894 response->signature = response_data.signature;
Martin Kreichgauer930f341a2022-01-07 20:20:462895 response->authenticator_attachment =
2896 response_data.transport_used
2897 ? device::AuthenticatorAttachmentFromTransport(
2898 *response_data.transport_used)
2899 : device::AuthenticatorAttachment::kAny;
zakaria ridouh15ce79e12021-09-24 20:20:142900 response_data.user_entity
2901 ? response->user_handle.emplace(response_data.user_entity->id)
2902 : response->user_handle.emplace();
2903
Adam Langleyf59b55602023-07-05 19:51:202904 for (RequestExtension ext : req_state_->requested_extensions) {
zakaria ridouh15ce79e12021-09-24 20:20:142905 switch (ext) {
2906 case RequestExtension::kAppID:
Adam Langleyf59b55602023-07-05 19:51:202907 DCHECK(req_state_->app_id);
Slobodan Pejica0f2b9d2023-09-28 01:56:252908 response_extensions->echo_appid_extension = true;
zakaria ridouh15ce79e12021-09-24 20:20:142909 if (response_data.authenticator_data.application_parameter() ==
Adam Langleyf59b55602023-07-05 19:51:202910 CreateApplicationParameter(*req_state_->app_id)) {
Slobodan Pejica0f2b9d2023-09-28 01:56:252911 response_extensions->appid_extension = true;
zakaria ridouh15ce79e12021-09-24 20:20:142912 }
2913 break;
2914 case RequestExtension::kPRF: {
Slobodan Pejica0f2b9d2023-09-28 01:56:252915 response_extensions->echo_prf = true;
Adam Langley5c0c8ab72023-10-10 23:06:122916 if (response_data.hmac_secret) {
2917 response_extensions->prf_results =
2918 PRFResultsToValues(*response_data.hmac_secret);
zakaria ridouh15ce79e12021-09-24 20:20:142919 } else {
Slobodan Pejica0f2b9d2023-09-28 01:56:252920 response_extensions->prf_not_evaluated =
2921 response_data.hmac_secret_not_evaluated;
zakaria ridouh15ce79e12021-09-24 20:20:142922 }
2923 break;
2924 }
2925 case RequestExtension::kLargeBlobRead:
Slobodan Pejica0f2b9d2023-09-28 01:56:252926 response_extensions->echo_large_blob = true;
2927 response_extensions->large_blob = response_data.large_blob;
zakaria ridouh15ce79e12021-09-24 20:20:142928 break;
2929 case RequestExtension::kLargeBlobWrite:
Slobodan Pejica0f2b9d2023-09-28 01:56:252930 response_extensions->echo_large_blob = true;
2931 response_extensions->echo_large_blob_written = true;
2932 response_extensions->large_blob_written =
2933 response_data.large_blob_written;
zakaria ridouh15ce79e12021-09-24 20:20:142934 break;
2935 case RequestExtension::kGetCredBlob: {
Arthur Sonzognic686e8f2024-01-11 08:36:372936 const std::optional<cbor::Value>& extensions =
zakaria ridouh15ce79e12021-09-24 20:20:142937 response_data.authenticator_data.extensions();
2938 if (extensions) {
2939 const cbor::Value::MapValue& map = extensions->GetMap();
2940 const auto& it = map.find(cbor::Value(device::kExtensionCredBlob));
2941 if (it != map.end() && it->second.is_bytestring()) {
Slobodan Pejica0f2b9d2023-09-28 01:56:252942 response_extensions->get_cred_blob = it->second.GetBytestring();
zakaria ridouh15ce79e12021-09-24 20:20:142943 }
2944 }
Slobodan Pejica0f2b9d2023-09-28 01:56:252945 if (!response_extensions->get_cred_blob.has_value()) {
Martin Kreichgauer40a31002022-05-02 19:43:572946 // The authenticator is supposed to return an empty byte string if it
2947 // does not have a credBlob for the credential. But in case it
2948 // doesn't, we return one to the caller anyway.
Slobodan Pejica0f2b9d2023-09-28 01:56:252949 response_extensions->get_cred_blob = std::vector<uint8_t>();
Martin Kreichgauer40a31002022-05-02 19:43:572950 }
2951
zakaria ridouh15ce79e12021-09-24 20:20:142952 break;
2953 }
2954 case RequestExtension::kHMACSecret:
2955 case RequestExtension::kCredProps:
2956 case RequestExtension::kLargeBlobEnable:
2957 case RequestExtension::kCredBlob:
Adam Langley4b0ca3e22021-11-29 20:51:182958 case RequestExtension::kMinPINLength:
Peter Boströmfc7ddc182024-10-31 19:37:212959 NOTREACHED();
zakaria ridouh15ce79e12021-09-24 20:20:142960 }
2961 }
Slobodan Pejica0f2b9d2023-09-28 01:56:252962 response->extensions = std::move(response_extensions);
zakaria ridouh15ce79e12021-09-24 20:20:142963
2964 return response;
2965}
2966
Amos Lim12696e5e32022-09-16 07:37:582967void AuthenticatorCommonImpl::CompleteGetAssertionRequest(
Manas Verma2ff0cc572019-03-19 23:42:562968 blink::mojom::AuthenticatorStatus status,
Martin Kreichgauer6119e842022-01-28 01:52:412969 blink::mojom::GetAssertionAuthenticatorResponsePtr response,
2970 blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
Victor Hugo Vianna Silva00692722025-03-18 19:51:482971 CHECK(std::holds_alternative<GetCredentialCallback>(
Adem Derinel72e11db2025-02-11 15:58:002972 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:482973 std::get<GetCredentialCallback>(req_state_->response_callback));
2974 auto get_credential_response_callback =
2975 std::move(std::get<GetCredentialCallback>(req_state_->response_callback));
Jonathan Njeunjeb769687652023-06-15 16:46:302976
Adem Derinele4777b62024-09-04 07:26:542977 if (req_state_->request_result) {
Nina Satragno129251c2023-10-23 21:50:402978 UMA_HISTOGRAM_ENUMERATION("WebAuthentication.GetAssertion.Result",
Adem Derinele4777b62024-09-04 07:26:542979 *req_state_->request_result);
Nina Satragno129251c2023-10-23 21:50:402980 }
2981
Victor Hugo Vianna Silva00692722025-03-18 19:51:482982 if (std::holds_alternative<GetAssertionOutcome>(
Adem Derinele4777b62024-09-04 07:26:542983 req_state_->request_outcome)) {
2984 RecordSignOutcomeMetric(
2985 req_state_->mode, GetRenderFrameHost()->GetPageUkmSourceId(),
Victor Hugo Vianna Silva00692722025-03-18 19:51:482986 std::get<GetAssertionOutcome>(req_state_->request_outcome));
Ken Buchanan23dce912024-07-11 16:41:272987 } else if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
2988 RecordSignOutcomeMetric(req_state_->mode,
2989 GetRenderFrameHost()->GetPageUkmSourceId(),
2990 GetAssertionOutcome::kSuccess);
2991 }
2992
Jonathan Njeunjeb769687652023-06-15 16:46:302993 if (status == blink::mojom::AuthenticatorStatus::SUCCESS) {
2994 static_cast<RenderFrameHostImpl*>(GetRenderFrameHost())
2995 ->WebAuthnAssertionRequestSucceeded();
2996 }
2997
Adem Derinel72e11db2025-02-11 15:58:002998 GetCallbackForAssertion(std::move(get_credential_response_callback))
Martin Kreichgauer6119e842022-01-28 01:52:412999 .Run(status, std::move(response), std::move(dom_exception_details));
Manas Verma2ff0cc572019-03-19 23:42:563000 Cleanup();
3001}
3002
Adem Derinel72e11db2025-02-11 15:58:003003void AuthenticatorCommonImpl::HandlePasswordResponse(
3004 password_manager::CredentialInfo credential) {
Victor Hugo Vianna Silva00692722025-03-18 19:51:483005 CHECK(std::holds_alternative<GetCredentialCallback>(
Adem Derinel72e11db2025-02-11 15:58:003006 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:483007 std::get<GetCredentialCallback>(req_state_->response_callback));
Adem Derinel72e11db2025-02-11 15:58:003008 // TODO(crbug.com/393055190): Define the required metrics and emit them.
3009 static_cast<RenderFrameHostImpl*>(GetRenderFrameHost())
3010 ->WebAuthnAssertionRequestSucceeded();
3011 auto credential_response =
3012 blink::mojom::GetCredentialResponse::NewPasswordResponse(
3013 std::move(credential));
Victor Hugo Vianna Silva00692722025-03-18 19:51:483014 std::move(std::get<GetCredentialCallback>(req_state_->response_callback))
Adem Derinel72e11db2025-02-11 15:58:003015 .Run(std::move(credential_response));
3016 Cleanup();
3017}
3018
Gabriel Viera7bc08f212024-07-10 15:42:333019void AuthenticatorCommonImpl::CompleteReportRequest(
3020 blink::mojom::AuthenticatorStatus status,
3021 blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
Adem Derinele4777b62024-09-04 07:26:543022 DCHECK(
Victor Hugo Vianna Silva00692722025-03-18 19:51:483023 std::holds_alternative<ReportCallback>(req_state_->response_callback) &&
3024 std::get<ReportCallback>(req_state_->response_callback));
3025 std::move(std::get<ReportCallback>(req_state_->response_callback))
Gabriel Viera7bc08f212024-07-10 15:42:333026 .Run(status, std::move(dom_exception_details));
3027 Cleanup();
3028}
3029
Amos Lim12696e5e32022-09-16 07:37:583030void AuthenticatorCommonImpl::Cleanup() {
Adam Langleye8ba2cc2024-08-06 13:53:233031 CHECK(!req_state_ || req_state_->request_key.value() == next_request_key_);
Adam Langleyf59b55602023-07-05 19:51:203032 req_state_.reset();
Adam Langleye8ba2cc2024-08-06 13:53:233033 next_request_key_++;
3034 CHECK(next_request_key_); // crash on overflow. Only 2^64 WebAuthn requests
3035 // per instance of this object are supported.
Manas Verma2ff0cc572019-03-19 23:42:563036}
3037
Amos Lim12696e5e32022-09-16 07:37:583038void AuthenticatorCommonImpl::DisableUI() {
Martin Kreichgauer77def30f02024-11-11 20:16:583039 // DisableUI() must be invoked before the request delegate is created, because
3040 // the delegate needs know what type of UI, if any, is shown for the request.
3041 CHECK(!req_state_ || !req_state_->request_delegate);
Manas Vermaca015f92020-01-23 23:26:353042 disable_ui_ = true;
3043}
3044
Nina Satragno8d67dec32023-04-18 22:10:443045void AuthenticatorCommonImpl::DisableTLSCheck() {
3046 disable_tls_check_ = true;
3047}
3048
Amos Lim12696e5e32022-09-16 07:37:583049RenderFrameHost* AuthenticatorCommonImpl::GetRenderFrameHost() const {
Adam Langleyb0385822021-03-19 23:34:003050 RenderFrameHost* ret = RenderFrameHost::FromID(render_frame_host_id_);
3051 DCHECK(ret);
3052 return ret;
3053}
3054
Adam Langley3ec44c22023-08-10 01:04:013055AuthenticatorRequestClientDelegate::RequestSource
3056AuthenticatorCommonImpl::RequestSource() const {
3057 if (serving_requests_for_ == ServingRequestsFor::kInternalUses) {
3058 return AuthenticatorRequestClientDelegate::RequestSource::kInternal;
3059 }
Ken Buchanan1ea549c12024-10-10 21:31:053060 if (req_state_->mode == AuthenticationRequestMode::kPayment) {
Adam Langley3ec44c22023-08-10 01:04:013061 return AuthenticatorRequestClientDelegate::RequestSource::
3062 kSecurePaymentConfirmation;
3063 }
3064 return AuthenticatorRequestClientDelegate::RequestSource::kWebAuthentication;
3065}
3066
Amos Lim12696e5e32022-09-16 07:37:583067BrowserContext* AuthenticatorCommonImpl::GetBrowserContext() const {
Adam Langleyb0385822021-03-19 23:34:003068 return GetRenderFrameHost()->GetBrowserContext();
Manas Verma2ff0cc572019-03-19 23:42:563069}
3070
Amos Lim12696e5e32022-09-16 07:37:583071device::FidoDiscoveryFactory* AuthenticatorCommonImpl::discovery_factory() {
Adam Langleyf59b55602023-07-05 19:51:203072 DCHECK(req_state_->discovery_factory);
3073 return req_state_->discovery_factory_testing_override
3074 ? req_state_->discovery_factory_testing_override.get()
3075 : req_state_->discovery_factory.get();
Martin Kreichgauer0b24720b2020-08-17 19:58:233076}
3077
Martin Kreichgauer4ce13be2022-10-28 23:20:213078void AuthenticatorCommonImpl::InitDiscoveryFactory() {
Adam Langleyf59b55602023-07-05 19:51:203079 req_state_->discovery_factory = MakeDiscoveryFactory(GetRenderFrameHost());
Martin Kreichgauer0b24720b2020-08-17 19:58:233080 // TODO(martinkr): |discovery_factory_testing_override_| is a long-lived
3081 // VirtualFidoDeviceDiscovery so that tests can maintain and alter virtual
3082 // authenticator state in between requests. We should extract a longer-lived
3083 // configuration object from VirtualFidoDeviceDiscovery, so we can simply
3084 // stick a short-lived instance into |discovery_factory_| and eliminate
3085 // |discovery_factory_testing_override_|.
Adam Langleyf59b55602023-07-05 19:51:203086 req_state_->discovery_factory_testing_override =
Adam Langley1e03fb02023-03-16 23:02:033087 AuthenticatorEnvironment::GetInstance()
Martin Kreichgauer0b24720b2020-08-17 19:58:233088 ->MaybeGetDiscoveryFactoryTestOverride();
3089}
3090
Amos Lim12696e5e32022-09-16 07:37:583091void AuthenticatorCommonImpl::EnableRequestProxyExtensionsAPISupport() {
Martin Kreichgauer8c97189a2022-01-10 20:31:433092 enable_request_proxy_api_ = true;
3093}
3094
Martin Kreichgauer165ff722021-08-26 01:33:523095WebAuthenticationRequestProxy*
Martin Kreichgauer1f4aa592023-01-06 18:39:373096AuthenticatorCommonImpl::GetWebAuthnRequestProxyIfActive(
3097 const url::Origin& caller_origin) {
3098 DCHECK(!caller_origin.opaque());
Martin Kreichgauerca18b432023-04-25 18:58:473099 // The Virtual Authenticator, which can be activated via Dev Tools UI or
3100 // ChromeDriver, should take precedence over request proxying. Otherwise
3101 // attaching a remote desktop session would interfere with automated or manual
3102 // testing.
3103 const bool virtual_authenticator_active =
3104 AuthenticatorEnvironment::GetInstance()
3105 ->MaybeGetVirtualAuthenticatorManager(
3106 static_cast<RenderFrameHostImpl*>(GetRenderFrameHost())
3107 ->frame_tree_node()) != nullptr;
3108 if (!enable_request_proxy_api_ || virtual_authenticator_active) {
Martin Kreichgauer8c97189a2022-01-10 20:31:433109 return nullptr;
3110 }
Martin Kreichgauer1f4aa592023-01-06 18:39:373111 return GetWebAuthenticationDelegate()->MaybeGetRequestProxy(
3112 GetBrowserContext(), caller_origin);
Martin Kreichgauer165ff722021-08-26 01:33:523113}
3114
Amos Lim12696e5e32022-09-16 07:37:583115void AuthenticatorCommonImpl::OnMakeCredentialProxyResponse(
Adam Langleye8ba2cc2024-08-06 13:53:233116 RequestKey request_key,
Martin Kreichgauerb27f6312022-01-25 00:03:323117 WebAuthenticationRequestProxy::RequestId request_id,
Martin Kreichgauer6119e842022-01-28 01:52:413118 blink::mojom::WebAuthnDOMExceptionDetailsPtr error,
Martin Kreichgauer8c97189a2022-01-10 20:31:433119 blink::mojom::MakeCredentialAuthenticatorResponsePtr response) {
Adam Langleye8ba2cc2024-08-06 13:53:233120 if (!CheckRequestKey(request_key)) {
3121 return;
3122 }
Adam Langleyf59b55602023-07-05 19:51:203123 DCHECK_EQ(*req_state_->pending_proxied_request_id, request_id);
Victor Hugo Vianna Silva00692722025-03-18 19:51:483124 DCHECK(std::holds_alternative<MakeCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:543125 req_state_->response_callback) &&
Victor Hugo Vianna Silva00692722025-03-18 19:51:483126 std::get<MakeCredentialCallback>(req_state_->response_callback));
Adam Langleyf59b55602023-07-05 19:51:203127 req_state_->pending_proxied_request_id.reset();
Martin Kreichgauer6119e842022-01-28 01:52:413128 if (error) {
3129 DCHECK(!response);
3130 CompleteMakeCredentialRequest(
3131 blink::mojom::AuthenticatorStatus::ERROR_WITH_DOM_EXCEPTION_DETAILS,
3132 nullptr, std::move(error), Focus::kDoCheck);
3133 return;
3134 }
3135 CompleteMakeCredentialRequest(blink::mojom::AuthenticatorStatus::SUCCESS,
3136 std::move(response), nullptr, Focus::kDoCheck);
Martin Kreichgauer8c97189a2022-01-10 20:31:433137}
3138
Amos Lim12696e5e32022-09-16 07:37:583139void AuthenticatorCommonImpl::OnGetAssertionProxyResponse(
Adam Langleye8ba2cc2024-08-06 13:53:233140 RequestKey request_key,
Martin Kreichgauer1beaff02022-02-02 18:58:423141 WebAuthenticationRequestProxy::RequestId request_id,
3142 blink::mojom::WebAuthnDOMExceptionDetailsPtr error,
3143 blink::mojom::GetAssertionAuthenticatorResponsePtr response) {
Adam Langleye8ba2cc2024-08-06 13:53:233144 if (!CheckRequestKey(request_key)) {
3145 return;
3146 }
Adam Langleyf59b55602023-07-05 19:51:203147 DCHECK_EQ(*req_state_->pending_proxied_request_id, request_id);
Victor Hugo Vianna Silva00692722025-03-18 19:51:483148 DCHECK(std::holds_alternative<GetCredentialCallback>(
Adem Derinele4777b62024-09-04 07:26:543149 req_state_->response_callback));
Adam Langleyf59b55602023-07-05 19:51:203150 req_state_->pending_proxied_request_id.reset();
Martin Kreichgauer1beaff02022-02-02 18:58:423151 if (error) {
3152 DCHECK(!response);
3153 CompleteGetAssertionRequest(
3154 blink::mojom::AuthenticatorStatus::ERROR_WITH_DOM_EXCEPTION_DETAILS,
3155 nullptr, std::move(error));
3156 return;
3157 }
3158 CompleteGetAssertionRequest(blink::mojom::AuthenticatorStatus::SUCCESS,
3159 std::move(response));
3160}
3161
Ken Buchananbe8629f2025-01-11 03:37:163162void AuthenticatorCommonImpl::UpdateChallengeFromUrl(
3163 ClientDataJsonParams params,
3164 std::optional<base::span<const uint8_t>> challenge) {
3165 // ChallengeUrl is only valid for GetAssertion requests.
Victor Hugo Vianna Silva00692722025-03-18 19:51:483166 CHECK(std::holds_alternative<device::CtapGetAssertionRequest>(
Ken Buchananbe8629f2025-01-11 03:37:163167 req_state_->ctap_request));
3168
3169 if (!challenge) {
3170 // TODO(https://crbug.com/381219428): This might warrant a more specific
3171 // error being returned to the RP. Also this should have its own logging
3172 // value when it is no longer a prototype.
3173 req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
3174 SignalFailureToRequestDelegate(
3175 AuthenticatorRequestClientDelegate::InterestingFailureReason::
3176 kChallengeUrlFailure,
3177 blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
3178 return;
3179 }
3180
Elly450ad9fe2025-07-09 17:13:223181 params.challenge = base::ToVector(*challenge);
Ken Buchananbe8629f2025-01-11 03:37:163182 req_state_->client_data_json = BuildClientDataJson(std::move(params));
Victor Hugo Vianna Silva00692722025-03-18 19:51:483183 std::get<device::CtapGetAssertionRequest>(req_state_->ctap_request)
Ken Buchananbe8629f2025-01-11 03:37:163184 .SetClientDataJson(req_state_->client_data_json);
3185 reinterpret_cast<device::GetAssertionRequestHandler*>(
3186 req_state_->request_handler.get())
3187 ->ProvideClientDataJson(req_state_->client_data_json);
3188}
3189
Adam Langleye8ba2cc2024-08-06 13:53:233190AuthenticatorCommonImpl::RequestKey AuthenticatorCommonImpl::GetRequestKey() {
3191 return req_state_->request_key;
3192}
3193
3194bool AuthenticatorCommonImpl::CheckRequestKey(RequestKey request_key) {
3195 return req_state_.get() && req_state_->request_key == request_key;
3196}
3197
Manas Verma2ff0cc572019-03-19 23:42:563198} // namespace content