0

[webauthn] Only disable BFCache during request

Before this CL, Chrome disabled the back forward cache for the lifetime
of the page whenever any WebAuthn related function was called by the
page. After this CL, Chrome will only disable the cache if the page is
navigated away during a get assertion or make credential request
instead.

Bug: 41492758
Change-Id: I7452a2eb9e4f8d95b81c09b8e476fdb58b11fa29
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6081700
Commit-Queue: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: Scott Haseley <shaseley@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Fergal Daly <fergal@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1407939}
This commit is contained in:
Nina Satragno
2025-01-17 08:15:02 -08:00
committed by Chromium LUCI CQ
parent d0ef9f115c
commit e8cffbbac7
14 changed files with 320 additions and 18 deletions

@ -7,6 +7,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/back_forward_cache_browsertest.h"
#include "content/browser/browser_interface_binders.h"
#include "content/browser/generic_sensor/frame_sensor_provider_proxy.h"
#include "content/browser/generic_sensor/web_contents_sensor_provider_proxy.h"
#include "content/browser/presentation/presentation_test_utils.h"
@ -41,6 +42,7 @@
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
#include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "ui/base/idle/idle_time_provider.h"
#include "ui/base/test/idle_test_utils.h"
@ -3367,8 +3369,9 @@ void RegisterServiceWorker(RenderFrameHostImpl* rfh) {
// Returns a unique script for each request, to test service worker update.
std::unique_ptr<net::test_server::HttpResponse> RequestHandlerForUpdateWorker(
const net::test_server::HttpRequest& request) {
if (request.relative_url != "/back_forward_cache/service-worker.js")
if (request.relative_url != "/back_forward_cache/service-worker.js") {
return nullptr;
}
static int counter = 0;
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
@ -4959,6 +4962,210 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, WebLocksNotCached) {
{}, {}, {}, FROM_HERE);
}
enum TestAuthenticatorBehavior {
kErrorOut,
kStallRequest,
};
// An implementation of blink::mojom::Authenticator that stalls all requests.
class TestAuthenticator : public blink::mojom::Authenticator {
public:
explicit TestAuthenticator(TestAuthenticatorBehavior behavior)
: behavior_(behavior) {
OverrideAuthenticatorBinderForTesting(base::BindRepeating(
&TestAuthenticator::BindAuthenticator, base::Unretained(this)));
}
~TestAuthenticator() override {
OverrideVibrationManagerBinderForTesting(base::NullCallback());
}
void BindAuthenticator(
mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
receiver_.Bind(std::move(receiver));
}
private:
// blink::mojom::Authenticator:
void MakeCredential(
blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
MakeCredentialCallback callback) override {
if (behavior_ == kStallRequest) {
pending_make_credential_callback_ = std::move(callback);
} else {
std::move(callback).Run(blink::mojom::AuthenticatorStatus::ABORT_ERROR,
nullptr, nullptr);
}
}
void GetAssertion(blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
GetAssertionCallback callback) override {
if (behavior_ == kStallRequest) {
pending_get_assertion_callback_ = std::move(callback);
} else {
std::move(callback).Run(blink::mojom::AuthenticatorStatus::ABORT_ERROR,
nullptr, nullptr);
}
}
void GetClientCapabilities(GetClientCapabilitiesCallback callback) override {}
void Report(blink::mojom::PublicKeyCredentialReportOptionsPtr options,
ReportCallback callback) override {}
void IsUserVerifyingPlatformAuthenticatorAvailable(
IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) override {
}
void IsConditionalMediationAvailable(
IsConditionalMediationAvailableCallback callback) override {}
void Cancel() override {}
MakeCredentialCallback pending_make_credential_callback_;
GetAssertionCallback pending_get_assertion_callback_;
TestAuthenticatorBehavior behavior_;
mojo::Receiver<blink::mojom::Authenticator> receiver_{this};
};
// Tests that an ongoing WebAuthn get assertion request disables BFcache.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
WebAuthnGetAssertion_NoCachingDuringRequest) {
TestAuthenticator test_authenticator(kStallRequest);
ASSERT_TRUE(CreateHttpsServer()->Start());
GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
// 1) Navigate to A.
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
// Leave a WebAuthn get assertion request pending.
EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
navigator.credentials.get({ publicKey: {
challenge: new TextEncoder().encode("speedrun a game"),
userVerification: "discouraged",
allowCredentials: [{type: "public-key", id: Uint8Array.from([1, 2, 3])}],
}});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// 2) Navigate to B.
ASSERT_TRUE(NavigateToURL(shell(), url_b));
// - Page A should not be in the cache.
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
// 3) Go back.
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored(
{NotRestoredReason::kBlocklistedFeatures},
{blink::scheduler::WebSchedulerTrackedFeature::kWebAuthentication}, {},
{}, {}, FROM_HERE);
}
// Tests that after a WebAuthn get assertion request completes, BFcache is not
// disabled.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
WebAuthnGetAssertion_CacheAfterRequest) {
TestAuthenticator test_authenticator(kErrorOut);
ASSERT_TRUE(CreateHttpsServer()->Start());
GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
// 1) Navigate to A.
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
// Complete a WebAuthn get assertion request.
EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
navigator.credentials.get({ publicKey: {
challenge: new TextEncoder().encode("speedrun a game"),
userVerification: "discouraged",
allowCredentials: [{type: "public-key", id: Uint8Array.from([1, 2, 3])}],
}}).catch(() => {});
)"));
// 2) Navigate to B.
ASSERT_TRUE(NavigateToURL(shell(), url_b));
// 3) Go back.
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
}
// Tests that an ongoing WebAuthn make credential request disables BFcache.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
WebAuthnMakeCredential_NoCachingDuringRequest) {
TestAuthenticator test_authenticator(kStallRequest);
ASSERT_TRUE(CreateHttpsServer()->Start());
GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
// 1) Navigate to A.
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
// Leave a WebAuthn make credential request pending.
EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
navigator.credentials.create({ publicKey: {
challenge: new TextEncoder().encode("speedrun a game"),
userVerification: "discouraged",
rp: { name: "Acme"},
user: {
id: new TextEncoder().encode("1234"),
name: "fox",
displayName: "Fox McCloud"
},
pubKeyCredParams: [{ type: "public-key", alg: -7}],
}});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
// 2) Navigate to B.
ASSERT_TRUE(NavigateToURL(shell(), url_b));
// - Page A should not be in the cache.
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
// 3) Go back.
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored(
{NotRestoredReason::kBlocklistedFeatures},
{blink::scheduler::WebSchedulerTrackedFeature::kWebAuthentication}, {},
{}, {}, FROM_HERE);
}
// Tests that after a WebAuthn make credential request completes, BFcache is not
// disabled.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
WebAuthnMakeCredential_CacheAfterRequest) {
TestAuthenticator test_authenticator(kErrorOut);
ASSERT_TRUE(CreateHttpsServer()->Start());
GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
// 1) Navigate to A.
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
// Leave a WebAuthn make credential request pending.
EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
navigator.credentials.create({ publicKey: {
challenge: new TextEncoder().encode("speedrun a game"),
userVerification: "discouraged",
rp: { name: "Acme"},
user: {
id: new TextEncoder().encode("1234"),
name: "fox",
displayName: "Fox McCloud"
},
pubKeyCredParams: [{ type: "public-key", alg: -7}],
}}).catch(() => {});
)"));
// 2) Navigate to B.
ASSERT_TRUE(NavigateToURL(shell(), url_b));
// 3) Go back.
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
}
// TODO(crbug.com/40937711): Reenable. This is flaky because we block on
// the permission request, not on API usage.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DISABLED_WebMidiNotCached) {

