0

[webauthn] Conditional mediation availability

Implement isConditionalMediationAvailable() on all desktop platforms.
Also implement a bunch of IWYU suggestions.

Bug: 1330946
Change-Id: Ica4a6fbd04efcd5529be7125ad771b576bbe1329
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3792649
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Commit-Queue: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1031639}
This commit is contained in:
Nina Satragno
2022-08-04 22:43:00 +00:00
committed by Chromium LUCI CQ
parent 1a8cc8bc75
commit c3444e8f8a
26 changed files with 238 additions and 49 deletions
chrome/android/javatests/src/org/chromium/chrome/browser/webauth
components/webauthn/android/java/src/org/chromium/components/webauthn
content
device/fido/win
third_party/blink
public
mojom
use_counter
webauthn
renderer
web_tests
external
flag-specific
disable-layout-ng
disable-site-isolation-trials
http
tests
credentialmanagement
platform
tools/metrics/histograms

@ -15,6 +15,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
@ -36,7 +37,8 @@ import org.chromium.net.test.ServerCertificate;
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1",
"enable-experimental-web-platform-features", "enable-features=WebAuthentication",
"ignore-certificate-errors"})
"ignore-certificate-errors", "enable-features=WebAuthenticationConditionalUI"})
@Batch(Batch.PER_CLASS)
public class AuthenticatorTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@ -84,6 +86,7 @@ public class AuthenticatorTest {
mUpdateWaiter = new AuthenticatorUpdateWaiter();
TestThreadUtils.runOnUiThreadBlocking(() -> mTab.addObserver(mUpdateWaiter));
mMockCredentialRequest = new MockFido2CredentialRequest();
AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mMockCredentialRequest);
}
@After
@ -104,7 +107,6 @@ public class AuthenticatorTest {
@Feature({"WebAuth"})
public void testCreatePublicKeyCredential() throws Exception {
mActivityTestRule.loadUrl(mUrl);
AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mMockCredentialRequest);
mActivityTestRule.runJavaScriptCodeInCurrentTab("doCreatePublicKeyCredential()");
Assert.assertEquals("Success", mUpdateWaiter.waitForUpdate());
}
@ -121,7 +123,6 @@ public class AuthenticatorTest {
@Feature({"WebAuth"})
public void testGetPublicKeyCredential() throws Exception {
mActivityTestRule.loadUrl(mUrl);
AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mMockCredentialRequest);
mActivityTestRule.runJavaScriptCodeInCurrentTab("doGetPublicKeyCredential()");
Assert.assertEquals("Success", mUpdateWaiter.waitForUpdate());
}
@ -137,9 +138,22 @@ public class AuthenticatorTest {
@Feature({"WebAuth"})
public void testIsUserVerifyingPlatformAuthenticatorAvailable() throws Exception {
mActivityTestRule.loadUrl(mUrl);
AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mMockCredentialRequest);
mActivityTestRule.runJavaScriptCodeInCurrentTab(
"doIsUserVerifyingPlatformAuthenticatorAvailable()");
Assert.assertEquals("Success", mUpdateWaiter.waitForUpdate());
}
/**
* Verify that the Mojo bridge between Blink and Java is working for
* PublicKeyCredential.isConditionalMediationAvailable.
*/
@Test
@DisableIf.Build(sdk_is_less_than = 24)
@MediumTest
@Feature({"WebAuth"})
public void testIsConditionalMediationAvailable() throws Exception {
mActivityTestRule.loadUrl(mUrl);
mActivityTestRule.runJavaScriptCodeInCurrentTab("doIsConditionalMediationAvailable()");
Assert.assertEquals("Success", mUpdateWaiter.waitForUpdate());
}
}

