Add setting to enable Credman for passkeys: enable CredentialManager APIs for A14+ (#2296 #329)

thanks to jhult
This commit is contained in:
Carmelo Messina
2025-10-09 11:45:09 +02:00
parent 873bc4ea61
commit 6fa2e7b98f
3 changed files with 511 additions and 285 deletions
+1
View File
@@ -304,6 +304,7 @@ Android-Pixel-Perfect-Mode.patch
Set-caret-blink-interval-to-default.patch
Set-the-screen-frame-rate-to-60-Hz.patch
Disable-Device-Attributes-API.patch
Add-setting-to-enable-Credman-for-passkeys.patch
Temp-disable-UseContextSnapshot.patch
# temporary or wip patches
@@ -0,0 +1,336 @@
From: uazo <uazo@users.noreply.github.com>
Date: Thu, 9 Oct 2025 09:11:01 +0000
Subject: Add setting to enable Credman for passkeys
Enables Credential Manager for passkey management on A14+ devices via a switch
in the settings. By default, the feature is disabled.
In other versions of Android, webauthn appears to be enabled on websites but
always returns a timeout error as per specifications.
Conditional immediate support is disabled.
License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
---
.../settings/PasswordSettings.java | 27 +++++++++++++++++
...etting-to-enable-Credman-for-passkeys.grdp | 12 ++++++++
.../webauthn/AuthenticatorImpl.java | 29 +++++++------------
.../components/webauthn/WebauthnFeatures.java | 2 ++
.../cred_man/CredManSupportProvider.java | 27 +++++------------
.../webauthn/android/webauthn_feature_map.cc | 1 +
components/webauthn/features.cc | 4 +++
components/webauthn/features.h | 3 ++
device/fido/features.cc | 2 ++
.../authentication_credentials_container.cc | 5 ++++
10 files changed, 75 insertions(+), 37 deletions(-)
create mode 100644 chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Add-setting-to-enable-Credman-for-passkeys.grdp
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -10,6 +10,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
@@ -64,6 +65,10 @@ import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.lifetime.ApplicationLifetime;
+import org.chromium.chrome.browser.flags.CromiteNativeUtils;
+import org.chromium.components.webauthn.WebauthnFeatureMap;
+import org.chromium.components.webauthn.WebauthnFeatures;
+
/**
* The "Passwords" screen in Settings, which allows the user to enable or disable password saving,
* to view saved passwords (just the username and URL), and to delete saved passwords.
@@ -322,6 +327,7 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
createSavePasswordsSwitch();
createAutoSignInCheckbox();
createEnableAndroidAutofillSwitch();
+ createCredManSwitch();
PasswordManagerHandlerProvider.getForProfile(getProfile())
.getPasswordManagerHandler()
@@ -673,6 +679,27 @@ public class PasswordSettings extends ChromeBaseSettingsFragment
getPrefService().getBoolean(Pref.CREDENTIALS_ENABLE_AUTOSIGNIN));
}
+ private void createCredManSwitch() {
+ ChromeSwitchPreference credManSwitch =
+ new ChromeSwitchPreference(getStyledContext(), null);
+ credManSwitch.setTitle(R.string.enable_android_credman_title);
+ credManSwitch.setOrder(0);
+ credManSwitch.setSummary(R.string.enable_android_credman_summary);
+ credManSwitch.setChecked(
+ WebauthnFeatureMap.getInstance().isEnabled(WebauthnFeatures.WEBAUTHN_ANDROID_PASSKEY));
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ credManSwitch.setSummary(R.string.enable_android_credman_disabled_summary);
+ credManSwitch.setEnabled(false);
+ }
+ credManSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
+ CromiteNativeUtils.setFlagEnabled("WebAuthenticationAndroidPasskey", (boolean)newValue);
+ if (!mSnackbarManagerSupplier.get().isShowing())
+ mSnackbarManagerSupplier.get().showSnackbar(mSnackbar);
+ return true;
+ });
+ getPreferenceScreen().addPreference(credManSwitch);
+ }
+
private void displayManageAccountLink() {
SyncService syncService = SyncServiceFactory.getForProfile(getProfile());
if (syncService == null || !syncService.isEngineInitialized()) {
diff --git a/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Add-setting-to-enable-Credman-for-passkeys.grdp b/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Add-setting-to-enable-Credman-for-passkeys.grdp
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Add-setting-to-enable-Credman-for-passkeys.grdp
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <message name="IDS_ENABLE_ANDROID_CREDMAN_TITLE" desc="" formatter_data="android_java">
+ Enable Android Credential Manager
+ </message>
+ <message name="IDS_ENABLE_ANDROID_CREDMAN_SUMMARY" desc="" formatter_data="android_java">
+ Enable support for Android Credential Manager
+ </message>
+ <message name="IDS_ENABLE_ANDROID_CREDMAN_DISABLED_SUMMARY" desc="" formatter_data="android_java">
+ Not supported on your device (require A14+)
+ </message>
+</grit-part>
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
@@ -34,6 +34,8 @@ import org.chromium.blink.mojom.PublicKeyCredentialReportOptions;
import org.chromium.blink.mojom.WebAuthnClientCapability;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import org.chromium.components.webauthn.CredManSupport;
+import org.chromium.components.webauthn.cred_man.CredManSupportProvider;
import org.chromium.components.ukm.UkmRecorder;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents;
@@ -169,8 +171,7 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
mIsPaymentRequest = options.isPaymentCredentialCreation;
mMakeCredentialCallback = callback;
mIsOperationPending = true;
- if (!GmsCoreUtils.isWebauthnSupported()
- || (!isChrome(mWebContents) && !GmsCoreUtils.isResultReceiverSupported())) {
+ if (CredManSupportProvider.getCredManSupport() == CredManSupport.DISABLED) {
recordOutcomeEvent(MakeCredentialOutcome.OTHER_FAILURE);
onError(AuthenticatorStatus.NOT_IMPLEMENTED);
return;
@@ -234,8 +235,7 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
mIsConditionalRequest = options.mediation == Mediation.CONDITIONAL;
mIsImmediateRequest = options.mediation == Mediation.IMMEDIATE;
- if (!GmsCoreUtils.isWebauthnSupported()
- || (!isChrome(mWebContents) && !GmsCoreUtils.isResultReceiverSupported())
+ if (CredManSupportProvider.getCredManSupport() == CredManSupport.DISABLED
|| options.publicKey == null) {
recordOutcomeEvent(GetAssertionOutcome.OTHER_FAILURE);
onError(AuthenticatorStatus.NOT_IMPLEMENTED);
@@ -260,12 +260,11 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
}
private boolean couldSupportConditionalMediation() {
- return GmsCoreUtils.isWebauthnSupported() && isChrome(mWebContents);
+ return isChrome(mWebContents);
}
private boolean couldSupportUvpaa() {
- return GmsCoreUtils.isWebauthnSupported()
- && (isChrome(mWebContents) || GmsCoreUtils.isResultReceiverSupported());
+ return isChrome(mWebContents);
}
@Override
@@ -329,26 +328,20 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
capabilities.add(
createWebAuthnClientCapability(
AuthenticatorConstants.CAPABILITY_CONDITIONAL_GET,
- couldSupportConditionalMediation() && isUvpaa));
+ true));
capabilities.add(
createWebAuthnClientCapability(
AuthenticatorConstants.CAPABILITY_UVPAA,
- couldSupportUvpaa() && isUvpaa));
- boolean conditionalCreateEnabled =
- couldSupportConditionalMediation()
- && DeviceFeatureMap.isEnabled(
- DeviceFeatureList.WEBAUTHN_PASSKEY_UPGRADE);
+ true));
capabilities.add(
createWebAuthnClientCapability(
AuthenticatorConstants.CAPABILITY_CONDITIONAL_CREATE,
- isUvpaa && conditionalCreateEnabled));
+ DeviceFeatureMap.isEnabled(
+ DeviceFeatureList.WEBAUTHN_PASSKEY_UPGRADE)));
capabilities.add(
createWebAuthnClientCapability(
AuthenticatorConstants.CAPABILITY_IMMEDIATE_GET,
- DeviceFeatureMap.isEnabled(
- DeviceFeatureList
- .WEBAUTHN_IMMEDIATE_GET)
- && isUvpaa));
+ false));
callback.call(capabilities.toArray(new WebAuthnClientCapability[0]));
});
}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/WebauthnFeatures.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/WebauthnFeatures.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/WebauthnFeatures.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/WebauthnFeatures.java
@@ -16,4 +16,6 @@ import org.chromium.build.annotations.NullMarked;
public abstract class WebauthnFeatures {
public static final String WEBAUTHN_ANDROID_PASSKEY_CACHE_MIGRATION =
"WebAuthenticationAndroidPasskeyCacheMigration";
+ public static final String WEBAUTHN_ANDROID_PASSKEY =
+ "WebAuthenticationAndroidPasskey";
}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManSupportProvider.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManSupportProvider.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManSupportProvider.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManSupportProvider.java
@@ -19,9 +19,11 @@ import org.chromium.base.version_info.VersionInfo;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.components.webauthn.CredManSupport;
-import org.chromium.components.webauthn.GmsCoreUtils;
import org.chromium.components.webauthn.WebauthnMode;
import org.chromium.components.webauthn.WebauthnModeProvider;
+import org.chromium.components.webauthn.WebauthnFeatureMap;
+import org.chromium.components.webauthn.WebauthnFeatures;
+import org.chromium.components.webauthn.WebauthnLogger;
@NullMarked
public class CredManSupportProvider {
@@ -57,12 +59,12 @@ public class CredManSupportProvider {
}
if (getAndroidVersion() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
sCredManSupport = CredManSupport.DISABLED;
- log(TAG, "Disabled because of Android version.");
+ WebauthnLogger.logError(TAG, "Disabled because of Android version.");
return sCredManSupport;
}
- if (notSkippedBecauseInTests() && hasOldGmsVersion()) {
+ if (!WebauthnFeatureMap.getInstance().isEnabled(WebauthnFeatures.WEBAUTHN_ANDROID_PASSKEY)) {
sCredManSupport = CredManSupport.DISABLED;
- log(TAG, "Disabled because of old GMS version.");
+ WebauthnLogger.logError(TAG, "Disabled because of user.");
return sCredManSupport;
}
if (notSkippedBecauseInTests()
@@ -70,14 +72,14 @@ public class CredManSupportProvider {
== null) {
sCredManSupport = CredManSupport.DISABLED;
recordCredManAvailability(/*available*/ false);
- log(TAG, "Disabled because CredentialManager is not available.");
+ WebauthnLogger.logError(TAG, "Disabled because CredentialManager is not available.");
return sCredManSupport;
}
recordCredManAvailability(/*available*/ true);
final CredManUiRecommender recommender =
ServiceLoaderUtil.maybeCreate(CredManUiRecommender.class);
- boolean customUiRecommended = recommender != null && recommender.recommendsCustomUi();
+ boolean customUiRecommended = true;
boolean gpmInCredMan =
sOverrideForcesGpm != null ? sOverrideForcesGpm : customUiRecommended;
boolean isChrome3pPwmMode =
@@ -116,13 +118,6 @@ public class CredManSupportProvider {
"WebAuthentication.Android.CredManAvailability", available);
}
- private static boolean hasOldGmsVersion() {
- assert sOverrideAndroidVersion == null : "Don't use in testing!";
- // The check works for unavailable and low GMS versions. `getGmsCoreVersion()` is -1 if the
- // GMS version can't be retrieved. Chrome assumes an insufficient GMS availability then.
- return GmsCoreUtils.getGmsCoreVersion() < getMinGmsVersionForCurrentChannel();
- }
-
private static int getAndroidVersion() {
return sOverrideAndroidVersion == null ? Build.VERSION.SDK_INT : sOverrideAndroidVersion;
}
@@ -130,10 +125,4 @@ public class CredManSupportProvider {
private static boolean notSkippedBecauseInTests() {
return sOverrideForcesGpm == null && sOverrideAndroidVersion == null;
}
-
- private static int getMinGmsVersionForCurrentChannel() {
- return (VersionInfo.isBetaBuild() || VersionInfo.isStableBuild())
- ? GMSCORE_MIN_VERSION_BETA_STABLE
- : GMSCORE_MIN_VERSION_CANARY_DEV;
- }
}
diff --git a/components/webauthn/android/webauthn_feature_map.cc b/components/webauthn/android/webauthn_feature_map.cc
--- a/components/webauthn/android/webauthn_feature_map.cc
+++ b/components/webauthn/android/webauthn_feature_map.cc
@@ -16,6 +16,7 @@ namespace {
// Array of features exposed through the Java WebauthnFeatureMap API.
const base::Feature* const kFeaturesExposedToJava[] = {
&kWebAuthnAndroidPasskeyCacheMigration,
+ &kWebAuthnAndroidPasskey,
};
// static
diff --git a/components/webauthn/features.cc b/components/webauthn/features.cc
--- a/components/webauthn/features.cc
+++ b/components/webauthn/features.cc
@@ -16,6 +16,10 @@ BASE_FEATURE(kWebAuthnAndroidPasskeyCacheMigration,
"WebAuthenticationAndroidPasskeyCacheMigration",
base::FEATURE_DISABLED_BY_DEFAULT);
+CROMITE_FEATURE(kWebAuthnAndroidPasskey,
+ "WebAuthenticationAndroidPasskey",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
#endif // BUILDFLAG(IS_ANDROID)
} // namespace webauthn::features
diff --git a/components/webauthn/features.h b/components/webauthn/features.h
--- a/components/webauthn/features.h
+++ b/components/webauthn/features.h
@@ -20,6 +20,9 @@ namespace webauthn::features {
COMPONENT_EXPORT(WEBAUTHN)
BASE_DECLARE_FEATURE(kWebAuthnAndroidPasskeyCacheMigration);
+COMPONENT_EXPORT(WEBAUTHN)
+BASE_DECLARE_FEATURE(kWebAuthnAndroidPasskey);
+
#endif // BUILDFLAG(IS_ANDROID)
} // namespace webauthn::features
diff --git a/device/fido/features.cc b/device/fido/features.cc
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -163,6 +163,7 @@ BASE_FEATURE(kWebAuthnImmediateGet,
#else
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_ANDROID)
+SET_CROMITE_FEATURE_DISABLED(kWebAuthnImmediateGet);
BASE_FEATURE_PARAM(int,
kWebAuthnImmediateMediationTimeoutMilliseconds,
@@ -175,6 +176,7 @@ BASE_FEATURE_PARAM(int,
BASE_FEATURE(kWebAuthnImmediateGetAutoselect,
"WebAuthenticationImmediateGetAutoselect",
base::FEATURE_ENABLED_BY_DEFAULT);
+SET_CROMITE_FEATURE_DISABLED(kWebAuthnImmediateGetAutoselect);
#if BUILDFLAG(IS_MAC)
// Default enabled in M139. Remove in or after M142.
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
--- a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
@@ -1057,6 +1057,11 @@ DOMException* AuthenticatorStatusToDOMException(
const WebAuthnDOMExceptionDetailsPtr& dom_exception_details) {
DCHECK_EQ(status != AuthenticatorStatus::ERROR_WITH_DOM_EXCEPTION_DETAILS,
dom_exception_details.is_null());
+ if (status == AuthenticatorStatus::UNKNOWN_ERROR ||
+ status == AuthenticatorStatus::NOT_IMPLEMENTED) {
+ status = AuthenticatorStatus::NOT_ALLOWED_ERROR;
+ base::PlatformThread::Sleep(base::Milliseconds(base::RandInt(1000, 6000)));
+ }
switch (status) {
case AuthenticatorStatus::SUCCESS:
NOTREACHED();
--
@@ -90,14 +90,13 @@ License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
components/omnibox/common/BUILD.gn | 1 -
.../components/omnibox/OmniboxFeatures.java | 8 -
components/signin/public/android/BUILD.gn | 3 -
components/webauthn/android/BUILD.gn | 18 +-
.../webauthn/AuthenticatorImpl.java | 97 +--
components/webauthn/android/BUILD.gn | 12 +-
.../webauthn/ConditionalUiState.java | 15 +
.../webauthn/Fido2CredentialRequest.java | 107 +--
.../webauthn/GmsCoreGetCredentialsHelper.java | 78 +-
.../components/webauthn/GmsCoreUtils.java | 31 +-
.../webauthn/IdentityCredentialsHelper.java | 78 +-
.../webauthn/WebauthnModeProvider.java | 15 -
.../webauthn/cred_man/CredManHelper.java | 24 +-
.../cred_man/CredManMetricsHelper.java | 17 +-
.../push_messaging/push_messaging_manager.cc | 2 +-
content/public/android/BUILD.gn | 4 -
@@ -112,7 +111,7 @@ License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
.../gms/ChromiumPlayServicesAvailability.java | 10 +-
third_party/androidx/customizations.gni | 14 +-
third_party/cardboard/BUILD.gn | 4 -
88 files changed, 137 insertions(+), 2618 deletions(-)
87 files changed, 123 insertions(+), 2612 deletions(-)
create mode 100644 components/webauthn/android/java/src/org/chromium/components/webauthn/ConditionalUiState.java
diff --git a/android_webview/expectations/system_webview_bundle.AndroidManifest.expected b/android_webview/expectations/system_webview_bundle.AndroidManifest.expected
@@ -3254,27 +3253,16 @@ diff --git a/components/signin/public/android/BUILD.gn b/components/signin/publi
diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/BUILD.gn
--- a/components/webauthn/android/BUILD.gn
+++ b/components/webauthn/android/BUILD.gn
@@ -7,8 +7,6 @@ import("//third_party/jni_zero/jni_zero.gni")
generate_jni("jni_headers") {
sources = [
- "java/src/org/chromium/components/webauthn/Fido2Api.java",
- "java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java",
"java/src/org/chromium/components/webauthn/InternalAuthenticator.java",
"java/src/org/chromium/components/webauthn/WebauthnBrowserBridge.java",
"java/src/org/chromium/components/webauthn/WebauthnFeatureMap.java",
@@ -40,10 +38,6 @@ android_library("java") {
"java/src/org/chromium/components/webauthn/AuthenticatorImpl.java",
@@ -41,8 +41,6 @@ android_library("java") {
"java/src/org/chromium/components/webauthn/Barrier.java",
"java/src/org/chromium/components/webauthn/CreateConfirmationUiDelegate.java",
- "java/src/org/chromium/components/webauthn/Fido2Api.java",
"java/src/org/chromium/components/webauthn/Fido2Api.java",
- "java/src/org/chromium/components/webauthn/Fido2ApiCall.java",
- "java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java",
- "java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java",
"java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java",
"java/src/org/chromium/components/webauthn/FidoIntentSender.java",
"java/src/org/chromium/components/webauthn/GetCredentialResponseCallback.java",
"java/src/org/chromium/components/webauthn/GetMatchingCredentialIdsResponseCallback.java",
@@ -73,11 +67,11 @@ android_library("java") {
@@ -73,11 +71,11 @@ android_library("java") {
"java/src/org/chromium/components/webauthn/cred_man/GpmCredManRequestDecorator.java",
]
@@ -3290,16 +3278,7 @@ diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/
"//base:base_java",
"//base:service_loader_java",
"//base/version_info/android:version_constants_java",
@@ -148,8 +142,6 @@ android_library("test_support_java") {
source_set("android") {
sources = [
"cred_man_support.h",
- "fido2api_native_android.cc",
- "fido2credentialrequest_native_android.cc",
"internal_authenticator_android.cc",
"internal_authenticator_android.h",
"webauthn_browser_bridge.cc",
@@ -223,8 +215,6 @@ robolectric_library("junit") {
@@ -223,8 +221,6 @@ robolectric_library("junit") {
deps = [
":java",
":test_support_java",
@@ -3308,193 +3287,6 @@ diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
@@ -76,19 +76,11 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
private @Nullable MakeCredential_Response mMakeCredentialCallback;
private @Nullable GetCredential_Response mGetCredentialCallback;
- private @Nullable Fido2CredentialRequest mPendingFido2CredentialRequest;
- private final Set<Fido2CredentialRequest> mUnclosedFido2CredentialRequests = new HashSet<>();
-
// Information about the request cached here for metric reporting purposes.
private boolean mIsConditionalRequest;
private boolean mIsPaymentRequest;
private boolean mIsImmediateRequest;
- // StaticFieldLeak complains that this is a memory leak because
- // `Fido2CredentialRequest` contains a `Context`. But this field is only
- // used in tests so a memory leak is irrelevent.
- @SuppressLint("StaticFieldLeak")
- private static @Nullable Fido2CredentialRequest sFido2CredentialRequestOverrideForTesting;
/**
* Builds the Authenticator service implementation.
@@ -124,19 +116,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
mCreateConfirmationUiDelegate = createConfirmationUiDelegate;
}
- public static void overrideFido2CredentialRequestForTesting(Fido2CredentialRequest request) {
- sFido2CredentialRequestOverrideForTesting = request;
- }
-
- private Fido2CredentialRequest getFido2CredentialRequest() {
- if (sFido2CredentialRequestOverrideForTesting != null) {
- return sFido2CredentialRequestOverrideForTesting;
- }
- Fido2CredentialRequest request = new Fido2CredentialRequest(this);
- mUnclosedFido2CredentialRequests.add(request);
- return request;
- }
-
/**
* Called by InternalAuthenticatorAndroid, which facilitates WebAuthn for processes that
* originate from the browser process. Since the request is from the browser process, the
@@ -196,16 +175,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
private void continueMakeCredential(PublicKeyCredentialCreationOptions options) {
log(TAG, "continueMakeCredential");
- mPendingFido2CredentialRequest = getFido2CredentialRequest();
- mPendingFido2CredentialRequest.handleMakeCredentialRequest(
- options,
- maybeCreateBrowserOptions(),
- assertNonNull(mOrigin),
- mTopOrigin,
- mPayment,
- this::onRegisterResponse,
- this::onError,
- this::recordOutcomeEvent);
}
private @Nullable Bundle maybeCreateBrowserOptions() {
@@ -241,17 +210,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
onError(AuthenticatorStatus.NOT_IMPLEMENTED);
return;
}
- assumeNonNull(options.publicKey);
-
- mPendingFido2CredentialRequest = getFido2CredentialRequest();
- mPendingFido2CredentialRequest.handleGetCredentialRequest(
- options,
- assertNonNull(mOrigin),
- mTopOrigin,
- mPayment,
- this::onCredentialResponse,
- this::onError,
- this::recordOutcomeEvent);
}
@Override
@@ -283,10 +241,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
decoratedCallback.call(false);
return;
}
-
- getFido2CredentialRequest()
- .handleIsUserVerifyingPlatformAuthenticatorAvailableRequest(
- isUvpaa -> decoratedCallback.call(isUvpaa));
}
@Override
@@ -323,34 +277,7 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
return;
}
- getFido2CredentialRequest()
- .handleIsUserVerifyingPlatformAuthenticatorAvailableRequest(
- isUvpaa -> {
- capabilities.add(
- createWebAuthnClientCapability(
- AuthenticatorConstants.CAPABILITY_CONDITIONAL_GET,
- couldSupportConditionalMediation() && isUvpaa));
- capabilities.add(
- createWebAuthnClientCapability(
- AuthenticatorConstants.CAPABILITY_UVPAA,
- couldSupportUvpaa() && isUvpaa));
- boolean conditionalCreateEnabled =
- couldSupportConditionalMediation()
- && DeviceFeatureMap.isEnabled(
- DeviceFeatureList.WEBAUTHN_PASSKEY_UPGRADE);
- capabilities.add(
- createWebAuthnClientCapability(
- AuthenticatorConstants.CAPABILITY_CONDITIONAL_CREATE,
- isUvpaa && conditionalCreateEnabled));
- capabilities.add(
- createWebAuthnClientCapability(
- AuthenticatorConstants.CAPABILITY_IMMEDIATE_GET,
- DeviceFeatureMap.isEnabled(
- DeviceFeatureList
- .WEBAUTHN_IMMEDIATE_GET)
- && isUvpaa));
- callback.call(capabilities.toArray(new WebAuthnClientCapability[0]));
- });
+ callback.call(capabilities.toArray(new WebAuthnClientCapability[0]));
}
// Helper function to create WebAuthnClientCapability instances
@@ -381,14 +308,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
callback.onResponse(new ArrayList<byte[]>());
return;
}
-
- getFido2CredentialRequest()
- .handleGetMatchingCredentialIdsRequest(
- relyingPartyId,
- credentialIds,
- requireThirdPartyPayment,
- callback,
- this::onError);
}
@Override
@@ -399,13 +318,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
callback.call(false);
return;
}
-
- // 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.
- getFido2CredentialRequest()
- .handleIsUserVerifyingPlatformAuthenticatorAvailableRequest(
- isUvpaa -> callback.call(isUvpaa));
}
@Override
@@ -417,9 +329,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
if (!mIsOperationPending || mGetCredentialCallback == null) {
return;
}
-
- assumeNonNull(mPendingFido2CredentialRequest);
- mPendingFido2CredentialRequest.cancelGetAssertion();
}
/** Callbacks for receiving responses from the internal handlers. */
@@ -468,7 +377,6 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
} else if (mGetCredentialCallback != null) {
mGetCredentialCallback.call(getCredentialResponseForAssertion(status, null));
}
- if (mPendingFido2CredentialRequest != null) mPendingFido2CredentialRequest.destroyBridge();
cleanupRequest();
}
@@ -508,14 +416,11 @@ public final class AuthenticatorImpl implements Authenticator, AuthenticationCon
mIsOperationPending = false;
mMakeCredentialCallback = null;
mGetCredentialCallback = null;
- mPendingFido2CredentialRequest = null;
}
@Override
public void close() {
log(TAG, "close");
- mUnclosedFido2CredentialRequests.forEach(Fido2CredentialRequest::destroyBridge);
- mUnclosedFido2CredentialRequests.clear();
cleanupRequest();
}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/ConditionalUiState.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/ConditionalUiState.java
new file mode 100644
--- /dev/null
@@ -3515,6 +3307,172 @@ new file mode 100644
+ CANCEL_PENDING,
+ CANCEL_PENDING_RP_ID_VALIDATION_COMPLETE,
+}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
@@ -26,8 +26,6 @@ import android.util.Pair;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
-import com.google.android.gms.tasks.Task;
-
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;
@@ -48,7 +46,6 @@ import org.chromium.blink.mojom.ResidentKeyRequirement;
import org.chromium.blink_public.common.BlinkFeatures;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
-import org.chromium.components.webauthn.Fido2ApiCall.Fido2ApiCallParams;
import org.chromium.components.webauthn.cred_man.CredManHelper;
import org.chromium.components.webauthn.cred_man.CredManSupportProvider;
import org.chromium.content_public.browser.ClientDataJson;
@@ -132,12 +129,7 @@ public class Fido2CredentialRequest
*/
public Fido2CredentialRequest(AuthenticationContextProvider authenticationContextProvider) {
mAuthenticationContextProvider = authenticationContextProvider;
- boolean playServicesAvailable;
- try {
- playServicesAvailable = Fido2ApiCallHelper.getInstance().arePlayServicesAvailable();
- } catch (Exception e) {
- playServicesAvailable = false;
- }
+ boolean playServicesAvailable = false;
mPlayServicesAvailable = playServicesAvailable;
mCredManHelper =
new CredManHelper(mAuthenticationContextProvider, this, mPlayServicesAvailable);
@@ -334,22 +326,6 @@ public class Fido2CredentialRequest
returnErrorAndResetCallback(AuthenticatorStatus.UNKNOWN_ERROR);
return;
}
- try {
- Fido2ApiCallHelper.getInstance()
- .invokeFido2MakeCredential(
- mAuthenticationContextProvider,
- options,
- Uri.parse(convertOriginToString(origin)),
- clientDataHash,
- maybeBrowserOptions,
- getMaybeResultReceiver(),
- this::onGotPendingIntent,
- this::onBinderCallException);
- } catch (NoSuchAlgorithmException e) {
- mMakeCredentialErrorOutcome = MakeCredentialOutcome.ALGORITHM_NOT_SUPPORTED;
- returnErrorAndResetCallback(AuthenticatorStatus.ALGORITHM_UNSUPPORTED);
- return;
- }
return;
}
int result =
@@ -397,23 +373,6 @@ public class Fido2CredentialRequest
returnErrorAndResetCallback(AuthenticatorStatus.UNKNOWN_ERROR);
return;
}
-
- try {
- Fido2ApiCallHelper.getInstance()
- .invokeFido2MakeCredential(
- mAuthenticationContextProvider,
- options,
- Uri.parse(convertOriginToString(origin)),
- clientDataHash,
- maybeBrowserOptions,
- getMaybeResultReceiver(),
- this::onGotPendingIntent,
- this::onBinderCallException);
- } catch (NoSuchAlgorithmException e) {
- mMakeCredentialErrorOutcome = MakeCredentialOutcome.ALGORITHM_NOT_SUPPORTED;
- returnErrorAndResetCallback(AuthenticatorStatus.ALGORITHM_UNSUPPORTED);
- return;
- }
}
/**
@@ -785,32 +744,6 @@ public class Fido2CredentialRequest
callback.onIsUserVerifyingPlatformAuthenticatorAvailableResponse(false);
return;
}
-
- Fido2ApiCallParams params =
- WebauthnModeProvider.getInstance()
- .getFido2ApiCallParams(mAuthenticationContextProvider.getWebContents());
- assertNonNull(mAuthenticationContextProvider.getContext());
- assertNonNull(params);
- Fido2ApiCall call = new Fido2ApiCall(mAuthenticationContextProvider.getContext(), params);
- Fido2ApiCall.BooleanResult result = new Fido2ApiCall.BooleanResult();
- Parcel args = call.start();
- args.writeStrongBinder(result);
-
- Task<Boolean> task =
- call.run(
- params.mIsUserVerifyingPlatformAuthenticatorAvailableMethodId,
- Fido2ApiCall.TRANSACTION_ISUVPAA,
- args,
- result);
- task.addOnSuccessListener(
- (isUvpaa) -> {
- callback.onIsUserVerifyingPlatformAuthenticatorAvailableResponse(isUvpaa);
- });
- task.addOnFailureListener(
- (e) -> {
- logError(TAG, "FIDO2 API call failed", e);
- callback.onIsUserVerifyingPlatformAuthenticatorAvailableResponse(false);
- });
}
public void handleGetMatchingCredentialIdsRequest(
@@ -1139,16 +1072,6 @@ public class Fido2CredentialRequest
if (options.mediation == Mediation.CONDITIONAL) {
mCancellableUiState = CancellableUiState.REQUEST_SENT_TO_PLATFORM;
}
-
- Fido2ApiCallHelper.getInstance()
- .invokeFido2GetAssertion(
- mAuthenticationContextProvider,
- publicKeyOptions,
- Uri.parse(callerOriginString),
- clientDataHash,
- getMaybeResultReceiver(),
- this::onGotPendingIntent,
- this::onBinderCallException);
}
private void handleNonCredentialReturn(GetCredentialOptions options, Integer reason) {
@@ -1192,34 +1115,6 @@ public class Fido2CredentialRequest
return;
}
mCancellableUiState = CancellableUiState.REQUEST_SENT_TO_PLATFORM;
-
- Fido2ApiCallParams params =
- WebauthnModeProvider.getInstance()
- .getFido2ApiCallParams(mAuthenticationContextProvider.getWebContents());
- assertNonNull(mAuthenticationContextProvider.getContext());
- assertNonNull(params);
- Fido2ApiCall call = new Fido2ApiCall(mAuthenticationContextProvider.getContext(), params);
- Parcel args = call.start();
- String callbackDescriptor = params.mCallbackDescriptor;
- Fido2ApiCall.PendingIntentResult result =
- new Fido2ApiCall.PendingIntentResult(callbackDescriptor);
- args.writeStrongBinder(result);
- args.writeInt(1); // This indicates that the following options are present.
- Fido2Api.appendBrowserGetAssertionOptionsToParcel(
- options,
- Uri.parse(callerOriginString),
- clientDataHash,
- /* tunnelId= */ null,
- /* resultReceiver= */ null,
- args);
- Task<PendingIntent> task =
- call.run(
- Fido2ApiCall.METHOD_BROWSER_HYBRID_SIGN,
- Fido2ApiCall.TRANSACTION_HYBRID_SIGN,
- args,
- result);
- task.addOnSuccessListener(this::onGotPendingIntent);
- task.addOnFailureListener(this::onBinderCallException);
}
// Handles a PendingIntent from the GMSCore FIDO library.
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/GmsCoreGetCredentialsHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/GmsCoreGetCredentialsHelper.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/GmsCoreGetCredentialsHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/GmsCoreGetCredentialsHelper.java
@@ -3813,75 +3771,6 @@ diff --git a/components/webauthn/android/java/src/org/chromium/components/webaut
public @WebauthnMode int getWebauthnMode(@Nullable WebContents webContents) {
if (mGlobalMode != WebauthnMode.NONE) return mGlobalMode;
return WebauthnModeProviderJni.get().getWebauthnModeForWebContents(webContents);
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManHelper.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManHelper.java
@@ -40,8 +40,6 @@ import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.components.webauthn.AuthenticationContextProvider;
import org.chromium.components.webauthn.Barrier;
-import org.chromium.components.webauthn.Fido2CredentialRequest.CancellableUiState;
-import org.chromium.components.webauthn.Fido2CredentialRequestJni;
import org.chromium.components.webauthn.GetAssertionOutcome;
import org.chromium.components.webauthn.GetCredentialResponseCallback;
import org.chromium.components.webauthn.MakeCredentialOutcome;
@@ -83,6 +81,16 @@ public class CredManHelper {
private CredManMetricsHelper mMetricsHelper;
private @Nullable Runnable mNoCredentialsFallback;
+ public enum CancellableUiState {
+ NONE,
+ WAITING_FOR_RP_ID_VALIDATION,
+ WAITING_FOR_CREDENTIAL_LIST,
+ WAITING_FOR_SELECTION,
+ REQUEST_SENT_TO_PLATFORM,
+ CANCEL_PENDING,
+ CANCEL_PENDING_RP_ID_VALIDATION_COMPLETE,
+ }
+
// A callback that provides an AuthenticatorStatus error in the first argument, and optionally a
// metrics recording outcome in the second.
public interface ErrorCallback {
@@ -113,8 +121,7 @@ public class CredManHelper {
ErrorCallback errorCallback) {
log(TAG, "startMakeRequest");
mClientDataJson = clientDataJson;
- final String requestAsJson =
- Fido2CredentialRequestJni.get().createOptionsToJson(options.serialize());
+ final String requestAsJson = "";
OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> receiver =
new OutcomeReceiver<>() {
@@ -450,8 +457,7 @@ public class CredManHelper {
CRED_MAN_PREFIX
+ "BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON");
assertNonNull(json);
- byte[] responseSerialized =
- Fido2CredentialRequestJni.get().getCredentialResponseFromJson(json);
+ byte[] responseSerialized = null;
if (responseSerialized == null) {
logError(
TAG,
@@ -619,8 +625,7 @@ public class CredManHelper {
boolean requestPasswords,
boolean preferImmediatelyAvailable,
boolean ignoreGpm) {
- final String requestAsJson =
- Fido2CredentialRequestJni.get().getOptionsToJson(options.serialize());
+ final String requestAsJson = "";
boolean hasAllowCredentials =
options.allowCredentials != null && options.allowCredentials.length != 0;
@@ -658,8 +663,7 @@ public class CredManHelper {
Bundle data) {
String json = data.getString(BUNDLE_KEY_REGISTRATION_RESPONSE_JSON);
assertNonNull(json);
- byte[] responseSerialized =
- Fido2CredentialRequestJni.get().makeCredentialResponseFromJson(json);
+ byte[] responseSerialized = null;
if (responseSerialized == null) {
logError(TAG, "Failed to convert response from CredMan to Mojo object: %s", json);
return null;
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManMetricsHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManMetricsHelper.java
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManMetricsHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/cred_man/CredManMetricsHelper.java