@ -702,6 +702,22 @@ void BindVibrationManager(
}
}
AuthenticatorBinder& GetAuthenticatorBinderOverride() {
static base::NoDestructor<AuthenticatorBinder> binder;
return *binder;
}
void BindAuthenticator(
RenderFrameHostImpl* frame,
mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
const auto& binder = GetAuthenticatorBinderOverride();
if (binder) {
binder.Run(std::move(receiver));
} else {
frame->GetWebAuthenticationService(std::move(receiver));
}
}
void BindMediaPlayerObserverClientHandler(
RenderFrameHost* frame_host,
mojo::PendingReceiver<media::mojom::MediaPlayerObserverClient> receiver) {
@ -936,8 +952,7 @@ void PopulateFrameBinders(RenderFrameHostImpl* host, mojo::BinderMap* map) {
base::Unretained(host)));
map->Add<blink::mojom::Authenticator>(
base::BindRepeating(&RenderFrameHostImpl::GetWebAuthenticationService,
base::Unretained(host)));
base::BindRepeating(&BindAuthenticator, base::Unretained(host)));
map->Add<payments::mojom::PaymentCredential>(base::BindRepeating(
&RenderFrameHostImpl::CreatePaymentCredential, base::Unretained(host)));
@ -1795,4 +1810,8 @@ void OverrideVibrationManagerBinderForTesting(VibrationManagerBinder binder) {
internal::GetVibrationManagerBinderOverride() = std::move(binder);
}
void OverrideAuthenticatorBinderForTesting(AuthenticatorBinder binder) {
internal::GetAuthenticatorBinderOverride() = std::move(binder);
}
} // namespace content