@ -189,6 +189,25 @@ public final class AuthenticatorImpl implements Authenticator {
isUvpaa -> onIsUserVerifyingPlatformAuthenticatorAvailableResponse(isUvpaa));
}
@Override
public void isConditionalMediationAvailable(
final IsConditionalMediationAvailable_Response callback) {
if (mGmsCorePackageVersion < GMSCORE_MIN_VERSION) {
callback.call(false);
return;
}
// The WebAuthenticationConditionalUI feature will only be enabled on Android when gmscore
// supports silent discovery. If the gmscore and chromium versions are out of sync for some
// reason, this method will return true but chrome will ignore conditional requests.
// Android surfaces only platform credentials on conditional requests, use IsUVPAA as a
// proxy for availability.
mIsUserVerifyingPlatformAuthenticatorAvailableCallbackQueue.add(callback);
getFido2CredentialRequest().handleIsUserVerifyingPlatformAuthenticatorAvailableRequest(
mRenderFrameHost,
isUvpaa -> onIsUserVerifyingPlatformAuthenticatorAvailableResponse(isUvpaa));
}
@Override
public void cancel() {
// Not implemented, ignored because request sent to gmscore fido cannot be cancelled.

@ -13,38 +13,27 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/rand_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/browser/bad_message.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/webauth/authenticator_environment_impl.h"
#include "content/browser/webauth/client_data_json.h"
#include "content/browser/webauth/is_uvpaa.h"
#include "content/browser/webauth/virtual_authenticator_manager_impl.h"
#include "content/browser/webauth/virtual_fido_discovery_factory.h"
#include "content/browser/webauth/webauth_request_security_checker.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "crypto/sha2.h"
#include "device/base/features.h"
#include "device/fido/attestation_statement.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/features.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
@ -56,15 +45,11 @@
#include "device/fido/public_key.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/public_key_credential_params.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cert/asn1_util.h"
#include "net/der/input.h"
#include "net/der/parse_values.h"
#include "net/der/parser.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/url_constants.h"
#include "url/url_util.h"
#if BUILDFLAG(IS_MAC)
#include "device/fido/mac/authenticator.h"
@ -73,6 +58,16 @@
#if BUILDFLAG(IS_CHROMEOS)
#include "device/fido/cros/authenticator.h"
#include "device/fido/features.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "device/fido/features.h"
#include "device/fido/win/authenticator.h"
#endif
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#include "content/browser/webauth/is_uvpaa.h"
#endif
namespace content {
@ -1159,6 +1154,30 @@ void AuthenticatorCommon::IsUserVerifyingPlatformAuthenticatorAvailable(
#endif
}
void AuthenticatorCommon::IsConditionalMediationAvailable(
blink::mojom::Authenticator::IsConditionalMediationAvailableCallback
callback) {
// Conditional mediation is always supported if the virtual environment is
// providing a platform authenticator.
absl::optional<bool> embedder_isuvpaa_override =
GetWebAuthenticationDelegate()
->IsUserVerifyingPlatformAuthenticatorAvailableOverride(
GetRenderFrameHost());
if (embedder_isuvpaa_override.has_value()) {
std::move(callback).Run(*embedder_isuvpaa_override);
return;
}
#if BUILDFLAG(IS_MAC)
std::move(callback).Run(true);
#elif BUILDFLAG(IS_WIN)
device::WinWebAuthnApiAuthenticator::IsConditionalMediationAvailable(
AuthenticatorEnvironmentImpl::GetInstance()->win_webauthn_api(),
std::move(callback));
#else
std::move(callback).Run(false);
#endif
}
void AuthenticatorCommon::Cancel() {
CancelWithStatus(blink::mojom::AuthenticatorStatus::ABORT_ERROR);
}

@ -13,7 +13,6 @@
#include <vector>
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h"
@ -22,11 +21,8 @@
#include "content/public/browser/web_authentication_request_proxy.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/make_credential_request_handler.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@ -89,6 +85,9 @@ class CONTENT_EXPORT AuthenticatorCommon {
void IsUserVerifyingPlatformAuthenticatorAvailable(
blink::mojom::Authenticator::
IsUserVerifyingPlatformAuthenticatorAvailableCallback callback);
void IsConditionalMediationAvailable(
blink::mojom::Authenticator::IsConditionalMediationAvailableCallback
callback);
void Cancel();
void Cleanup();

@ -71,6 +71,11 @@ void AuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailable(
std::move(callback));
}
void AuthenticatorImpl::IsConditionalMediationAvailable(
IsConditionalMediationAvailableCallback callback) {
authenticator_common_->IsConditionalMediationAvailable(std::move(callback));
}
void AuthenticatorImpl::Cancel() {
authenticator_common_->Cancel();
}

@ -57,6 +57,8 @@ class CONTENT_EXPORT AuthenticatorImpl
GetAssertionCallback callback) override;
void IsUserVerifyingPlatformAuthenticatorAvailable(
IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) override;
void IsConditionalMediationAvailable(
IsConditionalMediationAvailableCallback callback) override;
void Cancel() override;
std::unique_ptr<AuthenticatorCommon> authenticator_common_;

@ -97,6 +97,30 @@
window.document.title = 'Fail: ' + e.name + " " + e.message;
});
}
function doIsConditionalMediationAvailable() {
if (window.PublicKeyCredential === undefined) {
window.document.title = 'Fail: PublicKeyCredential === undefined';
return;
}
if (window.PublicKeyCredential.isConditionalMediationAvailable === undefined) {
window.document.title = 'Fail: isConditionalMediationAvailable() === undefined';
return;
}
PublicKeyCredential.isConditionalMediationAvailable()
.then(r => {
if (r == false) {
window.document.title = 'Success';
} else {
window.document.title = 'Fail: expected response to be false, got ' + r + ' instead';
}
})
.catch(e => {
window.document.title = 'Fail: ' + e.name + " " + e.message;
});
}
</script>
</head>
<body>

@ -44,6 +44,7 @@ struct PlatformCredentialListDeleter {
} // namespace
// static
void WinWebAuthnApiAuthenticator::IsUserVerifyingPlatformAuthenticatorAvailable(
WinWebAuthnApi* api,
base::OnceCallback<void(bool is_available)> callback) {
@ -63,6 +64,23 @@ void WinWebAuthnApiAuthenticator::IsUserVerifyingPlatformAuthenticatorAvailable(
std::move(callback));
}
// static
void WinWebAuthnApiAuthenticator::IsConditionalMediationAvailable(
WinWebAuthnApi* api,
base::OnceCallback<void(bool is_available)> callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::TaskPriority::USER_VISIBLE, base::MayBlock(),
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(
[](WinWebAuthnApi* api) {
return api && api->IsAvailable() && api->SupportsSilentDiscovery();
},
api),
std::move(callback));
}
// static
void WinWebAuthnApiAuthenticator::EnumeratePlatformCredentials(
WinWebAuthnApi* api,
base::OnceCallback<
@ -101,6 +119,7 @@ void WinWebAuthnApiAuthenticator::EnumeratePlatformCredentials(
std::move(callback));
}
// static
void WinWebAuthnApiAuthenticator::DeletePlatformCredential(
WinWebAuthnApi* api,
base::span<const uint8_t> credential_id,

@ -37,6 +37,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticator
WinWebAuthnApi* api,
base::OnceCallback<void(bool is_available)>);
// This method is safe to call without checking WinWebAuthnApi::IsAvailable().
// Returns false if |api| is nullptr.
static void IsConditionalMediationAvailable(
WinWebAuthnApi* api,
base::OnceCallback<void(bool is_available)>);
// Get metadata for all credentials in the platform authenticator. If such
// metadata is not available then the callback will be invoked with an empty
// list.

@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/public_key_credential_rp_entity.h"
@ -188,5 +189,21 @@ TEST_F(WinAuthenticatorTest, EnumeratePlatformCredentials_Supported) {
EXPECT_EQ(cred.user.display_name, kUserDisplayName);
}
TEST_F(WinAuthenticatorTest, IsConditionalMediationAvailable) {
for (bool silent_discovery : {false, true}) {
SCOPED_TRACE(silent_discovery);
fake_webauthn_api_->set_supports_silent_discovery(silent_discovery);
test::TestCallbackReceiver<bool> callback;
base::RunLoop run_loop;
WinWebAuthnApiAuthenticator::IsConditionalMediationAvailable(
fake_webauthn_api_.get(),
base::BindLambdaForTesting([&](bool is_available) {
EXPECT_EQ(is_available, silent_discovery);
run_loop.Quit();
}));
run_loop.Run();
}
}
} // namespace
} // namespace device