@ -10,6 +10,7 @@
#include "mojo/public/cpp/bindings/binder_map.h"
#include "services/device/public/mojom/battery_monitor.mojom-forward.h"
#include "services/device/public/mojom/vibration_manager.mojom-forward.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
#include "url/origin.h"
namespace content {
@ -84,6 +85,12 @@ using VibrationManagerBinder = base::RepeatingCallback<void(
CONTENT_EXPORT void OverrideVibrationManagerBinderForTesting(
VibrationManagerBinder binder);
// Allows tests to override how frame hosts bind Authenticator receivers.
using AuthenticatorBinder = base::RepeatingCallback<void(
mojo::PendingReceiver<blink::mojom::Authenticator>)>;
CONTENT_EXPORT void OverrideAuthenticatorBinderForTesting(
AuthenticatorBinder binder);
} // namespace content
#endif // CONTENT_BROWSER_BROWSER_INTERFACE_BINDERS_H_

@ -1885,6 +1885,9 @@ Page::BackForwardCacheNotRestoredReason BlocklistedFeatureToProtocol(
return Page::BackForwardCacheNotRestoredReasonEnum::UnloadHandler;
case WebSchedulerTrackedFeature::kParserAborted:
return Page::BackForwardCacheNotRestoredReasonEnum::ParserAborted;
case WebSchedulerTrackedFeature::kWebAuthentication:
return Page::BackForwardCacheNotRestoredReasonEnum::
ContentWebAuthenticationAPI;
}
}
@ -2104,6 +2107,7 @@ Page::BackForwardCacheNotRestoredReasonType MapBlocklistedFeatureToType(
case WebSchedulerTrackedFeature::kWebLocks:
case WebSchedulerTrackedFeature::kWebSocket:
case WebSchedulerTrackedFeature::kKeepaliveRequest:
case WebSchedulerTrackedFeature::kWebAuthentication:
return Page::BackForwardCacheNotRestoredReasonTypeEnum::SupportPending;
case WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoStore:
case WebSchedulerTrackedFeature::kMainResourceHasCacheControlNoCache:

@ -200,6 +200,7 @@ WebSchedulerTrackedFeatures GetDisallowedWebSchedulerTrackedFeatures() {
WebSchedulerTrackedFeature::kSharedWorker,
WebSchedulerTrackedFeature::kSpeechRecognizer,
WebSchedulerTrackedFeature::kUnloadHandler,
WebSchedulerTrackedFeature::kWebAuthentication,
WebSchedulerTrackedFeature::kWebDatabase,
WebSchedulerTrackedFeature::kWebHID,
WebSchedulerTrackedFeature::kWebLocks,

@ -767,10 +767,12 @@ AuthenticatorCommonImpl::AuthenticatorCommonImpl(
// Disable the back-forward cache for any document that makes WebAuthn
// requests. Pages using privacy-sensitive APIs are generally exempt from
// back-forward cache for now as a precaution.
BackForwardCache::DisableForRenderFrameHost(
render_frame_host,
BackForwardCacheDisable::DisabledReason(
BackForwardCacheDisable::DisabledReasonId::kWebAuthenticationAPI));
if (!base::FeatureList::IsEnabled(device::kWebAuthnNewBfCacheHandling)) {
BackForwardCache::DisableForRenderFrameHost(
render_frame_host,
BackForwardCacheDisable::DisabledReason(
BackForwardCacheDisable::DisabledReasonId::kWebAuthenticationAPI));
}
}
AuthenticatorCommonImpl::~AuthenticatorCommonImpl() = default;

@ -54,9 +54,11 @@
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/did_commit_navigation_interceptor.h"
#include "content/test/fake_network_url_loader_factory.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/features.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/fido_transport_protocol.h"
@ -663,6 +665,12 @@ class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase {
protected:
void SetUpOnMainThread() override {
WebAuthBrowserTestBase::SetUpOnMainThread();
// The renderer would disable bfcache during the lifetime of a request.
// Since we don't have a renderer here and some of the navigation tests
// depend on bfcache being disabled, do so manually.
content::BackForwardCache::DisableForRenderFrameHost(
shell()->web_contents()->GetPrimaryMainFrame(),
RenderFrameHostDisabledForTestingReason());
ConnectToAuthenticator();
}
@ -1860,8 +1868,15 @@ IN_PROC_BROWSER_TEST_F(WebAuthCrossDomainTest, Get) {
class WebAuthLocalClientBackForwardCacheBrowserTest
: public WebAuthLocalClientBrowserTest {
public:
WebAuthLocalClientBackForwardCacheBrowserTest() {
scoped_feature_list_.InitAndDisableFeature(
device::kWebAuthnNewBfCacheHandling);
}
protected:
BackForwardCacheDisabledTester tester_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBackForwardCacheBrowserTest,

@ -403,7 +403,9 @@ void SetRuntimeFeaturesFromChromiumFeatures() {
{"ExperimentalMachineLearningNeuralNetwork",
raw_ref(webnn::mojom::features::
kExperimentalWebMachineLearningNeuralNetwork),
kSetOnlyIfOverridden}};
kSetOnlyIfOverridden},
{"WebAuthenticationNewBfCacheHandlingBlink",
raw_ref(device::kWebAuthnNewBfCacheHandling)}};
for (const auto& mapping : runtimeFeatureNameToChromiumFeatureMapping) {
SetRuntimeFeatureFromChromiumFeature(
*mapping.chromium_feature, mapping.option, [&mapping](bool enabled) {

@ -177,4 +177,9 @@ BASE_FEATURE(kWebAuthnEnclaveAttestation,
"WebAuthenticationEnclaveAttestation",
base::FEATURE_DISABLED_BY_DEFAULT);
// Default enabled in M134. Remove in or after M137.
BASE_FEATURE(kWebAuthnNewBfCacheHandling,
"WebAuthenticationNewBfCacheHandling",
base::FEATURE_ENABLED_BY_DEFAULT);
} // namespace device

@ -144,6 +144,13 @@ BASE_DECLARE_FEATURE(kWebAuthnNeverSkipTrustThisComputer);
COMPONENT_EXPORT(DEVICE_FIDO)
BASE_DECLARE_FEATURE(kWebAuthnEnclaveAttestation);
// With this flag, WebAuthn only disables the back-forward cache during the
// lifetime of a WebAuthn request.
// With the flag off, the back-forward cache is disabled for the lifetime of
// the page when a request is started.
COMPONENT_EXPORT(DEVICE_FIDO)
BASE_DECLARE_FEATURE(kWebAuthnNewBfCacheHandling);
} // namespace device
#endif // DEVICE_FIDO_FEATURES_H_

@ -152,6 +152,8 @@ FeatureNames FeatureToNames(WebSchedulerTrackedFeature feature) {
return {"parser-aborted", "parser was aborted"};
case WebSchedulerTrackedFeature::kWebBluetooth:
return {"webbluetooth", "Active Bluetooth connection"};
case WebSchedulerTrackedFeature::kWebAuthentication:
return {"webauthn", "Active WebAuthn transaction"};
}
return {};
}

@ -167,10 +167,13 @@ enum class WebSchedulerTrackedFeature : uint32_t {
// connection.
kWebBluetooth = 69,
// The back/forward cache is disabled during WebAuthn transactions.
kWebAuthentication = 70,
// Please keep in sync with WebSchedulerTrackedFeature in
// tools/metrics/histograms/enums.xml. These values should not be renumbered.
kMaxValue = kWebBluetooth,
kMaxValue = kWebAuthentication,
};
using WebSchedulerTrackedFeatures =

@ -575,6 +575,7 @@ AuthenticationExtensionsPRFValues* GetPRFExtensionResults(
void OnMakePublicKeyCredentialComplete(
std::unique_ptr<ScopedPromiseResolver> scoped_resolver,
std::unique_ptr<ScopedAbortState> scoped_abort_state,
FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle feature_handle,
RequiredOriginType required_origin_type,
bool is_rk_required,
AuthenticatorStatus status,
@ -686,6 +687,7 @@ bool IsForPayment(const CredentialCreationOptions* options,
void OnSaveCredentialIdForPaymentExtension(
std::unique_ptr<ScopedPromiseResolver> scoped_resolver,
std::unique_ptr<ScopedAbortState> scoped_abort_state,
FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle feature_handle,
MakeCredentialAuthenticatorResponsePtr credential,
PaymentCredentialStorageStatus storage_status) {
auto status = AuthenticatorStatus::SUCCESS;
@ -696,6 +698,7 @@ void OnSaveCredentialIdForPaymentExtension(
}
OnMakePublicKeyCredentialComplete(
std::move(scoped_resolver), std::move(scoped_abort_state),
std::move(feature_handle),
RequiredOriginType::kSecureWithPaymentOrCreateCredentialPermissionPolicy,
/*is_rk_required=*/false, status, std::move(credential),
/*dom_exception_details=*/nullptr);
@ -704,6 +707,7 @@ void OnSaveCredentialIdForPaymentExtension(
void OnMakePublicKeyCredentialWithPaymentExtensionComplete(
std::unique_ptr<ScopedPromiseResolver> scoped_resolver,
std::unique_ptr<ScopedAbortState> scoped_abort_state,
FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle feature_handle,
const String& rp_id_for_payment_extension,
const WTF::Vector<uint8_t>& user_id_for_payment_extension,
AuthenticatorStatus status,
@ -739,12 +743,14 @@ void OnMakePublicKeyCredentialWithPaymentExtensionComplete(
std::move(user_id_for_payment_extension),
WTF::BindOnce(&OnSaveCredentialIdForPaymentExtension,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state), std::move(credential)));
std::move(scoped_abort_state), std::move(feature_handle),
std::move(credential)));
}
void OnGetAssertionComplete(
std::unique_ptr<ScopedPromiseResolver> scoped_resolver,
std::unique_ptr<ScopedAbortState> scoped_abort_state,
FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle feature_handle,
bool is_conditional_ui_request,
AuthenticatorStatus status,
GetAssertionAuthenticatorResponsePtr credential,
@ -1502,10 +1508,19 @@ ScriptPromise<IDLNullable<Credential>> AuthenticationCredentialsContainer::get(
CredentialManagerProxy::From(script_state)->Authenticator();
authenticator->GetAssertion(
std::move(mojo_options),
WTF::BindOnce(&OnGetAssertionComplete,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state),
is_conditional_ui_request));
WTF::BindOnce(
&OnGetAssertionComplete,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state),
RuntimeEnabledFeatures::
WebAuthenticationNewBfCacheHandlingBlinkEnabled()
? ExecutionContext::From(script_state)
->GetScheduler()
->RegisterFeature(
SchedulingPolicy::Feature::kWebAuthentication,
SchedulingPolicy::DisableBackForwardCache())
: FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle(),
is_conditional_ui_request));
} else {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError,
@ -1955,6 +1970,13 @@ AuthenticationCredentialsContainer::create(
auto* authenticator =
CredentialManagerProxy::From(script_state)->Authenticator();
FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle feature_handle =
RuntimeEnabledFeatures::WebAuthenticationNewBfCacheHandlingBlinkEnabled()
? ExecutionContext::From(script_state)
->GetScheduler()
->RegisterFeature(SchedulingPolicy::Feature::kWebAuthentication,
SchedulingPolicy::DisableBackForwardCache())
: FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle();
if (mojo_options->is_payment_credential_creation) {
String rp_id_for_payment_extension = mojo_options->relying_party->id;
WTF::Vector<uint8_t> user_id_for_payment_extension = mojo_options->user->id;
@ -1968,7 +1990,7 @@ AuthenticationCredentialsContainer::create(
WTF::BindOnce(&OnMakePublicKeyCredentialWithPaymentExtensionComplete,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state),
rp_id_for_payment_extension,
std::move(feature_handle), rp_id_for_payment_extension,
std::move(user_id_for_payment_extension)));
} else {
authenticator->MakeCredential(
@ -1976,7 +1998,7 @@ AuthenticationCredentialsContainer::create(
WTF::BindOnce(&OnMakePublicKeyCredentialWithPaymentExtensionComplete,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state),
rp_id_for_payment_extension,
std::move(feature_handle), rp_id_for_payment_extension,
std::move(user_id_for_payment_extension)));
}
} else {
@ -1987,8 +2009,8 @@ AuthenticationCredentialsContainer::create(
std::move(mojo_options),
WTF::BindOnce(&OnMakePublicKeyCredentialComplete,
std::make_unique<ScopedPromiseResolver>(resolver),
std::move(scoped_abort_state), required_origin_type,
is_rk_required));
std::move(scoped_abort_state), std::move(feature_handle),
required_origin_type, is_rk_required));
}
return promise;

@ -4743,6 +4743,12 @@
name: "WebAuthenticationLargeBlobExtension",
status: "stable",
},
{
name: "WebAuthenticationNewBfCacheHandlingBlink",
// No status because this blink runtime feature doesn't work by itself.
// It's controlled by the corresponding Chromium feature which needs to
// be enabled to make the whole feature work.
},
// https://w3c.github.io/webauthn/#prf-extension
{
name: "WebAuthenticationPRF",