@ -3636,6 +3636,7 @@ enum WebFeature {
kPersistentQuotaType = 4315,
kCrossOriginScrollIntoView = 4316,
kLinkRelCanonical = 4317,
kCredentialManagerIsConditionalMediationAvailable = 4318,
// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots.

@ -587,6 +587,12 @@ interface Authenticator {
// credential using a user-verifying platform authenticator.
IsUserVerifyingPlatformAuthenticatorAvailable() => (bool available);
// Returns true if Conditional Mediation is available. Relying Parties can use
// this method to determine whether they can pass "conditional" to the
// "mediation" parameter of a webauthn get call and have the browser autofill
// webauthn credentials on form inputs.
IsConditionalMediationAvailable() => (bool available);
// Cancel an ongoing MakeCredential or GetAssertion request
// Only one MakeCredential or GetAssertion call at a time is allowed,
// any future calls are cancelled

@ -7,17 +7,12 @@
#include <memory>
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
#include "build/build_config.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/blink/public/common/sms/webotp_constants.h"
#include "third_party/blink/public/common/sms/webotp_service_outcome.h"
#include "third_party/blink/public/mojom/credentialmanagement/credential_manager.mojom-blink.h"
#include "third_party/blink/public/mojom/payments/payment_credential.mojom-blink.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
#include "third_party/blink/public/mojom/sms/webotp_service.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@ -35,7 +30,6 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_provider.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_otp_credential_request_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_payment_credential_instrument.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_creation_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_parameters.h"
@ -44,12 +38,10 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_user_entity.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_htmlformelement_passwordcredentialdata.h"
#include "third_party/blink/renderer/core/dom/abort_signal.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/frame.h"
#include "third_party/blink/renderer/core/frame/frame_console.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/navigator.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
@ -60,7 +52,7 @@
#include "third_party/blink/renderer/modules/credentialmanagement/authenticator_attestation_response.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.h" // IWYU pragma: keep
#include "third_party/blink/renderer/modules/credentialmanagement/federated_credential.h"
#include "third_party/blink/renderer/modules/credentialmanagement/identity_credential.h"
#include "third_party/blink/renderer/modules/credentialmanagement/otp_credential.h"
@ -72,7 +64,6 @@
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@ -1129,8 +1120,9 @@ ScriptPromise CredentialsContainer::get(ScriptState* script_state,
WTF::Bind(&AbortPublicKeyRequest, WrapPersistent(script_state)));
}
bool is_conditional_ui_request = conditionalMediationSupported() &&
options->mediation() == "conditional";
bool is_conditional_ui_request =
RuntimeEnabledFeatures::WebAuthenticationConditionalUIEnabled() &&
options->mediation() == "conditional";
if (is_conditional_ui_request &&
options->publicKey()->hasAllowCredentials() &&
!options->publicKey()->allowCredentials().IsEmpty()) {
@ -1683,10 +1675,6 @@ ScriptPromise CredentialsContainer::preventSilentAccess(
return promise;
}
bool CredentialsContainer::conditionalMediationSupported() {
return RuntimeEnabledFeatures::WebAuthenticationConditionalUIEnabled();
}
void CredentialsContainer::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
Supplement<Navigator>::Trace(visitor);

@ -38,7 +38,6 @@ class MODULES_EXPORT CredentialsContainer final : public ScriptWrappable,
const CredentialCreationOptions*,
ExceptionState&);
ScriptPromise preventSilentAccess(ScriptState*);
bool conditionalMediationSupported();
void Trace(Visitor*) const override;
};

@ -10,5 +10,4 @@ interface CredentialsContainer {
[CallWith=ScriptState, MeasureAs=CredentialManagerStore] Promise<Credential> store(Credential credential);
[CallWith=ScriptState, RaisesException, MeasureAs=CredentialManagerCreate] Promise<Credential?> create(optional CredentialCreationOptions options = {});
[CallWith=ScriptState, MeasureAs=CredentialManagerPreventSilentAccess] Promise<void> preventSilentAccess();
[RuntimeEnabled=WebAuthenticationConditionalUI] readonly attribute boolean conditionalMediationSupported;
};

@ -9,7 +9,6 @@
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom-shared.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential_manager_proxy.h"
#include "third_party/blink/renderer/modules/credentialmanagement/scoped_promise_resolver.h"
@ -56,6 +55,7 @@ PublicKeyCredential::PublicKeyCredential(
AuthenticatorAttachmentToString(authenticator_attachment)),
extension_outputs_(extension_outputs) {}
// static
ScriptPromise
PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
ScriptState* script_state) {
@ -89,6 +89,31 @@ PublicKeyCredential::getClientExtensionResults() const {
extension_outputs_.Get());
}
// static
ScriptPromise PublicKeyCredential::isConditionalMediationAvailable(
ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// Ignore calls if the current realm execution context is no longer valid,
// e.g., because the responsible document was detached.
DCHECK(resolver->GetExecutionContext());
if (resolver->GetExecutionContext()->IsContextDestroyed()) {
resolver->Reject();
return promise;
}
UseCounter::Count(
resolver->GetExecutionContext(),
WebFeature::kCredentialManagerIsConditionalMediationAvailable);
auto* authenticator =
CredentialManagerProxy::From(script_state)->Authenticator();
authenticator->IsConditionalMediationAvailable(
WTF::Bind([](std::unique_ptr<ScopedPromiseResolver> resolver,
bool available) { resolver->Release()->Resolve(available); },
std::make_unique<ScopedPromiseResolver>(resolver)));
return promise;
}
void PublicKeyCredential::Trace(Visitor* visitor) const {
visitor->Trace(raw_id_);
visitor->Trace(response_);

@ -44,6 +44,7 @@ class MODULES_EXPORT PublicKeyCredential : public Credential {
static ScriptPromise isUserVerifyingPlatformAuthenticatorAvailable(
ScriptState*);
AuthenticationExtensionsClientOutputs* getClientExtensionResults() const;
static ScriptPromise isConditionalMediationAvailable(ScriptState*);
// Credential:
void Trace(Visitor*) const override;

@ -14,4 +14,5 @@
[SameObject] readonly attribute DOMString? authenticatorAttachment;
[CallWith=ScriptState] static Promise<boolean> isUserVerifyingPlatformAuthenticatorAvailable();
AuthenticationExtensionsClientOutputs getClientExtensionResults();
[RuntimeEnabled=WebAuthenticationConditionalUI, CallWith=ScriptState] static Promise<boolean> isConditionalMediationAvailable();
};

@ -2532,9 +2532,11 @@
},
// A more subtle UI for WebAuthn that is web exposed. Under development.
// It's controlled by the corresponding Chromium feature which needs to
// be enabled to make the whole feature work.
// be enabled to make the whole feature work, except for WPTs where the
// virtual authenticator environment is used.
{
name: "WebAuthenticationConditionalUI",
status: "test",
},
{
name: "WebAuthenticationLargeBlobExtension",

@ -0,0 +1,37 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Conditional Mediation tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src=helpers.js></script>
<body></body>
<script>
"use strict";
// Test that a configuration that must support conditional mediation reports
// supporting it.
virtualAuthenticatorPromiseTest(async t => {
assert_own_property(window.PublicKeyCredential, "isConditionalMediationAvailable");
assert_true(await window.PublicKeyCredential.isConditionalMediationAvailable());
}, {
protocol: "ctap2",
hasResidentKey: true,
hasUserVerification: true,
transport: "internal",
}, "Conditional mediation supported");
// Test that a configuration that cannot possibly support conditional mediation
// does not report supporting it.
virtualAuthenticatorPromiseTest(async t => {
assert_own_property(window.PublicKeyCredential, "isConditionalMediationAvailable");
assert_false(await window.PublicKeyCredential.isConditionalMediationAvailable());
}, {
protocol: "ctap2",
hasResidentKey: false,
hasUserVerification: false,
transport: "nfc",
}, "Conditional mediation not supported");
</script>

@ -1,5 +1,5 @@
This is a testharness.js-based test.
Found 78 tests; 74 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 78 tests; 75 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup
PASS idl_test validation
PASS Partial dictionary CredentialCreationOptions: original dictionary defined
@ -46,7 +46,7 @@ PASS PublicKeyCredential interface: attribute rawId
PASS PublicKeyCredential interface: attribute response
PASS PublicKeyCredential interface: attribute authenticatorAttachment
PASS PublicKeyCredential interface: operation getClientExtensionResults()
FAIL PublicKeyCredential interface: operation isConditionalMediationAvailable() assert_own_property: interface object missing static operation expected property "isConditionalMediationAvailable" missing
PASS PublicKeyCredential interface: operation isConditionalMediationAvailable()
FAIL PublicKeyCredential interface: operation toJSON() assert_own_property: interface prototype object missing non-static operation expected property "toJSON" missing
PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
FAIL PublicKeyCredential interface: operation parseCreationOptionsFromJSON(PublicKeyCredentialCreationOptionsJSON) assert_own_property: interface object missing static operation expected property "parseCreationOptionsFromJSON" missing

@ -1,5 +1,5 @@
This is a testharness.js-based test.
Found 78 tests; 74 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 78 tests; 75 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup
PASS idl_test validation
PASS Partial dictionary CredentialCreationOptions: original dictionary defined
@ -46,7 +46,7 @@ PASS PublicKeyCredential interface: attribute rawId
PASS PublicKeyCredential interface: attribute response
PASS PublicKeyCredential interface: attribute authenticatorAttachment
PASS PublicKeyCredential interface: operation getClientExtensionResults()
FAIL PublicKeyCredential interface: operation isConditionalMediationAvailable() assert_own_property: interface object missing static operation expected property "isConditionalMediationAvailable" missing
PASS PublicKeyCredential interface: operation isConditionalMediationAvailable()
FAIL PublicKeyCredential interface: operation toJSON() assert_own_property: interface prototype object missing non-static operation expected property "toJSON" missing
PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
FAIL PublicKeyCredential interface: operation parseCreationOptionsFromJSON(PublicKeyCredentialCreationOptionsJSON) assert_own_property: interface object missing static operation expected property "parseCreationOptionsFromJSON" missing

@ -140,6 +140,10 @@ export class MockAuthenticator {
return false;
}
async isConditionalMediationAvailable() {
return false;
}
async cancel() {}
// Resets state of mock Authenticator.

@ -1,5 +1,5 @@
This is a testharness.js-based test.
Found 78 tests; 74 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 78 tests; 75 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup
PASS idl_test validation
PASS Partial dictionary CredentialCreationOptions: original dictionary defined
@ -46,7 +46,7 @@ PASS PublicKeyCredential interface: attribute rawId
PASS PublicKeyCredential interface: attribute response
PASS PublicKeyCredential interface: attribute authenticatorAttachment
PASS PublicKeyCredential interface: operation getClientExtensionResults()
FAIL PublicKeyCredential interface: operation isConditionalMediationAvailable() assert_own_property: interface object missing static operation expected property "isConditionalMediationAvailable" missing
PASS PublicKeyCredential interface: operation isConditionalMediationAvailable()
FAIL PublicKeyCredential interface: operation toJSON() assert_own_property: interface prototype object missing non-static operation expected property "toJSON" missing
PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
FAIL PublicKeyCredential interface: operation parseCreationOptionsFromJSON(PublicKeyCredentialCreationOptionsJSON) assert_own_property: interface object missing static operation expected property "parseCreationOptionsFromJSON" missing

@ -6730,6 +6730,7 @@ interface PromiseRejectionEvent : Event
getter reason
method constructor
interface PublicKeyCredential : Credential
static method isConditionalMediationAvailable
static method isUserVerifyingPlatformAuthenticatorAvailable
attribute @@toStringTag
getter authenticatorAttachment

@ -40936,6 +40936,7 @@ Called by update_use_counter_feature_enum.py.-->
<int value="4315" label="PersistentQuotaType"/>
<int value="4316" label="CrossOriginScrollIntoView"/>
<int value="4317" label="LinkRelCanonical"/>
<int value="4318" label="CredentialManagerIsConditionalMediationAvailable"/>
</enum>
<enum name="FeaturePolicyAllowlistType">