From e193c14feb344bc88fcd6f3632876af4946d90e2 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 14:11:09 +0000 Subject: [PATCH 01/23] WIP: feat(appcheck): Implement initial reCAPTCHA Enterprise provider This commit introduces the basic structure for the reCAPTCHA Enterprise App Check provider. It's currently a work in progress and not yet ready for review or final integration. - Added `RecaptchaEnterpriseAppCheckProviderFactory`. - Added `FirebaseAppCheckRecaptchaEnterpriseRegistrar`. - Further implementation and testing are required. --- ...eAppCheckRecaptchaEnterpriseRegistrar.java | 70 +++++++++++++++++++ ...tchaEnterpriseAppCheckProviderFactory.java | 44 ++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java new file mode 100644 index 00000000000..426bbcf0a6b --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -0,0 +1,70 @@ +package com.google.firebase.appcheck.recaptchaenterprise; + +import android.app.Application; +import android.content.Context; + +import com.google.android.gms.common.annotation.KeepForSdk; +import com.google.firebase.FirebaseApp; +import com.google.firebase.annotations.concurrent.Blocking; +import com.google.firebase.annotations.concurrent.Lightweight; +import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; +import com.google.firebase.appcheck.recaptchaenterprise.internal.SiteKey; +import com.google.firebase.components.Component; +import com.google.firebase.components.ComponentRegistrar; +import com.google.firebase.components.Dependency; +import com.google.firebase.components.Qualified; +import com.google.firebase.platforminfo.LibraryVersionComponent; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * {@link ComponentRegistrar} for setting up FirebaseAppCheck reCAPTCHA Enterprise's dependency + * injections in Firebase Android Components. + * + * @hide + */ +@KeepForSdk +public class FirebaseAppCheckRecaptchaEnterpriseRegistrar implements ComponentRegistrar { + private static final String LIBRARY_NAME = "fire-app-check-recaptcha-enterprise"; + + @Override + public List> getComponents() { + Qualified liteExecutor = Qualified.qualified(Lightweight.class, Executor.class); + Qualified blockingExecutor = Qualified.qualified(Blocking.class, Executor.class); + + return Arrays.asList( + Component.builder(Application.class) + .name(LIBRARY_NAME) + .add(Dependency.required(Context.class)) + .factory( + container -> { + Context context = container.get(Context.class); + return (Application) context.getApplicationContext(); + }) + .build(), + Component.builder(SiteKey.class) + .name(LIBRARY_NAME) + .factory( + container -> new SiteKey(RecaptchaEnterpriseAppCheckProviderFactory.getSiteKey())) + .build(), + Component.builder(RecaptchaEnterpriseAppCheckProvider.class) + .name(LIBRARY_NAME) + .add(Dependency.required(FirebaseApp.class)) + .add(Dependency.required(Application.class)) + .add(Dependency.required(SiteKey.class)) + .add(Dependency.required(liteExecutor)) + .add(Dependency.required(blockingExecutor)) + .factory( + (container -> + new RecaptchaEnterpriseAppCheckProvider( + container.get(FirebaseApp.class), + container.get(Application.class), + container.get(SiteKey.class), + container.get(liteExecutor), + container.get(blockingExecutor)))) + .build(), + LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java new file mode 100644 index 00000000000..db54812884b --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -0,0 +1,44 @@ +package com.google.firebase.appcheck.recaptchaenterprise; + +import androidx.annotation.NonNull; +import com.google.firebase.FirebaseApp; +import com.google.firebase.appcheck.AppCheckProvider; +import com.google.firebase.appcheck.AppCheckProviderFactory; +import com.google.firebase.appcheck.FirebaseAppCheck; +import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; + +/** + * Implementation of an {@link AppCheckProviderFactory} that builds
+ * {@link RecaptchaEnterpriseAppCheckProvider}s. This is the default implementation. + */ +public class RecaptchaEnterpriseAppCheckProviderFactory implements AppCheckProviderFactory { + + private static volatile RecaptchaEnterpriseAppCheckProviderFactory instance; + private static String siteKey; + + /** Gets an instance of this class for installation into a {@link FirebaseAppCheck} instance. */ + @NonNull + public static RecaptchaEnterpriseAppCheckProviderFactory getInstance(@NonNull String siteKey) { + if (instance == null) { + synchronized (RecaptchaEnterpriseAppCheckProviderFactory.class) { + if (instance == null) { + instance = new RecaptchaEnterpriseAppCheckProviderFactory(); + RecaptchaEnterpriseAppCheckProviderFactory.siteKey = siteKey; + } + } + } + return instance; + } + + @NonNull + public static String getSiteKey() { + return siteKey; + } + + @NonNull + @Override + @SuppressWarnings("FirebaseUseExplicitDependencies") + public AppCheckProvider create(@NonNull FirebaseApp firebaseApp) { + return firebaseApp.get(RecaptchaEnterpriseAppCheckProvider.class); + } +} From 5288e7c46fc9eb05f0549bedbe6310949a7de82f Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 16:16:25 +0000 Subject: [PATCH 02/23] WIP: feat(appcheck-recaptcha-enterprise): Add token exchange models and site key config This commit introduces the necessary data models and configuration for performing reCAPTCHA Enterprise token exchange with the Firebase App Check backend. - `SiteKey.java`: Provides a structured way to hold and manage the reCAPTCHA Enterprise site key. - `ExchangeRecaptchaEnterpriseTokenRequest.java`: Defines the client-side model for the payload sent to the Firebase App Check Token Exchange API. - `packageinfo.java`: Includes package-level metadata for the new reCAPTCHA Enterprise App Check module. Further implementation, integration, and testing are still in progress. This is not yet ready for review or final merge. --- ...changeRecaptchaEnterpriseTokenRequest.java | 31 +++++++++++++++++++ .../recaptchaenterprise/internal/SiteKey.java | 17 ++++++++++ .../internal/package-info.java | 2 ++ 3 files changed, 50 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java new file mode 100644 index 00000000000..5d20930487e --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java @@ -0,0 +1,31 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Client-side model of the ExchangeRecaptchaEnterpriseTokenRequest payload from the Firebase App + * Check Token Exchange API. + */ +public class ExchangeRecaptchaEnterpriseTokenRequest { + + @VisibleForTesting + static final String RECAPTCHA_ENTERPRISE_TOKEN_KEY = "recaptchaEnterpriseToken"; + + private final String recaptchaEnterpriseToken; + + public ExchangeRecaptchaEnterpriseTokenRequest(@NonNull String recaptchaEnterpriseToken) { + this.recaptchaEnterpriseToken = recaptchaEnterpriseToken; + } + + @NonNull + public String toJsonString() throws JSONException { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(RECAPTCHA_ENTERPRISE_TOKEN_KEY, recaptchaEnterpriseToken); + + return jsonObject.toString(); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java new file mode 100644 index 00000000000..a75f96ed809 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java @@ -0,0 +1,17 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import androidx.annotation.NonNull; +import java.util.Objects; + +public class SiteKey { + private final String value; + + public SiteKey(@NonNull String value) { + this.value = Objects.requireNonNull(value, "Site key cannot be null"); + } + + @NonNull + public String value() { + return value; + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java new file mode 100644 index 00000000000..7d1cc21f754 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java @@ -0,0 +1,2 @@ +/** @hide */ +package com.google.firebase.appcheck.recaptchaenterprise.internal; From c25340b05c0ada5267e2df454bd2c6bd22558260 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 16:30:58 +0000 Subject: [PATCH 03/23] WIP: feat(appcheck-network): Extend NetworkClient for reCAPTCHA Enterprise This commit modifies the `NetworkClient` to support fetching App Check tokens using the reCAPTCHA Enterprise attestation type. - Adds the `RECAPTCHA_ENTERPRISE_URL_TEMPLATE` constant for the new API endpoint. - Includes `RECAPTCHA_ENTERPRISE` as a new `AttestationTokenType`. - Updates the `getUrlTemplate` method to return the correct URL for reCAPTCHA Enterprise token exchange requests. --- .../firebase/appcheck/internal/NetworkClient.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java index 410f59290cb..a347fa24bf9 100644 --- a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java +++ b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java @@ -19,9 +19,11 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; + import com.google.android.gms.common.util.AndroidUtilsLight; import com.google.android.gms.common.util.Hex; import com.google.android.gms.tasks.Tasks; @@ -31,6 +33,7 @@ import com.google.firebase.appcheck.FirebaseAppCheck; import com.google.firebase.heartbeatinfo.HeartBeatController; import com.google.firebase.inject.Provider; + import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; @@ -41,6 +44,8 @@ import java.lang.annotation.RetentionPolicy; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; + import org.json.JSONException; /** @@ -57,9 +62,10 @@ public class NetworkClient { "https://firebaseappcheck.googleapis.com/v1/projects/%s/apps/%s:exchangePlayIntegrityToken?key=%s"; private static final String PLAY_INTEGRITY_CHALLENGE_URL_TEMPLATE = "https://firebaseappcheck.googleapis.com/v1/projects/%s/apps/%s:generatePlayIntegrityChallenge?key=%s"; + private static final String RECAPTCHA_ENTERPRISE_URL_TEMPLATE = + "https://firebaseappcheck.googleapis.com/v1/projects/%s/apps/%s:exchangeRecaptchaEnterpriseToken?key=%s"; private static final String CONTENT_TYPE = "Content-Type"; private static final String APPLICATION_JSON = "application/json"; - private static final String UTF_8 = "UTF-8"; @VisibleForTesting static final String X_FIREBASE_CLIENT = "X-Firebase-Client"; @VisibleForTesting static final String X_ANDROID_PACKAGE = "X-Android-Package"; @VisibleForTesting static final String X_ANDROID_CERT = "X-Android-Cert"; @@ -71,12 +77,13 @@ public class NetworkClient { private final Provider heartBeatControllerProvider; @Retention(RetentionPolicy.SOURCE) - @IntDef({UNKNOWN, DEBUG, PLAY_INTEGRITY}) + @IntDef({UNKNOWN, DEBUG, PLAY_INTEGRITY, RECAPTCHA_ENTERPRISE}) public @interface AttestationTokenType {} public static final int UNKNOWN = 0; public static final int DEBUG = 2; public static final int PLAY_INTEGRITY = 3; + public static final int RECAPTCHA_ENTERPRISE = 4; public NetworkClient(@NonNull FirebaseApp firebaseApp) { this( @@ -172,7 +179,7 @@ private String makeNetworkRequest( ? urlConnection.getInputStream() : urlConnection.getErrorStream(); StringBuilder response = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, UTF_8))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { response.append(line); @@ -236,6 +243,8 @@ private static String getUrlTemplate(@AttestationTokenType int tokenType) { return DEBUG_EXCHANGE_URL_TEMPLATE; case PLAY_INTEGRITY: return PLAY_INTEGRITY_EXCHANGE_URL_TEMPLATE; + case RECAPTCHA_ENTERPRISE: + return RECAPTCHA_ENTERPRISE_URL_TEMPLATE; default: throw new IllegalArgumentException("Unknown token type."); } From 86c3bbbf2681f5566a4d3911e7fb72155bd59ecf Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 16:32:04 +0000 Subject: [PATCH 04/23] WIP: feat(appcheck-network): Extend NetworkClient for reCAPTCHA Enterprise This commit modifies the `NetworkClient` to support fetching App Check tokens using the reCAPTCHA Enterprise attestation type. - Adds the `RECAPTCHA_ENTERPRISE_URL_TEMPLATE` constant for the new API endpoint. - Includes `RECAPTCHA_ENTERPRISE` as a new `AttestationTokenType`. - Updates the `getUrlTemplate` method to return the correct URL for reCAPTCHA Enterprise token exchange requests. --- .../com/google/firebase/appcheck/internal/NetworkClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java index a347fa24bf9..6ae382d0946 100644 --- a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java +++ b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java @@ -255,7 +255,7 @@ HttpURLConnection createHttpUrlConnection(URL url) throws IOException { return (HttpURLConnection) url.openConnection(); } - private static final boolean isResponseSuccess(int responseCode) { + private static boolean isResponseSuccess(int responseCode) { return responseCode >= 200 && responseCode < 300; } } From 68ac173c5fbbefe625d2d377415a83aea58f637c Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 16:53:04 +0000 Subject: [PATCH 05/23] WIP: feat(appcheck-recaptcha-enterprise): Implement RecaptchaEnterpriseAppCheckProvider Implements the core `AppCheckProvider` for reCAPTCHA Enterprise, orchestrating the attestation and token exchange process. This class: - Initializes the reCAPTCHA Android client using the provided `SiteKey`. - Defines the `getToken()` method to: - Obtain a reCAPTCHA Enterprise attestation token from the Android client. - Exchange this attestation token with the Firebase App Check backend via `NetworkClient` using the `ExchangeRecaptchaEnterpriseTokenRequest` payload. - Convert the backend response into a `DefaultAppCheckToken`. - Utilizes provided executors for asynchronous operations to ensure smooth integration with the Android application lifecycle. Further unit and integration tests are still pending for this implementation. --- .../RecaptchaEnterpriseAppCheckProvider.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java new file mode 100644 index 00000000000..5c4f9fd2198 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -0,0 +1,100 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.Tasks; +import com.google.android.recaptcha.Recaptcha; +import com.google.android.recaptcha.RecaptchaAction; +import com.google.android.recaptcha.RecaptchaTasksClient; +import com.google.firebase.FirebaseApp; +import com.google.firebase.annotations.concurrent.Blocking; +import com.google.firebase.annotations.concurrent.Lightweight; +import com.google.firebase.appcheck.AppCheckProvider; +import com.google.firebase.appcheck.AppCheckToken; +import com.google.firebase.appcheck.internal.DefaultAppCheckToken; +import com.google.firebase.appcheck.internal.NetworkClient; +import com.google.firebase.appcheck.internal.RetryManager; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.concurrent.Executor; + +public class RecaptchaEnterpriseAppCheckProvider implements AppCheckProvider { + + private final RecaptchaAction recaptchaAction = RecaptchaAction.custom("fire_app_check"); + private final Task recaptchaTasksClientTask; + private final Executor liteExecutor; + private final Executor blockingExecutor; + private final RetryManager retryManager; + private final NetworkClient networkClient; + + public RecaptchaEnterpriseAppCheckProvider( + @NonNull FirebaseApp firebaseApp, + @NonNull Application application, + @NonNull SiteKey siteKey, + @Lightweight Executor liteExecutor, + @Blocking Executor blockingExecutor) { + this.liteExecutor = liteExecutor; + this.blockingExecutor = blockingExecutor; + this.retryManager = new RetryManager(); + this.networkClient = new NetworkClient(firebaseApp); + recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey.value()); + } + + @VisibleForTesting + RecaptchaEnterpriseAppCheckProvider( + @Lightweight Executor liteExecutor, + @Blocking Executor blockingExecutor, + @NonNull RetryManager retryManager, + @NonNull NetworkClient networkClient, + @NonNull RecaptchaTasksClient recaptchaTasksClient) { + this.liteExecutor = liteExecutor; + this.blockingExecutor = blockingExecutor; + this.retryManager = retryManager; + this.networkClient = networkClient; + this.recaptchaTasksClientTask = Tasks.forResult(recaptchaTasksClient); + } + + @NonNull + @Override + public Task getToken() { + return getRecaptchaEnterpriseAttestation() + .onSuccessTask( + liteExecutor, + recaptchaEnterpriseToken -> { + ExchangeRecaptchaEnterpriseTokenRequest request = + new ExchangeRecaptchaEnterpriseTokenRequest(recaptchaEnterpriseToken); + return Tasks.call( + blockingExecutor, + () -> + networkClient.exchangeAttestationForAppCheckToken( + request.toJsonString().getBytes(StandardCharsets.UTF_8), + NetworkClient.RECAPTCHA_ENTERPRISE, + retryManager)); + }) + .onSuccessTask( + liteExecutor, + appCheckTokenResponse -> + Tasks.forResult( + DefaultAppCheckToken.constructFromAppCheckTokenResponse( + appCheckTokenResponse))); + } + + @NonNull + private Task getRecaptchaEnterpriseAttestation() { + return recaptchaTasksClientTask.continueWithTask( + blockingExecutor, + task -> { + if (task.isSuccessful()) { + RecaptchaTasksClient client = task.getResult(); + return client.executeTask(recaptchaAction); + } else { + throw Objects.requireNonNull(task.getException()); + } + }); + } +} From 014dc4a909bf7a738f5fdc3d841ea2aa41f1e8bc Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 8 Jul 2025 18:47:50 +0000 Subject: [PATCH 06/23] WIP: chore(appcheck-recaptcha-enterprise): Integrate module into build system and manifest This commit integrates the new `firebase-appcheck-recaptchaenterprise` module into the project's build configuration and AndroidManifest.xml. Key changes include: - `AndroidManifest.xml`: Updated to include necessary permissions or components required by the reCAPTCHA Enterprise SDK or Firebase App Check. - `firebase-appcheck-recaptchaenterprise.gradle`: Configures the build settings for the new reCAPTCHA Enterprise App Check module. - `subprojects.cfg`: Adds `appcheck:firebase-appcheck-recaptchaenterprise` to the list of subprojects, making the module discoverable in the build. --- ...rebase-appcheck-recaptchaenterprise.gradle | 54 +++++++++++++++++++ .../src/main/AndroidManifest.xml | 15 ++++++ subprojects.cfg | 1 + 3 files changed, 70 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle new file mode 100644 index 00000000000..5fd19526e72 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -0,0 +1,54 @@ +plugins { + id 'firebase-library' +} + +firebaseLibrary { + libraryGroup = "appcheck" + releaseNotes { + name.set("{{app_check}} Recaptcha Enterprise") + versionName.set("appcheck-recaptchaenterprise") + hasKTX.set(false) + } +} + +android { + adbOptions { + timeOutInMs 60 * 1000 + } + + namespace "com.google.firebase.appcheck.recaptchaenterprise" + compileSdkVersion project.compileSdkVersion + defaultConfig { + targetSdkVersion project.targetSdkVersion + minSdkVersion project.minSdkVersion + versionName version + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + testOptions.unitTests.includeAndroidResources = false +} + +dependencies { + javadocClasspath libs.autovalue.annotations + + api project(':appcheck:firebase-appcheck') + api 'com.google.firebase:firebase-annotations:16.2.0' + api 'com.google.firebase:firebase-common:21.0.0' + api 'com.google.firebase:firebase-common-ktx:21.0.0' + api 'com.google.firebase:firebase-components:18.0.0' + api 'com.google.android.recaptcha:recaptcha:18.8.0-beta01' + + testImplementation(project(":integ-testing")) { + exclude group: 'com.google.firebase', module: 'firebase-common' + exclude group: 'com.google.firebase', module: 'firebase-components' + } + testImplementation libs.androidx.test.core + testImplementation libs.truth + testImplementation libs.junit + testImplementation libs.mockito.core.v5120 + testImplementation libs.robolectric +} \ No newline at end of file diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..416bb3e8aee --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/subprojects.cfg b/subprojects.cfg index a43cf350e91..e42fd66e518 100644 --- a/subprojects.cfg +++ b/subprojects.cfg @@ -3,6 +3,7 @@ appcheck:firebase-appcheck-debug-testing appcheck:firebase-appcheck-debug appcheck:firebase-appcheck-interop appcheck:firebase-appcheck-playintegrity +appcheck:firebase-appcheck-recaptchaenterprise appcheck:firebase-appcheck firebase-abt From 2d2b96d3075e65af1e68d70a87a4cc907f4107f5 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Wed, 9 Jul 2025 06:24:31 +0000 Subject: [PATCH 07/23] test(appcheck-recaptcha-enterprise): Add unit tests for reCAPTCHA Enterprise module --- ...CheckRecaptchaEnterpriseRegistrarTest.java | 49 +++++ ...EnterpriseAppCheckProviderFactoryTest.java | 25 +++ ...geRecaptchaEnterpriseTokenRequestTest.java | 30 +++ ...captchaEnterpriseAppCheckProviderTest.java | 193 ++++++++++++++++++ .../internal/SiteKeyTest.java | 28 +++ 5 files changed, 325 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java new file mode 100644 index 00000000000..323f303dcd8 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java @@ -0,0 +1,49 @@ +package com.google.firebase.appcheck.recaptchaenterprise; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Application; +import android.content.Context; + +import com.google.firebase.FirebaseApp; +import com.google.firebase.annotations.concurrent.Blocking; +import com.google.firebase.annotations.concurrent.Lightweight; +import com.google.firebase.appcheck.recaptchaenterprise.internal.SiteKey; +import com.google.firebase.components.Component; +import com.google.firebase.components.Dependency; +import com.google.firebase.components.Qualified; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.util.List; +import java.util.concurrent.Executor; + +/** Tests for {@link FirebaseAppCheckRecaptchaEnterpriseRegistrar}. */ +@RunWith(RobolectricTestRunner.class) +public class FirebaseAppCheckRecaptchaEnterpriseRegistrarTest { + @Test + public void testGetComponents() { + FirebaseAppCheckRecaptchaEnterpriseRegistrar registrar = + new FirebaseAppCheckRecaptchaEnterpriseRegistrar(); + List> components = registrar.getComponents(); + assertThat(components).isNotEmpty(); + assertThat(components).hasSize(4); + Component applicationComponent = components.get(0); + assertThat(applicationComponent.getDependencies()) + .containsExactly(Dependency.required(Context.class)); + assertThat(applicationComponent.isLazy()).isTrue(); + Component siteKeyComponent = components.get(1); + assertThat(siteKeyComponent.isLazy()).isTrue(); + Component appCheckRecaptchaEnterpriseComponent = components.get(2); + assertThat(appCheckRecaptchaEnterpriseComponent.getDependencies()) + .containsExactly( + Dependency.required(FirebaseApp.class), + Dependency.required(Application.class), + Dependency.required(SiteKey.class), + Dependency.required(Qualified.qualified(Lightweight.class, Executor.class)), + Dependency.required(Qualified.qualified(Blocking.class, Executor.class))); + assertThat(appCheckRecaptchaEnterpriseComponent.isLazy()).isTrue(); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java new file mode 100644 index 00000000000..f6b9c2e2fa4 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java @@ -0,0 +1,25 @@ +package com.google.firebase.appcheck.recaptchaenterprise; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@link RecaptchaEnterpriseAppCheckProviderFactory}. */ +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class RecaptchaEnterpriseAppCheckProviderFactoryTest { + static final String SITE_KEY = "siteKey"; + + @Test + public void testGetInstance_callTwice_sameInstance() { + RecaptchaEnterpriseAppCheckProviderFactory firstInstance = + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY); + RecaptchaEnterpriseAppCheckProviderFactory secondInstance = + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY); + + assertThat(firstInstance).isEqualTo(secondInstance); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java new file mode 100644 index 00000000000..0e62da3d205 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java @@ -0,0 +1,30 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import static com.google.common.truth.Truth.assertThat; + +import org.json.JSONObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@link ExchangeRecaptchaEnterpriseTokenRequest}. */ +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class ExchangeRecaptchaEnterpriseTokenRequestTest { + private static final String RECAPTCHA_ENTERPRISE_TOKEN = "recaptchaEnterpriseToken"; + + @Test + public void toJsonString_expectSerialized() throws Exception { + ExchangeRecaptchaEnterpriseTokenRequest exchangeRecaptchaEnterpriseTokenRequest = + new ExchangeRecaptchaEnterpriseTokenRequest(RECAPTCHA_ENTERPRISE_TOKEN); + + String jsonString = exchangeRecaptchaEnterpriseTokenRequest.toJsonString(); + JSONObject jsonObject = new JSONObject(jsonString); + + assertThat( + jsonObject.getString( + ExchangeRecaptchaEnterpriseTokenRequest.RECAPTCHA_ENTERPRISE_TOKEN_KEY)) + .isEqualTo(RECAPTCHA_ENTERPRISE_TOKEN); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java new file mode 100644 index 00000000000..6d09a1f8525 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -0,0 +1,193 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; + +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.Tasks; +import com.google.android.recaptcha.RecaptchaAction; +import com.google.android.recaptcha.RecaptchaTasksClient; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseException; +import com.google.firebase.appcheck.AppCheckToken; +import com.google.firebase.appcheck.internal.AppCheckTokenResponse; +import com.google.firebase.appcheck.internal.DefaultAppCheckToken; +import com.google.firebase.appcheck.internal.NetworkClient; +import com.google.firebase.appcheck.internal.RetryManager; +import com.google.firebase.concurrent.TestOnlyExecutors; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.LooperMode; + +import java.io.IOException; +import java.util.concurrent.Executor; + +/** Tests for {@link RecaptchaEnterpriseAppCheckProvider}. */ +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +@LooperMode(LooperMode.Mode.LEGACY) +public class RecaptchaEnterpriseAppCheckProviderTest { + private static final String APP_CHECK_TOKEN = "appCheckToken"; + private static final String RECAPTCHA_ENTERPRISE_TOKEN = "recaptchaEnterpriseToken"; + private final Executor liteExecutor = MoreExecutors.directExecutor(); + private final Executor blockingExecutor = MoreExecutors.directExecutor(); + private final SiteKey siteKey = new SiteKey("siteKey"); + + @Mock private NetworkClient mockNetworkClient; + @Mock private Application mockApplication; + @Mock FirebaseApp mockFirebaseApp; + @Mock RecaptchaTasksClient mockRecaptchaTasksClient; + @Mock RetryManager mockRetryManager; + + @Before + public void setup() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testPublicConstructor_nullFirebaseApp_expectThrows() { + assertThrows( + NullPointerException.class, + () -> + new RecaptchaEnterpriseAppCheckProvider( + null, + mockApplication, + siteKey, + TestOnlyExecutors.lite(), + TestOnlyExecutors.blocking())); + } + + @Test + public void testPublicConstructor_nullApplication_expectThrows() { + assertThrows( + NullPointerException.class, + () -> + new RecaptchaEnterpriseAppCheckProvider( + mockFirebaseApp, + null, + siteKey, + TestOnlyExecutors.lite(), + TestOnlyExecutors.blocking())); + } + + @Test + public void testPublicConstructor_nullSiteKey_expectThrows() { + assertThrows( + NullPointerException.class, + () -> + new RecaptchaEnterpriseAppCheckProvider( + mockFirebaseApp, + mockApplication, + siteKey, + TestOnlyExecutors.lite(), + TestOnlyExecutors.blocking())); + } + + @Test + public void getToken_onSuccess_setsTaskResult() throws Exception { + when(mockRecaptchaTasksClient.executeTask(any(RecaptchaAction.class))) + .thenReturn(Tasks.forResult(RECAPTCHA_ENTERPRISE_TOKEN)); + String jsonResponse = + new JSONObject().put("token", APP_CHECK_TOKEN).put("ttl", 3600).toString(); + when(mockNetworkClient.exchangeAttestationForAppCheckToken( + any(byte[].class), eq(NetworkClient.RECAPTCHA_ENTERPRISE), eq(mockRetryManager))) + .thenReturn(AppCheckTokenResponse.fromJsonString(jsonResponse)); + + RecaptchaEnterpriseAppCheckProvider provider = + new RecaptchaEnterpriseAppCheckProvider( + liteExecutor, + blockingExecutor, + mockRetryManager, + mockNetworkClient, + mockRecaptchaTasksClient); + Task task = provider.getToken(); + + assertThat(task.isSuccessful()).isTrue(); + AppCheckToken token = task.getResult(); + assertThat(token).isInstanceOf(DefaultAppCheckToken.class); + assertThat(token.getToken()).isEqualTo(APP_CHECK_TOKEN); + + ArgumentCaptor recaptchaActionCaptor = + ArgumentCaptor.forClass(RecaptchaAction.class); + verify(mockRecaptchaTasksClient).executeTask(recaptchaActionCaptor.capture()); + assertThat(recaptchaActionCaptor.getValue().getAction()).isEqualTo("fire_app_check"); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(byte[].class); + verify(mockNetworkClient) + .exchangeAttestationForAppCheckToken( + requestCaptor.capture(), eq(NetworkClient.RECAPTCHA_ENTERPRISE), eq(mockRetryManager)); + } + + @Test + public void getToken_invalidSiteKey_returnException() { + Exception exception = new Exception("Site key invalid"); + when(mockRecaptchaTasksClient.executeTask(any(RecaptchaAction.class))) + .thenReturn(Tasks.forException(exception)); + + RecaptchaEnterpriseAppCheckProvider provider = + new RecaptchaEnterpriseAppCheckProvider( + liteExecutor, + blockingExecutor, + mockRetryManager, + mockNetworkClient, + mockRecaptchaTasksClient); + Task task = provider.getToken(); + assertThat(task.isSuccessful()).isFalse(); + assertThat(task.getException()).isEqualTo(exception); + } + + @Test + public void getToken_recaptchaFails_returnException() { + Exception exception = new Exception("Recaptcha error"); + when(mockRecaptchaTasksClient.executeTask(any(RecaptchaAction.class))) + .thenReturn(Tasks.forException(exception)); + + RecaptchaEnterpriseAppCheckProvider provider = + new RecaptchaEnterpriseAppCheckProvider( + liteExecutor, + blockingExecutor, + mockRetryManager, + mockNetworkClient, + mockRecaptchaTasksClient); + Task task = provider.getToken(); + assertThat(task.isSuccessful()).isFalse(); + assertThat(task.getException()).isEqualTo(exception); + } + + @Test + public void getToken_networkFails_returnException() + throws FirebaseException, JSONException, IOException { + when(mockRecaptchaTasksClient.executeTask(any(RecaptchaAction.class))) + .thenReturn(Tasks.forResult(RECAPTCHA_ENTERPRISE_TOKEN)); + Exception exception = new IOException("Network error"); + when(mockNetworkClient.exchangeAttestationForAppCheckToken( + any(byte[].class), eq(NetworkClient.RECAPTCHA_ENTERPRISE), eq(mockRetryManager))) + .thenThrow(exception); + + RecaptchaEnterpriseAppCheckProvider provider = + new RecaptchaEnterpriseAppCheckProvider( + liteExecutor, + blockingExecutor, + mockRetryManager, + mockNetworkClient, + mockRecaptchaTasksClient); + Task task = provider.getToken(); + assertThat(task.isSuccessful()).isFalse(); + assertThat(task.getException()).isEqualTo(exception); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java new file mode 100644 index 00000000000..303d16dc93c --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java @@ -0,0 +1,28 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import static org.junit.Assert.assertThrows; + +import com.google.common.truth.Truth; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@Link SiteKey}. */ +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SiteKeyTest { + private static final String VALUE = "siteKey"; + + @Test + public void testConstructor_nullValue_expectThrows() { + assertThrows(NullPointerException.class, () -> new SiteKey(null)); + } + + @Test + public void testConstructor_nonNullValue_succeeds() { + SiteKey siteKey = new SiteKey(VALUE); + Truth.assertThat(siteKey.value()).isEqualTo(VALUE); + } +} From 7a99c47d9b64e77f2155577ea062443d2190f2e8 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Fri, 11 Jul 2025 04:42:44 +0000 Subject: [PATCH 08/23] Resolves comments --- ...rebase-appcheck-recaptchaenterprise.gradle | 10 ++-- .../src/main/AndroidManifest.xml | 3 +- ...eAppCheckRecaptchaEnterpriseRegistrar.java | 24 ++------ ...tchaEnterpriseAppCheckProviderFactory.java | 56 ++++++++++++++----- .../internal/FirebaseExecutors.java | 30 ++++++++++ .../RecaptchaEnterpriseAppCheckProvider.java | 7 ++- .../recaptchaenterprise/internal/SiteKey.java | 17 ------ ...CheckRecaptchaEnterpriseRegistrarTest.java | 22 +------- ...EnterpriseAppCheckProviderFactoryTest.java | 19 +++++-- ...captchaEnterpriseAppCheckProviderTest.java | 2 +- .../internal/SiteKeyTest.java | 28 ---------- 11 files changed, 105 insertions(+), 113 deletions(-) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java delete mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java delete mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle index 5fd19526e72..185520fb21a 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -36,11 +36,11 @@ dependencies { javadocClasspath libs.autovalue.annotations api project(':appcheck:firebase-appcheck') - api 'com.google.firebase:firebase-annotations:16.2.0' - api 'com.google.firebase:firebase-common:21.0.0' - api 'com.google.firebase:firebase-common-ktx:21.0.0' - api 'com.google.firebase:firebase-components:18.0.0' - api 'com.google.android.recaptcha:recaptcha:18.8.0-beta01' + api libs.firebase.annotations + api libs.firebase.common.v2200 + api libs.firebase.common.ktx + api libs.firebase.components.v1800 + api libs.recaptcha.v1871 testImplementation(project(":integ-testing")) { exclude group: 'com.google.firebase', module: 'firebase-common' diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml index 416bb3e8aee..eaa6ceba966 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml @@ -1,8 +1,7 @@ - - + > getComponents() { return (Application) context.getApplicationContext(); }) .build(), - Component.builder(SiteKey.class) + Component.builder(FirebaseExecutors.class) .name(LIBRARY_NAME) - .factory( - container -> new SiteKey(RecaptchaEnterpriseAppCheckProviderFactory.getSiteKey())) - .build(), - Component.builder(RecaptchaEnterpriseAppCheckProvider.class) - .name(LIBRARY_NAME) - .add(Dependency.required(FirebaseApp.class)) - .add(Dependency.required(Application.class)) - .add(Dependency.required(SiteKey.class)) .add(Dependency.required(liteExecutor)) .add(Dependency.required(blockingExecutor)) .factory( - (container -> - new RecaptchaEnterpriseAppCheckProvider( - container.get(FirebaseApp.class), - container.get(Application.class), - container.get(SiteKey.class), - container.get(liteExecutor), - container.get(blockingExecutor)))) + container -> + new FirebaseExecutors( + container.get(liteExecutor), container.get(blockingExecutor))) .build(), LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)); } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index db54812884b..df47019b8b5 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -1,44 +1,72 @@ package com.google.firebase.appcheck.recaptchaenterprise; +import android.app.Application; + import androidx.annotation.NonNull; import com.google.firebase.FirebaseApp; import com.google.firebase.appcheck.AppCheckProvider; import com.google.firebase.appcheck.AppCheckProviderFactory; import com.google.firebase.appcheck.FirebaseAppCheck; +import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * Implementation of an {@link AppCheckProviderFactory} that builds
* {@link RecaptchaEnterpriseAppCheckProvider}s. This is the default implementation. */ public class RecaptchaEnterpriseAppCheckProviderFactory implements AppCheckProviderFactory { - private static volatile RecaptchaEnterpriseAppCheckProviderFactory instance; - private static String siteKey; + private static FirebaseExecutors firebaseExecutors; + private static final Map factoryInstances = + new ConcurrentHashMap<>(); + private final String siteKey; + private volatile RecaptchaEnterpriseAppCheckProvider provider; + + private RecaptchaEnterpriseAppCheckProviderFactory(@NonNull String siteKey) { + this.siteKey = siteKey; + } /** Gets an instance of this class for installation into a {@link FirebaseAppCheck} instance. */ @NonNull public static RecaptchaEnterpriseAppCheckProviderFactory getInstance(@NonNull String siteKey) { - if (instance == null) { - synchronized (RecaptchaEnterpriseAppCheckProviderFactory.class) { - if (instance == null) { - instance = new RecaptchaEnterpriseAppCheckProviderFactory(); - RecaptchaEnterpriseAppCheckProviderFactory.siteKey = siteKey; + RecaptchaEnterpriseAppCheckProviderFactory factory = factoryInstances.get(siteKey); + if (factory == null) { + synchronized (factoryInstances) { + factory = factoryInstances.get(siteKey); + if (factory == null) { + factory = new RecaptchaEnterpriseAppCheckProviderFactory(siteKey); + factoryInstances.put(siteKey, factory); } } } - return instance; - } - - @NonNull - public static String getSiteKey() { - return siteKey; + return factory; } @NonNull @Override @SuppressWarnings("FirebaseUseExplicitDependencies") public AppCheckProvider create(@NonNull FirebaseApp firebaseApp) { - return firebaseApp.get(RecaptchaEnterpriseAppCheckProvider.class); + if (provider == null) { + synchronized (this) { + if (provider == null) { + if (RecaptchaEnterpriseAppCheckProviderFactory.firebaseExecutors == null) { + firebaseExecutors = firebaseApp.get(FirebaseExecutors.class); + } + Application application = firebaseApp.get(Application.class); + + provider = + new RecaptchaEnterpriseAppCheckProvider( + firebaseApp, + application, + siteKey, + firebaseExecutors.getLiteExecutor(), + firebaseExecutors.getBlockingExecutor()); + } + } + } + return provider; } } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java new file mode 100644 index 00000000000..27609777c53 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java @@ -0,0 +1,30 @@ +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import com.google.firebase.annotations.concurrent.Blocking; +import com.google.firebase.annotations.concurrent.Lightweight; + +import java.util.concurrent.Executor; + +/** + * This class encapsulates a {@link com.google.firebase.annotations.concurrent.Lightweight} + * executor and a {@link com.google.firebase.annotations.concurrent.Blocking} executor, + * making them available for various asynchronous operations related to reCAPTCHA Enterprise + * App Check. + **/ +public class FirebaseExecutors { + private final Executor liteExecutor; + private final Executor blockingExecutor; + + public FirebaseExecutors(@Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { + this.liteExecutor = liteExecutor; + this.blockingExecutor = blockingExecutor; + } + + public Executor getLiteExecutor() { + return liteExecutor; + } + + public Executor getBlockingExecutor() { + return blockingExecutor; + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 5c4f9fd2198..39ea2ffb8ef 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -1,6 +1,7 @@ package com.google.firebase.appcheck.recaptchaenterprise.internal; import android.app.Application; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -31,18 +32,19 @@ public class RecaptchaEnterpriseAppCheckProvider implements AppCheckProvider { private final Executor blockingExecutor; private final RetryManager retryManager; private final NetworkClient networkClient; + private static final String TAG = "rCEAppCheckProvider"; public RecaptchaEnterpriseAppCheckProvider( @NonNull FirebaseApp firebaseApp, @NonNull Application application, - @NonNull SiteKey siteKey, + @NonNull String siteKey, @Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { this.liteExecutor = liteExecutor; this.blockingExecutor = blockingExecutor; this.retryManager = new RetryManager(); this.networkClient = new NetworkClient(firebaseApp); - recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey.value()); + recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey); } @VisibleForTesting @@ -93,6 +95,7 @@ private Task getRecaptchaEnterpriseAttestation() { RecaptchaTasksClient client = task.getResult(); return client.executeTask(recaptchaAction); } else { + Log.w(TAG, "Recaptcha task failed", task.getException()); throw Objects.requireNonNull(task.getException()); } }); diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java deleted file mode 100644 index a75f96ed809..00000000000 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKey.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.google.firebase.appcheck.recaptchaenterprise.internal; - -import androidx.annotation.NonNull; -import java.util.Objects; - -public class SiteKey { - private final String value; - - public SiteKey(@NonNull String value) { - this.value = Objects.requireNonNull(value, "Site key cannot be null"); - } - - @NonNull - public String value() { - return value; - } -} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java index 323f303dcd8..3b6ac48ff1c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java @@ -1,24 +1,15 @@ package com.google.firebase.appcheck.recaptchaenterprise; import static com.google.common.truth.Truth.assertThat; - -import android.app.Application; import android.content.Context; - -import com.google.firebase.FirebaseApp; -import com.google.firebase.annotations.concurrent.Blocking; -import com.google.firebase.annotations.concurrent.Lightweight; -import com.google.firebase.appcheck.recaptchaenterprise.internal.SiteKey; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; -import com.google.firebase.components.Qualified; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.List; -import java.util.concurrent.Executor; /** Tests for {@link FirebaseAppCheckRecaptchaEnterpriseRegistrar}. */ @RunWith(RobolectricTestRunner.class) @@ -29,21 +20,10 @@ public void testGetComponents() { new FirebaseAppCheckRecaptchaEnterpriseRegistrar(); List> components = registrar.getComponents(); assertThat(components).isNotEmpty(); - assertThat(components).hasSize(4); + assertThat(components).hasSize(3); Component applicationComponent = components.get(0); assertThat(applicationComponent.getDependencies()) .containsExactly(Dependency.required(Context.class)); assertThat(applicationComponent.isLazy()).isTrue(); - Component siteKeyComponent = components.get(1); - assertThat(siteKeyComponent.isLazy()).isTrue(); - Component appCheckRecaptchaEnterpriseComponent = components.get(2); - assertThat(appCheckRecaptchaEnterpriseComponent.getDependencies()) - .containsExactly( - Dependency.required(FirebaseApp.class), - Dependency.required(Application.class), - Dependency.required(SiteKey.class), - Dependency.required(Qualified.qualified(Lightweight.class, Executor.class)), - Dependency.required(Qualified.qualified(Blocking.class, Executor.class))); - assertThat(appCheckRecaptchaEnterpriseComponent.isLazy()).isTrue(); } } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java index f6b9c2e2fa4..906a5c1614d 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java @@ -11,15 +11,26 @@ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class RecaptchaEnterpriseAppCheckProviderFactoryTest { - static final String SITE_KEY = "siteKey"; + static final String SITE_KEY_1 = "siteKey1"; + static final String SITE_KEY_2 = "siteKey2"; @Test - public void testGetInstance_callTwice_sameInstance() { + public void testGetInstance_callTwiceSameSiteKey_sameInstance() { RecaptchaEnterpriseAppCheckProviderFactory firstInstance = - RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY); + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); RecaptchaEnterpriseAppCheckProviderFactory secondInstance = - RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY); + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); assertThat(firstInstance).isEqualTo(secondInstance); } + + @Test + public void testGetInstance_callTwiceDifferentSiteKey_differentInstance() { + RecaptchaEnterpriseAppCheckProviderFactory firstInstance = + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); + RecaptchaEnterpriseAppCheckProviderFactory secondInstance = + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_2); + + assertThat(firstInstance).isNotEqualTo(secondInstance); + } } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java index 6d09a1f8525..a10470736f2 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -47,7 +47,7 @@ public class RecaptchaEnterpriseAppCheckProviderTest { private static final String RECAPTCHA_ENTERPRISE_TOKEN = "recaptchaEnterpriseToken"; private final Executor liteExecutor = MoreExecutors.directExecutor(); private final Executor blockingExecutor = MoreExecutors.directExecutor(); - private final SiteKey siteKey = new SiteKey("siteKey"); + private final String siteKey = "siteKey"; @Mock private NetworkClient mockNetworkClient; @Mock private Application mockApplication; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java deleted file mode 100644 index 303d16dc93c..00000000000 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/SiteKeyTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.google.firebase.appcheck.recaptchaenterprise.internal; - -import static org.junit.Assert.assertThrows; - -import com.google.common.truth.Truth; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -/** Tests for {@Link SiteKey}. */ -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class SiteKeyTest { - private static final String VALUE = "siteKey"; - - @Test - public void testConstructor_nullValue_expectThrows() { - assertThrows(NullPointerException.class, () -> new SiteKey(null)); - } - - @Test - public void testConstructor_nonNullValue_succeeds() { - SiteKey siteKey = new SiteKey(VALUE); - Truth.assertThat(siteKey.value()).isEqualTo(VALUE); - } -} From c8e295b5366cf4b22bcf6288b8cfa68c1fddb298 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Fri, 11 Jul 2025 04:42:44 +0000 Subject: [PATCH 09/23] Resolves comments --- .../FirebaseAppCheckRecaptchaEnterpriseRegistrar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index 22f5e5cd2d7..e9d8b17cab6 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -25,7 +25,7 @@ */ @KeepForSdk public class FirebaseAppCheckRecaptchaEnterpriseRegistrar implements ComponentRegistrar { - private static final String LIBRARY_NAME = "fire-app-check-recaptcha-enterprise"; + private static final String LIBRARY_NAME = "firebase-app-check-recaptcha-enterprise"; @Override public List> getComponents() { From 5f925b4e303045aaebf2664f20ce5fd9505b9302 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Mon, 14 Jul 2025 15:03:42 +0000 Subject: [PATCH 10/23] feat: Add new library versions --- gradle/libs.versions.toml | 59 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb734c90b18..ccff38658bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,11 +3,11 @@ # it needs to match the protobuf version which grpc has transitive dependency on, which # needs to match the version of grpc that grpc-kotlin has a transitive dependency on. android-lint = "31.3.2" -androidGradlePlugin = "8.6.1" -androidx-test-core="1.5.0" -androidx-test-junit="1.1.5" -androidx-test-truth = "1.6.0" -appcompat = "1.7.1" +androidGradlePlugin = "8.3.2" +androidx-test-core = "1.5.0" +androidx-test-junit = "1.1.5" +androidx-test-truth = "1.5.0" +appcompat = "1.7.0" autoValueParcel = "0.2.6" autovalue = "1.10.1" awaitility = "3.1.0" @@ -17,21 +17,24 @@ cardview = "1.0.0" checkerQual = "2.5.2" constraintlayout = "2.1.4" coreKtx = "1.12.0" -coroutines = "1.9.0" -dagger = "2.51" # Don't bump above 2.51 as it causes a bug in AppDistro FeedbackSender JPEG code +coroutines = "1.7.3" +dagger = "2.43.2" datastore = "1.1.3" dexmaker = "2.28.1" dexmakerVersion = "1.2" espressoCore = "3.6.1" featureDelivery = "2.1.0" +firebaseAnnotations = "17.0.0" firebaseAppdistributionGradle = "5.1.1" -firebaseCommon = "22.0.0" -firebaseComponents = "19.0.0" +firebaseCommon = "21.0.0" +firebaseCommonVersion = "22.0.0" +firebaseComponents = "18.0.1" +firebaseComponentsVersion = "18.0.0" firebaseCrashlyticsGradle = "3.0.4" glide = "4.16.0" googleApiClient = "1.30.9" googleServices = "4.3.15" -gradleErrorpronePlugin = "4.2.0" +gradleErrorpronePlugin = "3.1.0" grpc = "1.62.2" grpcKotlin = "1.4.1" hamcrest = "2.2" @@ -45,16 +48,17 @@ javalite = "3.25.5" jsonassert = "1.5.0" kotest = "5.9.0" # Do not use 5.9.1 because it reverts the fix for https://github.com/kotest/kotest/issues/3981 kotestAssertionsCore = "5.8.1" -kotlin = "2.0.21" -ktorVersion = "3.0.3" +kotlin = "1.8.22" +ktorVersion = "2.3.2" legacySupportV4 = "1.0.0" lifecycleProcess = "2.3.1" material = "1.12.0" -mavenResolverApi = "1.9.23" +mavenResolverApi = "1.9.22" mavenResolverProvider = "3.9.9" -mockito = "5.16.0" +mockito = "5.2.0" mockitoAndroid = "3.4.0" -mockk = "1.14.0" # Do not use 1.14.2 or above due to a bug in spyK and bumps kotlin to 2.1.x +mockitoCore = "5.12.0" +mockk = "1.13.11" playServicesCloudMessaging = "17.2.0" playServicesStats = "17.0.2" playServicesVision = "20.1.3" @@ -63,14 +67,16 @@ protobufGradlePlugin = "0.9.4" protobufjavautil = "3.25.5" protoc = "3.25.5" quickcheck = "0.6" -reactiveStreams = "1.0.4" -robolectric = "4.12" # Do not use >4.12 as it breaks the build +reactiveStreams = "1.0.3" +recaptcha = "18.7.1" +recaptchaVersion = "18.8.0-beta01" +robolectric = "4.12" runner = "1.0.2" rxandroid = "2.0.2" rxjava = "2.1.14" -serialization = "1.7.3" +serialization = "1.5.1" slf4jNop = "2.0.9" -spotless = "7.0.4" +spotless = "7.0.0.BETA3" testServices = "1.2.0" truth = "1.4.4" truthProtoExtension = "1.0" @@ -93,7 +99,6 @@ androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "card androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx-core = { module = "androidx.core:core", version = "1.13.1" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } -androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } androidx-espresso-idling-resource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "espressoCore" } @@ -115,12 +120,17 @@ dexmaker = { module = "com.linkedin.dexmaker:dexmaker", version.ref = "dexmaker" errorprone-annotations = { module = "com.google.errorprone:error_prone_annotations", version = "2.26.0" } feature-delivery = { module = "com.google.android.play:feature-delivery", version.ref = "featureDelivery" } findbugs-jsr305 = { module = "com.google.code.findbugs:jsr305", version = "3.0.2" } +firebase-annotations = { module = "com.google.firebase:firebase-annotations", version.ref = "firebaseAnnotations" } firebase-appdistribution-gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppdistributionGradle" } firebase-common = { module = "com.google.firebase:firebase-common", version.ref = "firebaseCommon" } +firebase-common-ktx = { module = "com.google.firebase:firebase-common-ktx", version.ref = "firebaseCommon" } +firebase-common-v2200 = { module = "com.google.firebase:firebase-common", version.ref = "firebaseCommonVersion" } firebase-components = { module = "com.google.firebase:firebase-components", version.ref = "firebaseComponents" } +firebase-components-v1800 = { module = "com.google.firebase:firebase-components", version.ref = "firebaseComponentsVersion" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } google-api-client = { module = "com.google.api-client:google-api-client", version.ref = "googleApiClient" } google-dexmaker = { module = "com.google.dexmaker:dexmaker", version.ref = "dexmakerVersion" } +google-recaptcha = { module = "com.google.android.recaptcha:recaptcha", version.ref = "recaptchaVersion" } grpc-android = { module = "io.grpc:grpc-android", version.ref = "grpc" } grpc-kotlin-stub = { module = "io.grpc:grpc-kotlin-stub", version.ref = "grpcKotlin" } grpc-okhttp = { module = "io.grpc:grpc-okhttp", version.ref = "grpc" } @@ -128,7 +138,7 @@ grpc-protobuf-lite = { module = "io.grpc:grpc-protobuf-lite", version.ref = "grp grpc-protoc-gen-java = { module = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" } grpc-protoc-gen-kotlin = { module = "io.grpc:protoc-gen-grpc-kotlin", version.ref = "grpcKotlin" } grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" } -grpc-testing = { module= "io.grpc:grpc-testing", version.ref="grpc" } +grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" } hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } hamcrest-junit = { module = "org.hamcrest:hamcrest-junit", version.ref = "hamcrestJunit" } hamcrest-library = { module = "org.hamcrest:hamcrest-library", version.ref = "hamcrestLibrary" } @@ -166,6 +176,7 @@ maven-resolver-provider = { module = "org.apache.maven:maven-resolver-provider", maven-resolver-transport-file = { module = "org.apache.maven.resolver:maven-resolver-transport-file", version.ref = "mavenResolverApi" } maven-resolver-transport-http = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "mavenResolverApi" } maven-resolver-util = { module = "org.apache.maven.resolver:maven-resolver-util", version.ref = "mavenResolverApi" } +mockito-core-v5120 = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "3.12.13" } org-json = { module = "org.json:json", version = "20240303" } play-services-cloud-messaging = { module = "com.google.android.gms:play-services-cloud-messaging", version.ref = "playServicesCloudMessaging" } @@ -195,11 +206,13 @@ kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-te mockito-android = { module = "org.mockito:mockito-android", version.ref = "mockitoAndroid" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } mockito-dexmaker = { module = "com.linkedin.dexmaker:dexmaker-mockito", version = "2.28.3" } +mockito-mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } protobuf-java-util = { module = "com.google.protobuf:protobuf-java-util", version.ref = "protobufjavautil" } quickcheck = { module = "net.java:quickcheck", version.ref = "quickcheck" } reactive-streams = { module = "org.reactivestreams:reactive-streams", version.ref = "reactiveStreams" } +recaptcha-v1871 = { module = "com.google.android.recaptcha:recaptcha", version.ref = "recaptcha" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } runner = { module = "com.android.support.test:runner", version.ref = "runner" } rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" } @@ -209,13 +222,13 @@ spotless-plugin-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradl truth = { module = "com.google.truth:truth", version.ref = "truth" } truth-liteproto-extension = { module = "com.google.truth.extensions:truth-liteproto-extension", version.ref = "truth" } truth-proto-extension = { module = "com.google.truth.extensions:truth-proto-extension", version.ref = "truthProtoExtension" } -turbine = { module = "app.cash.turbine:turbine", version = "1.2.1" } +turbine = { module = "app.cash.turbine:turbine", version = "1.2.0" } # Remove three-ten-abp once minSdkVersion is changed to 26 or later, and, instead use the # correspondingly-named classes from the java.time package, which should be drop-in replacements. # Do not use three-ten-abp in production code (it's only for tests) because it has performance # issues. -testonly-three-ten-abp = { module = "com.jakewharton.threetenabp:threetenabp", version = "1.4.9" } +testonly-three-ten-abp = { module = "com.jakewharton.threetenabp:threetenabp", version = "1.4.7" } wiremock-standalone = { module = "com.github.tomakehurst:wiremock-standalone", version.ref = "wiremockStandalone" } [bundles] From 9c34a6eb2d0556757a900842f9191b10e99e590d Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Mon, 14 Jul 2025 15:31:32 +0000 Subject: [PATCH 11/23] Resolves build errors --- .../firebase-appcheck-recaptchaenterprise.gradle | 1 - gradle/libs.versions.toml | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle index 185520fb21a..aee114c6f91 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -7,7 +7,6 @@ firebaseLibrary { releaseNotes { name.set("{{app_check}} Recaptcha Enterprise") versionName.set("appcheck-recaptchaenterprise") - hasKTX.set(false) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ccff38658bc..e56253a2955 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,8 +4,8 @@ # needs to match the version of grpc that grpc-kotlin has a transitive dependency on. android-lint = "31.3.2" androidGradlePlugin = "8.3.2" -androidx-test-core = "1.5.0" -androidx-test-junit = "1.1.5" +androidx-test-core="1.5.0" +androidx-test-junit="1.1.5" androidx-test-truth = "1.5.0" appcompat = "1.7.0" autoValueParcel = "0.2.6" @@ -99,6 +99,7 @@ androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "card androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx-core = { module = "androidx.core:core", version = "1.13.1" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } +androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } androidx-espresso-idling-resource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "espressoCore" } @@ -138,7 +139,7 @@ grpc-protobuf-lite = { module = "io.grpc:grpc-protobuf-lite", version.ref = "grp grpc-protoc-gen-java = { module = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" } grpc-protoc-gen-kotlin = { module = "io.grpc:protoc-gen-grpc-kotlin", version.ref = "grpcKotlin" } grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" } -grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" } +grpc-testing = { module= "io.grpc:grpc-testing", version.ref="grpc" } hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } hamcrest-junit = { module = "org.hamcrest:hamcrest-junit", version.ref = "hamcrestJunit" } hamcrest-library = { module = "org.hamcrest:hamcrest-library", version.ref = "hamcrestLibrary" } From 290a9fb15a3b3305981158a1fe0b652d117e134f Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Mon, 14 Jul 2025 17:37:32 +0000 Subject: [PATCH 12/23] Fix: Apply Spotless formatting to appcheck-recaptchaenterprise module --- ...irebaseAppCheckRecaptchaEnterpriseRegistrar.java | 4 +--- .../RecaptchaEnterpriseAppCheckProviderFactory.java | 2 -- .../ExchangeRecaptchaEnterpriseTokenRequest.java | 1 - .../internal/FirebaseExecutors.java | 13 ++++++------- .../RecaptchaEnterpriseAppCheckProvider.java | 3 --- ...aseAppCheckRecaptchaEnterpriseRegistrarTest.java | 5 ++--- .../RecaptchaEnterpriseAppCheckProviderTest.java | 7 ++----- .../firebase/appcheck/internal/NetworkClient.java | 3 ++- 8 files changed, 13 insertions(+), 25 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index e9d8b17cab6..05a0bc1369c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -2,7 +2,6 @@ import android.app.Application; import android.content.Context; - import com.google.android.gms.common.annotation.KeepForSdk; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; @@ -12,7 +11,6 @@ import com.google.firebase.components.Dependency; import com.google.firebase.components.Qualified; import com.google.firebase.platforminfo.LibraryVersionComponent; - import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -25,7 +23,7 @@ */ @KeepForSdk public class FirebaseAppCheckRecaptchaEnterpriseRegistrar implements ComponentRegistrar { - private static final String LIBRARY_NAME = "firebase-app-check-recaptcha-enterprise"; + private static final String LIBRARY_NAME = "fire-app-check-recaptcha-enterprise"; @Override public List> getComponents() { diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index df47019b8b5..7ff01761cc5 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -1,7 +1,6 @@ package com.google.firebase.appcheck.recaptchaenterprise; import android.app.Application; - import androidx.annotation.NonNull; import com.google.firebase.FirebaseApp; import com.google.firebase.appcheck.AppCheckProvider; @@ -9,7 +8,6 @@ import com.google.firebase.appcheck.FirebaseAppCheck; import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java index 5d20930487e..775c3f1fc60 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java @@ -2,7 +2,6 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; - import org.json.JSONException; import org.json.JSONObject; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java index 27609777c53..f5d31cf0ba6 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java @@ -2,20 +2,19 @@ import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; - import java.util.concurrent.Executor; /** - * This class encapsulates a {@link com.google.firebase.annotations.concurrent.Lightweight} - * executor and a {@link com.google.firebase.annotations.concurrent.Blocking} executor, - * making them available for various asynchronous operations related to reCAPTCHA Enterprise - * App Check. - **/ + * This class encapsulates a {@link com.google.firebase.annotations.concurrent.Lightweight} executor + * and a {@link com.google.firebase.annotations.concurrent.Blocking} executor, making them available + * for various asynchronous operations related to reCAPTCHA Enterprise App Check. + */ public class FirebaseExecutors { private final Executor liteExecutor; private final Executor blockingExecutor; - public FirebaseExecutors(@Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { + public FirebaseExecutors( + @Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { this.liteExecutor = liteExecutor; this.blockingExecutor = blockingExecutor; } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 39ea2ffb8ef..42ac0a9bd9c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -2,10 +2,8 @@ import android.app.Application; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; - import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.android.recaptcha.Recaptcha; @@ -19,7 +17,6 @@ import com.google.firebase.appcheck.internal.DefaultAppCheckToken; import com.google.firebase.appcheck.internal.NetworkClient; import com.google.firebase.appcheck.internal.RetryManager; - import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.Executor; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java index 3b6ac48ff1c..7950e5a5972 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java @@ -1,16 +1,15 @@ package com.google.firebase.appcheck.recaptchaenterprise; import static com.google.common.truth.Truth.assertThat; + import android.content.Context; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; - +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import java.util.List; - /** Tests for {@link FirebaseAppCheckRecaptchaEnterpriseRegistrar}. */ @RunWith(RobolectricTestRunner.class) public class FirebaseAppCheckRecaptchaEnterpriseRegistrarTest { diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java index a10470736f2..26033a638a1 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -8,7 +8,6 @@ import static org.mockito.Mockito.when; import android.app.Application; - import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.android.recaptcha.RecaptchaAction; @@ -22,7 +21,8 @@ import com.google.firebase.appcheck.internal.NetworkClient; import com.google.firebase.appcheck.internal.RetryManager; import com.google.firebase.concurrent.TestOnlyExecutors; - +import java.io.IOException; +import java.util.concurrent.Executor; import org.json.JSONException; import org.json.JSONObject; import org.junit.Before; @@ -35,9 +35,6 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.LooperMode; -import java.io.IOException; -import java.util.concurrent.Executor; - /** Tests for {@link RecaptchaEnterpriseAppCheckProvider}. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) diff --git a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java index 6ae382d0946..04dd11bcd49 100644 --- a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java +++ b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java @@ -179,7 +179,8 @@ private String makeNetworkRequest( ? urlConnection.getInputStream() : urlConnection.getErrorStream(); StringBuilder response = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { response.append(line); From b6dea8dfb3070ceda78211c6e5e1f2b95c554a51 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Mon, 14 Jul 2025 17:37:32 +0000 Subject: [PATCH 13/23] Fix: Apply Spotless formatting to appcheck-recaptchaenterprise module --- .../com/google/firebase/appcheck/internal/NetworkClient.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java index 04dd11bcd49..fa91e6f8323 100644 --- a/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java +++ b/appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java @@ -19,11 +19,9 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; - import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; - import com.google.android.gms.common.util.AndroidUtilsLight; import com.google.android.gms.common.util.Hex; import com.google.android.gms.tasks.Tasks; @@ -33,7 +31,6 @@ import com.google.firebase.appcheck.FirebaseAppCheck; import com.google.firebase.heartbeatinfo.HeartBeatController; import com.google.firebase.inject.Provider; - import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; @@ -45,7 +42,6 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; - import org.json.JSONException; /** From ff30974d722fd4c1a1c0b56321e459901d848ce7 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Mon, 14 Jul 2025 18:08:43 +0000 Subject: [PATCH 14/23] Fix: Add api.txt file --- appcheck/firebase-appcheck-recaptchaenterprise/api.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/api.txt diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/api.txt b/appcheck/firebase-appcheck-recaptchaenterprise/api.txt new file mode 100644 index 00000000000..99d21ff038c --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/api.txt @@ -0,0 +1,10 @@ +// Signature format: 3.0 +package com.google.firebase.appcheck.recaptchaenterprise { + + public class RecaptchaEnterpriseAppCheckProviderFactory implements com.google.firebase.appcheck.AppCheckProviderFactory { + method public com.google.firebase.appcheck.AppCheckProvider create(com.google.firebase.FirebaseApp); + method public static com.google.firebase.appcheck.recaptchaenterprise.RecaptchaEnterpriseAppCheckProviderFactory getInstance(String); + } + +} + From 874ef41ff3978526bc2b5384e4ae4e45bcd43984 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Wed, 16 Jul 2025 15:37:11 +0000 Subject: [PATCH 15/23] Resolves comments --- ...rebase-appcheck-recaptchaenterprise.gradle | 26 +++++--- .../src/main/AndroidManifest.xml | 14 ++++- ...eAppCheckRecaptchaEnterpriseRegistrar.java | 22 +++++-- ...tchaEnterpriseAppCheckProviderFactory.java | 14 +++++ ...changeRecaptchaEnterpriseTokenRequest.java | 14 +++++ .../internal/FirebaseExecutors.java | 14 +++++ .../RecaptchaEnterpriseAppCheckProvider.java | 28 ++++++++- .../internal/package-info.java | 14 +++++ ...CheckRecaptchaEnterpriseRegistrarTest.java | 14 +++++ ...EnterpriseAppCheckProviderFactoryTest.java | 14 +++++ ...geRecaptchaEnterpriseTokenRequestTest.java | 16 ++++- ...captchaEnterpriseAppCheckProviderTest.java | 54 ++++++----------- gradle/libs.versions.toml | 60 +++++++------------ 13 files changed, 216 insertions(+), 88 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle index aee114c6f91..ca62f064e17 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + plugins { id 'firebase-library' } @@ -32,14 +46,10 @@ android { } dependencies { - javadocClasspath libs.autovalue.annotations - api project(':appcheck:firebase-appcheck') - api libs.firebase.annotations - api libs.firebase.common.v2200 - api libs.firebase.common.ktx - api libs.firebase.components.v1800 - api libs.recaptcha.v1871 + api 'com.google.firebase:firebase-common' + api 'com.google.firebase:firebase-components' + api 'com.google.android.recaptcha:recaptcha:18.7.1' testImplementation(project(":integ-testing")) { exclude group: 'com.google.firebase', module: 'firebase-common' @@ -48,6 +58,6 @@ dependencies { testImplementation libs.androidx.test.core testImplementation libs.truth testImplementation libs.junit - testImplementation libs.mockito.core.v5120 + testImplementation libs.mockito.core testImplementation libs.robolectric } \ No newline at end of file diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml index eaa6ceba966..7d331ed90c3 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/AndroidManifest.xml @@ -1,4 +1,16 @@ - + + + + + + + + + + + + + diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index 05a0bc1369c..fefc9525b9b 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -1,8 +1,22 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise; import android.app.Application; -import android.content.Context; import com.google.android.gms.common.annotation.KeepForSdk; +import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; @@ -33,11 +47,11 @@ public List> getComponents() { return Arrays.asList( Component.builder(Application.class) .name(LIBRARY_NAME) - .add(Dependency.required(Context.class)) + .add(Dependency.required(FirebaseApp.class)) .factory( container -> { - Context context = container.get(Context.class); - return (Application) context.getApplicationContext(); + FirebaseApp firebaseApp = container.get(FirebaseApp.class); + return (Application) firebaseApp.getApplicationContext(); }) .build(), Component.builder(FirebaseExecutors.class) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index 7ff01761cc5..b119db8aa5d 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise; import android.app.Application; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java index 775c3f1fc60..76107a7ad0e 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequest.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise.internal; import androidx.annotation.NonNull; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java index f5d31cf0ba6..b117965e47c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise.internal; import com.google.firebase.annotations.concurrent.Blocking; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 42ac0a9bd9c..326c54e8755 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise.internal; import android.app.Application; @@ -21,6 +35,18 @@ import java.util.Objects; import java.util.concurrent.Executor; +/** + * An implementation of {@link AppCheckProvider} that uses reCAPTCHA Enterprise for device + * attestation. + * + *

This class orchestrates the flow: + * + *

    + *
  1. Obtain a reCAPTCHA token via {@code RecaptchaTasksClient}. + *
  2. Exchange the reCAPTCHA token with the Firebase App Check backend using {@link + * NetworkClient} to receive a Firebase App Check token. + *
+ */ public class RecaptchaEnterpriseAppCheckProvider implements AppCheckProvider { private final RecaptchaAction recaptchaAction = RecaptchaAction.custom("fire_app_check"); @@ -93,7 +119,7 @@ private Task getRecaptchaEnterpriseAttestation() { return client.executeTask(recaptchaAction); } else { Log.w(TAG, "Recaptcha task failed", task.getException()); - throw Objects.requireNonNull(task.getException()); + return Tasks.forException((Objects.requireNonNull(task.getException()))); } }); } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java index 7d1cc21f754..baae7a935b7 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/package-info.java @@ -1,2 +1,16 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /** @hide */ package com.google.firebase.appcheck.recaptchaenterprise.internal; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java index 7950e5a5972..998234989bb 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise; import static com.google.common.truth.Truth.assertThat; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java index 906a5c1614d..f65d0613d41 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise; import static com.google.common.truth.Truth.assertThat; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java index 0e62da3d205..760e79b4393 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise.internal; import static com.google.common.truth.Truth.assertThat; @@ -6,11 +20,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; /** Tests for {@link ExchangeRecaptchaEnterpriseTokenRequest}. */ @RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) public class ExchangeRecaptchaEnterpriseTokenRequestTest { private static final String RECAPTCHA_ENTERPRISE_TOKEN = "recaptchaEnterpriseToken"; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java index 26033a638a1..0937dc9d21c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -1,3 +1,17 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.firebase.appcheck.recaptchaenterprise.internal; import static com.google.common.truth.Truth.assertThat; @@ -29,6 +43,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -52,6 +67,9 @@ public class RecaptchaEnterpriseAppCheckProviderTest { @Mock RecaptchaTasksClient mockRecaptchaTasksClient; @Mock RetryManager mockRetryManager; + @Captor private ArgumentCaptor recaptchaActionCaptor; + @Captor private ArgumentCaptor requestCaptor; + @Before public void setup() { MockitoAnnotations.openMocks(this); @@ -70,19 +88,6 @@ public void testPublicConstructor_nullFirebaseApp_expectThrows() { TestOnlyExecutors.blocking())); } - @Test - public void testPublicConstructor_nullApplication_expectThrows() { - assertThrows( - NullPointerException.class, - () -> - new RecaptchaEnterpriseAppCheckProvider( - mockFirebaseApp, - null, - siteKey, - TestOnlyExecutors.lite(), - TestOnlyExecutors.blocking())); - } - @Test public void testPublicConstructor_nullSiteKey_expectThrows() { assertThrows( @@ -91,7 +96,7 @@ public void testPublicConstructor_nullSiteKey_expectThrows() { new RecaptchaEnterpriseAppCheckProvider( mockFirebaseApp, mockApplication, - siteKey, + null, TestOnlyExecutors.lite(), TestOnlyExecutors.blocking())); } @@ -120,34 +125,13 @@ public void getToken_onSuccess_setsTaskResult() throws Exception { assertThat(token).isInstanceOf(DefaultAppCheckToken.class); assertThat(token.getToken()).isEqualTo(APP_CHECK_TOKEN); - ArgumentCaptor recaptchaActionCaptor = - ArgumentCaptor.forClass(RecaptchaAction.class); verify(mockRecaptchaTasksClient).executeTask(recaptchaActionCaptor.capture()); assertThat(recaptchaActionCaptor.getValue().getAction()).isEqualTo("fire_app_check"); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(byte[].class); verify(mockNetworkClient) .exchangeAttestationForAppCheckToken( requestCaptor.capture(), eq(NetworkClient.RECAPTCHA_ENTERPRISE), eq(mockRetryManager)); } - @Test - public void getToken_invalidSiteKey_returnException() { - Exception exception = new Exception("Site key invalid"); - when(mockRecaptchaTasksClient.executeTask(any(RecaptchaAction.class))) - .thenReturn(Tasks.forException(exception)); - - RecaptchaEnterpriseAppCheckProvider provider = - new RecaptchaEnterpriseAppCheckProvider( - liteExecutor, - blockingExecutor, - mockRetryManager, - mockNetworkClient, - mockRecaptchaTasksClient); - Task task = provider.getToken(); - assertThat(task.isSuccessful()).isFalse(); - assertThat(task.getException()).isEqualTo(exception); - } - @Test public void getToken_recaptchaFails_returnException() { Exception exception = new Exception("Recaptcha error"); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e56253a2955..47c128231f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,11 +3,11 @@ # it needs to match the protobuf version which grpc has transitive dependency on, which # needs to match the version of grpc that grpc-kotlin has a transitive dependency on. android-lint = "31.3.2" -androidGradlePlugin = "8.3.2" -androidx-test-core="1.5.0" -androidx-test-junit="1.1.5" -androidx-test-truth = "1.5.0" -appcompat = "1.7.0" +androidGradlePlugin = "8.6.1" +androidx-test-core = "1.5.0" +androidx-test-junit = "1.1.5" +androidx-test-truth = "1.6.0" +appcompat = "1.7.1" autoValueParcel = "0.2.6" autovalue = "1.10.1" awaitility = "3.1.0" @@ -17,24 +17,21 @@ cardview = "1.0.0" checkerQual = "2.5.2" constraintlayout = "2.1.4" coreKtx = "1.12.0" -coroutines = "1.7.3" -dagger = "2.43.2" +coroutines = "1.9.0" +dagger = "2.51" # Don't bump above 2.51 as it causes a bug in AppDistro FeedbackSender JPEG code datastore = "1.1.3" dexmaker = "2.28.1" dexmakerVersion = "1.2" espressoCore = "3.6.1" featureDelivery = "2.1.0" -firebaseAnnotations = "17.0.0" firebaseAppdistributionGradle = "5.1.1" -firebaseCommon = "21.0.0" -firebaseCommonVersion = "22.0.0" -firebaseComponents = "18.0.1" -firebaseComponentsVersion = "18.0.0" +firebaseCommon = "22.0.0" +firebaseComponents = "19.0.0" firebaseCrashlyticsGradle = "3.0.4" glide = "4.16.0" googleApiClient = "1.30.9" googleServices = "4.3.15" -gradleErrorpronePlugin = "3.1.0" +gradleErrorpronePlugin = "4.2.0" grpc = "1.62.2" grpcKotlin = "1.4.1" hamcrest = "2.2" @@ -48,17 +45,16 @@ javalite = "3.25.5" jsonassert = "1.5.0" kotest = "5.9.0" # Do not use 5.9.1 because it reverts the fix for https://github.com/kotest/kotest/issues/3981 kotestAssertionsCore = "5.8.1" -kotlin = "1.8.22" -ktorVersion = "2.3.2" +kotlin = "2.0.21" +ktorVersion = "3.0.3" legacySupportV4 = "1.0.0" lifecycleProcess = "2.3.1" material = "1.12.0" -mavenResolverApi = "1.9.22" +mavenResolverApi = "1.9.23" mavenResolverProvider = "3.9.9" -mockito = "5.2.0" +mockito = "5.16.0" mockitoAndroid = "3.4.0" -mockitoCore = "5.12.0" -mockk = "1.13.11" +mockk = "1.14.0" # Do not use 1.14.2 or above due to a bug in spyK and bumps kotlin to 2.1.x playServicesCloudMessaging = "17.2.0" playServicesStats = "17.0.2" playServicesVision = "20.1.3" @@ -67,16 +63,14 @@ protobufGradlePlugin = "0.9.4" protobufjavautil = "3.25.5" protoc = "3.25.5" quickcheck = "0.6" -reactiveStreams = "1.0.3" -recaptcha = "18.7.1" -recaptchaVersion = "18.8.0-beta01" -robolectric = "4.12" +reactiveStreams = "1.0.4" +robolectric = "4.12" # Do not use >4.12 as it breaks the build runner = "1.0.2" rxandroid = "2.0.2" rxjava = "2.1.14" -serialization = "1.5.1" +serialization = "1.7.3" slf4jNop = "2.0.9" -spotless = "7.0.0.BETA3" +spotless = "7.0.4" testServices = "1.2.0" truth = "1.4.4" truthProtoExtension = "1.0" @@ -121,17 +115,12 @@ dexmaker = { module = "com.linkedin.dexmaker:dexmaker", version.ref = "dexmaker" errorprone-annotations = { module = "com.google.errorprone:error_prone_annotations", version = "2.26.0" } feature-delivery = { module = "com.google.android.play:feature-delivery", version.ref = "featureDelivery" } findbugs-jsr305 = { module = "com.google.code.findbugs:jsr305", version = "3.0.2" } -firebase-annotations = { module = "com.google.firebase:firebase-annotations", version.ref = "firebaseAnnotations" } firebase-appdistribution-gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppdistributionGradle" } firebase-common = { module = "com.google.firebase:firebase-common", version.ref = "firebaseCommon" } -firebase-common-ktx = { module = "com.google.firebase:firebase-common-ktx", version.ref = "firebaseCommon" } -firebase-common-v2200 = { module = "com.google.firebase:firebase-common", version.ref = "firebaseCommonVersion" } firebase-components = { module = "com.google.firebase:firebase-components", version.ref = "firebaseComponents" } -firebase-components-v1800 = { module = "com.google.firebase:firebase-components", version.ref = "firebaseComponentsVersion" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } google-api-client = { module = "com.google.api-client:google-api-client", version.ref = "googleApiClient" } google-dexmaker = { module = "com.google.dexmaker:dexmaker", version.ref = "dexmakerVersion" } -google-recaptcha = { module = "com.google.android.recaptcha:recaptcha", version.ref = "recaptchaVersion" } grpc-android = { module = "io.grpc:grpc-android", version.ref = "grpc" } grpc-kotlin-stub = { module = "io.grpc:grpc-kotlin-stub", version.ref = "grpcKotlin" } grpc-okhttp = { module = "io.grpc:grpc-okhttp", version.ref = "grpc" } @@ -139,7 +128,7 @@ grpc-protobuf-lite = { module = "io.grpc:grpc-protobuf-lite", version.ref = "grp grpc-protoc-gen-java = { module = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" } grpc-protoc-gen-kotlin = { module = "io.grpc:protoc-gen-grpc-kotlin", version.ref = "grpcKotlin" } grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" } -grpc-testing = { module= "io.grpc:grpc-testing", version.ref="grpc" } +grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" } hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } hamcrest-junit = { module = "org.hamcrest:hamcrest-junit", version.ref = "hamcrestJunit" } hamcrest-library = { module = "org.hamcrest:hamcrest-library", version.ref = "hamcrestLibrary" } @@ -177,7 +166,6 @@ maven-resolver-provider = { module = "org.apache.maven:maven-resolver-provider", maven-resolver-transport-file = { module = "org.apache.maven.resolver:maven-resolver-transport-file", version.ref = "mavenResolverApi" } maven-resolver-transport-http = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "mavenResolverApi" } maven-resolver-util = { module = "org.apache.maven.resolver:maven-resolver-util", version.ref = "mavenResolverApi" } -mockito-core-v5120 = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "3.12.13" } org-json = { module = "org.json:json", version = "20240303" } play-services-cloud-messaging = { module = "com.google.android.gms:play-services-cloud-messaging", version.ref = "playServicesCloudMessaging" } @@ -207,13 +195,11 @@ kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-te mockito-android = { module = "org.mockito:mockito-android", version.ref = "mockitoAndroid" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } mockito-dexmaker = { module = "com.linkedin.dexmaker:dexmaker-mockito", version = "2.28.3" } -mockito-mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } protobuf-java-util = { module = "com.google.protobuf:protobuf-java-util", version.ref = "protobufjavautil" } quickcheck = { module = "net.java:quickcheck", version.ref = "quickcheck" } reactive-streams = { module = "org.reactivestreams:reactive-streams", version.ref = "reactiveStreams" } -recaptcha-v1871 = { module = "com.google.android.recaptcha:recaptcha", version.ref = "recaptcha" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } runner = { module = "com.android.support.test:runner", version.ref = "runner" } rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" } @@ -223,13 +209,13 @@ spotless-plugin-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradl truth = { module = "com.google.truth:truth", version.ref = "truth" } truth-liteproto-extension = { module = "com.google.truth.extensions:truth-liteproto-extension", version.ref = "truth" } truth-proto-extension = { module = "com.google.truth.extensions:truth-proto-extension", version.ref = "truthProtoExtension" } -turbine = { module = "app.cash.turbine:turbine", version = "1.2.0" } +turbine = { module = "app.cash.turbine:turbine", version = "1.2.1" } # Remove three-ten-abp once minSdkVersion is changed to 26 or later, and, instead use the # correspondingly-named classes from the java.time package, which should be drop-in replacements. # Do not use three-ten-abp in production code (it's only for tests) because it has performance # issues. -testonly-three-ten-abp = { module = "com.jakewharton.threetenabp:threetenabp", version = "1.4.7" } +testonly-three-ten-abp = { module = "com.jakewharton.threetenabp:threetenabp", version = "1.4.9" } wiremock-standalone = { module = "com.github.tomakehurst:wiremock-standalone", version.ref = "wiremockStandalone" } [bundles] @@ -252,4 +238,4 @@ spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } protobuf = { id = "com.google.protobuf", version.ref = "protobufGradlePlugin" } errorprone = { id = "net.ltgt.errorprone", version.ref = "gradleErrorpronePlugin" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } -crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsGradle" } +crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsGradle" } \ No newline at end of file From f30a68e33c34502d5b20c5c002710d1fa992f306 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Thu, 17 Jul 2025 05:01:13 +0000 Subject: [PATCH 16/23] Resolves comments --- ...irebase-appcheck-recaptchaenterprise.gradle | 1 + ...seAppCheckRecaptchaEnterpriseRegistrar.java | 11 ----------- ...ptchaEnterpriseAppCheckProviderFactory.java | 15 +++------------ .../RecaptchaEnterpriseAppCheckProvider.java | 18 ++++++++++++++---- ...ngeRecaptchaEnterpriseTokenRequestTest.java | 3 --- 5 files changed, 18 insertions(+), 30 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle index ca62f064e17..1f6ab23ece6 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -60,4 +60,5 @@ dependencies { testImplementation libs.junit testImplementation libs.mockito.core testImplementation libs.robolectric + testImplementation libs.org.json } \ No newline at end of file diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index fefc9525b9b..9630946df26 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -14,9 +14,7 @@ package com.google.firebase.appcheck.recaptchaenterprise; -import android.app.Application; import com.google.android.gms.common.annotation.KeepForSdk; -import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; @@ -45,15 +43,6 @@ public List> getComponents() { Qualified blockingExecutor = Qualified.qualified(Blocking.class, Executor.class); return Arrays.asList( - Component.builder(Application.class) - .name(LIBRARY_NAME) - .add(Dependency.required(FirebaseApp.class)) - .factory( - container -> { - FirebaseApp firebaseApp = container.get(FirebaseApp.class); - return (Application) firebaseApp.getApplicationContext(); - }) - .build(), Component.builder(FirebaseExecutors.class) .name(LIBRARY_NAME) .add(Dependency.required(liteExecutor)) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index b119db8aa5d..3ebc4ee55ae 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -44,17 +44,8 @@ private RecaptchaEnterpriseAppCheckProviderFactory(@NonNull String siteKey) { /** Gets an instance of this class for installation into a {@link FirebaseAppCheck} instance. */ @NonNull public static RecaptchaEnterpriseAppCheckProviderFactory getInstance(@NonNull String siteKey) { - RecaptchaEnterpriseAppCheckProviderFactory factory = factoryInstances.get(siteKey); - if (factory == null) { - synchronized (factoryInstances) { - factory = factoryInstances.get(siteKey); - if (factory == null) { - factory = new RecaptchaEnterpriseAppCheckProviderFactory(siteKey); - factoryInstances.put(siteKey, factory); - } - } - } - return factory; + return factoryInstances.computeIfAbsent( + siteKey, RecaptchaEnterpriseAppCheckProviderFactory::new); } @NonNull @@ -67,7 +58,7 @@ public AppCheckProvider create(@NonNull FirebaseApp firebaseApp) { if (RecaptchaEnterpriseAppCheckProviderFactory.firebaseExecutors == null) { firebaseExecutors = firebaseApp.get(FirebaseExecutors.class); } - Application application = firebaseApp.get(Application.class); + Application application = (Application) firebaseApp.getApplicationContext(); provider = new RecaptchaEnterpriseAppCheckProvider( diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 326c54e8755..166cf73d801 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -43,18 +43,20 @@ * *
    *
  1. Obtain a reCAPTCHA token via {@code RecaptchaTasksClient}. - *
  2. Exchange the reCAPTCHA token with the Firebase App Check backend using {@link - * NetworkClient} to receive a Firebase App Check token. + *
  3. Exchange the reCAPTCHA token with the Firebase App Check backend to receive a Firebase App + * Check token. *
*/ public class RecaptchaEnterpriseAppCheckProvider implements AppCheckProvider { private final RecaptchaAction recaptchaAction = RecaptchaAction.custom("fire_app_check"); - private final Task recaptchaTasksClientTask; + private volatile Task recaptchaTasksClientTask; private final Executor liteExecutor; private final Executor blockingExecutor; private final RetryManager retryManager; private final NetworkClient networkClient; + private String siteKey; + private Application application; private static final String TAG = "rCEAppCheckProvider"; public RecaptchaEnterpriseAppCheckProvider( @@ -63,11 +65,12 @@ public RecaptchaEnterpriseAppCheckProvider( @NonNull String siteKey, @Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { + this.application = application; + this.siteKey = siteKey; this.liteExecutor = liteExecutor; this.blockingExecutor = blockingExecutor; this.retryManager = new RetryManager(); this.networkClient = new NetworkClient(firebaseApp); - recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey); } @VisibleForTesting @@ -111,6 +114,13 @@ public Task getToken() { @NonNull private Task getRecaptchaEnterpriseAttestation() { + if (recaptchaTasksClientTask == null) { + synchronized (this) { + if (recaptchaTasksClientTask == null) { + recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey); + } + } + } return recaptchaTasksClientTask.continueWithTask( blockingExecutor, task -> { diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java index 760e79b4393..20e48b7eb46 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ExchangeRecaptchaEnterpriseTokenRequestTest.java @@ -18,11 +18,8 @@ import org.json.JSONObject; import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; /** Tests for {@link ExchangeRecaptchaEnterpriseTokenRequest}. */ -@RunWith(RobolectricTestRunner.class) public class ExchangeRecaptchaEnterpriseTokenRequestTest { private static final String RECAPTCHA_ENTERPRISE_TOKEN = "recaptchaEnterpriseToken"; From 24056ce9110704b8066546f0461c6f87081258ee Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Thu, 17 Jul 2025 05:05:01 +0000 Subject: [PATCH 17/23] Resolves comments --- ...rebaseAppCheckRecaptchaEnterpriseRegistrarTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java index 998234989bb..1d3ddadca85 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrarTest.java @@ -16,9 +16,7 @@ import static com.google.common.truth.Truth.assertThat; -import android.content.Context; import com.google.firebase.components.Component; -import com.google.firebase.components.Dependency; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,10 +31,8 @@ public void testGetComponents() { new FirebaseAppCheckRecaptchaEnterpriseRegistrar(); List> components = registrar.getComponents(); assertThat(components).isNotEmpty(); - assertThat(components).hasSize(3); - Component applicationComponent = components.get(0); - assertThat(applicationComponent.getDependencies()) - .containsExactly(Dependency.required(Context.class)); - assertThat(applicationComponent.isLazy()).isTrue(); + assertThat(components).hasSize(2); + Component firebaseExecutorsComponent = components.get(0); + assertThat(firebaseExecutorsComponent.isLazy()).isTrue(); } } From 4027c4dfc7e2d0b2dbf37db2b95559c694f05b8d Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Fri, 18 Jul 2025 21:39:16 +0000 Subject: [PATCH 18/23] Resolves comments --- ...rebase-appcheck-recaptchaenterprise.gradle | 4 ++ ...eAppCheckRecaptchaEnterpriseRegistrar.java | 15 ++++-- ...tchaEnterpriseAppCheckProviderFactory.java | 19 ++------ ...eExecutors.java => ProviderComponent.java} | 42 ++++++++-------- .../ProviderMultiResourceComponent.java | 48 +++++++++++++++++++ .../RecaptchaEnterpriseAppCheckProvider.java | 8 ++-- ...captchaEnterpriseAppCheckProviderTest.java | 4 -- 7 files changed, 95 insertions(+), 45 deletions(-) rename appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/{FirebaseExecutors.java => ProviderComponent.java} (52%) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle index 1f6ab23ece6..0a9c95c31c7 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle +++ b/appcheck/firebase-appcheck-recaptchaenterprise/firebase-appcheck-recaptchaenterprise.gradle @@ -46,11 +46,15 @@ android { } dependencies { + implementation(libs.dagger.dagger) + api project(':appcheck:firebase-appcheck') api 'com.google.firebase:firebase-common' api 'com.google.firebase:firebase-components' api 'com.google.android.recaptcha:recaptcha:18.7.1' + annotationProcessor(libs.dagger.compiler) + testImplementation(project(":integ-testing")) { exclude group: 'com.google.firebase', module: 'firebase-common' exclude group: 'com.google.firebase', module: 'firebase-components' diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index 9630946df26..31fd135fb47 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -15,13 +15,15 @@ package com.google.firebase.appcheck.recaptchaenterprise; import com.google.android.gms.common.annotation.KeepForSdk; +import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; -import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; +import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentRegistrar; import com.google.firebase.components.Dependency; import com.google.firebase.components.Qualified; +import com.google.firebase.appcheck.recaptchaenterprise.internal.DaggerProviderComponent; import com.google.firebase.platforminfo.LibraryVersionComponent; import java.util.Arrays; import java.util.List; @@ -43,14 +45,19 @@ public List> getComponents() { Qualified blockingExecutor = Qualified.qualified(Blocking.class, Executor.class); return Arrays.asList( - Component.builder(FirebaseExecutors.class) + Component.builder(ProviderMultiResourceComponent.class) .name(LIBRARY_NAME) + .add(Dependency.required(FirebaseApp.class)) .add(Dependency.required(liteExecutor)) .add(Dependency.required(blockingExecutor)) .factory( container -> - new FirebaseExecutors( - container.get(liteExecutor), container.get(blockingExecutor))) + DaggerProviderComponent.builder() + .setFirebaseApp(container.get(FirebaseApp.class)) + .setLiteExecutor(container.get(liteExecutor)) + .setBlockingExecutor(container.get(blockingExecutor)) + .build() + .getMultiResourceComponent()) .build(), LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)); } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index 3ebc4ee55ae..a41ae335278 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -14,13 +14,12 @@ package com.google.firebase.appcheck.recaptchaenterprise; -import android.app.Application; import androidx.annotation.NonNull; import com.google.firebase.FirebaseApp; import com.google.firebase.appcheck.AppCheckProvider; import com.google.firebase.appcheck.AppCheckProviderFactory; import com.google.firebase.appcheck.FirebaseAppCheck; -import com.google.firebase.appcheck.recaptchaenterprise.internal.FirebaseExecutors; +import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -31,7 +30,6 @@ */ public class RecaptchaEnterpriseAppCheckProviderFactory implements AppCheckProviderFactory { - private static FirebaseExecutors firebaseExecutors; private static final Map factoryInstances = new ConcurrentHashMap<>(); private final String siteKey; @@ -55,18 +53,9 @@ public AppCheckProvider create(@NonNull FirebaseApp firebaseApp) { if (provider == null) { synchronized (this) { if (provider == null) { - if (RecaptchaEnterpriseAppCheckProviderFactory.firebaseExecutors == null) { - firebaseExecutors = firebaseApp.get(FirebaseExecutors.class); - } - Application application = (Application) firebaseApp.getApplicationContext(); - - provider = - new RecaptchaEnterpriseAppCheckProvider( - firebaseApp, - application, - siteKey, - firebaseExecutors.getLiteExecutor(), - firebaseExecutors.getBlockingExecutor()); + ProviderMultiResourceComponent component = + firebaseApp.get(ProviderMultiResourceComponent.class); + provider = component.get(siteKey); } } } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java similarity index 52% rename from appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java rename to appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java index b117965e47c..662d45c52b8 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/FirebaseExecutors.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java @@ -14,30 +14,34 @@ package com.google.firebase.appcheck.recaptchaenterprise.internal; +import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; import java.util.concurrent.Executor; +import javax.inject.Singleton; +import dagger.BindsInstance; +import dagger.Component; +import dagger.Module; -/** - * This class encapsulates a {@link com.google.firebase.annotations.concurrent.Lightweight} executor - * and a {@link com.google.firebase.annotations.concurrent.Blocking} executor, making them available - * for various asynchronous operations related to reCAPTCHA Enterprise App Check. - */ -public class FirebaseExecutors { - private final Executor liteExecutor; - private final Executor blockingExecutor; - - public FirebaseExecutors( - @Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { - this.liteExecutor = liteExecutor; - this.blockingExecutor = blockingExecutor; - } +@Singleton +@Component(modules = ProviderComponent.MainModule.class) +public interface ProviderComponent { + ProviderMultiResourceComponent getMultiResourceComponent(); - public Executor getLiteExecutor() { - return liteExecutor; - } + @Component.Builder + interface Builder { + @BindsInstance + Builder setFirebaseApp(FirebaseApp firebaseApp); + + @BindsInstance + Builder setLiteExecutor(@Lightweight Executor liteExecutor); - public Executor getBlockingExecutor() { - return blockingExecutor; + @BindsInstance + Builder setBlockingExecutor(@Blocking Executor blockingExecutor); + + ProviderComponent build(); } + + @Module + abstract class MainModule {} } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java new file mode 100644 index 00000000000..d625235b9b5 --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java @@ -0,0 +1,48 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.appcheck.recaptchaenterprise.internal; + +import androidx.annotation.NonNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.inject.Inject; +import javax.inject.Singleton; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; + +/** Multi-resource container for RecaptchaEnterpriseAppCheckProvider */ +@Singleton +public final class ProviderMultiResourceComponent { + private final RecaptchaEnterpriseAppCheckProviderFactory providerFactory; + + private final Map instances = + new ConcurrentHashMap<>(); + + @Inject + ProviderMultiResourceComponent(RecaptchaEnterpriseAppCheckProviderFactory providerFactory) { + this.providerFactory = providerFactory; + } + + @NonNull + public RecaptchaEnterpriseAppCheckProvider get(@NonNull String siteKey) { + return instances.computeIfAbsent(siteKey, providerFactory::create); + } + + @AssistedFactory + interface RecaptchaEnterpriseAppCheckProviderFactory { + RecaptchaEnterpriseAppCheckProvider create(@Assisted String siteKey); + } +} diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 166cf73d801..51729df45fd 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -34,6 +34,8 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.Executor; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedInject; /** * An implementation of {@link AppCheckProvider} that uses reCAPTCHA Enterprise for device @@ -59,13 +61,13 @@ public class RecaptchaEnterpriseAppCheckProvider implements AppCheckProvider { private Application application; private static final String TAG = "rCEAppCheckProvider"; + @AssistedInject public RecaptchaEnterpriseAppCheckProvider( @NonNull FirebaseApp firebaseApp, - @NonNull Application application, - @NonNull String siteKey, + @Assisted @NonNull String siteKey, @Lightweight Executor liteExecutor, @Blocking Executor blockingExecutor) { - this.application = application; + this.application = (Application) firebaseApp.getApplicationContext(); this.siteKey = siteKey; this.liteExecutor = liteExecutor; this.blockingExecutor = blockingExecutor; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java index 0937dc9d21c..6a336b7a1d2 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Application; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.android.recaptcha.RecaptchaAction; @@ -62,7 +61,6 @@ public class RecaptchaEnterpriseAppCheckProviderTest { private final String siteKey = "siteKey"; @Mock private NetworkClient mockNetworkClient; - @Mock private Application mockApplication; @Mock FirebaseApp mockFirebaseApp; @Mock RecaptchaTasksClient mockRecaptchaTasksClient; @Mock RetryManager mockRetryManager; @@ -82,7 +80,6 @@ public void testPublicConstructor_nullFirebaseApp_expectThrows() { () -> new RecaptchaEnterpriseAppCheckProvider( null, - mockApplication, siteKey, TestOnlyExecutors.lite(), TestOnlyExecutors.blocking())); @@ -95,7 +92,6 @@ public void testPublicConstructor_nullSiteKey_expectThrows() { () -> new RecaptchaEnterpriseAppCheckProvider( mockFirebaseApp, - mockApplication, null, TestOnlyExecutors.lite(), TestOnlyExecutors.blocking())); From 97fc25d9eaa7d5275d5bf84ffe5a33fbe112c1bd Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Fri, 18 Jul 2025 21:53:58 +0000 Subject: [PATCH 19/23] Corrects Formatting --- .../FirebaseAppCheckRecaptchaEnterpriseRegistrar.java | 2 +- .../internal/ProviderComponent.java | 4 ++-- .../internal/ProviderMultiResourceComponent.java | 5 ++--- .../internal/RecaptchaEnterpriseAppCheckProvider.java | 4 ++-- .../RecaptchaEnterpriseAppCheckProviderTest.java | 10 ++-------- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java index 31fd135fb47..2a87456ed69 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/FirebaseAppCheckRecaptchaEnterpriseRegistrar.java @@ -18,12 +18,12 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; +import com.google.firebase.appcheck.recaptchaenterprise.internal.DaggerProviderComponent; import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentRegistrar; import com.google.firebase.components.Dependency; import com.google.firebase.components.Qualified; -import com.google.firebase.appcheck.recaptchaenterprise.internal.DaggerProviderComponent; import com.google.firebase.platforminfo.LibraryVersionComponent; import java.util.Arrays; import java.util.List; diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java index 662d45c52b8..356a646bd5b 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderComponent.java @@ -17,11 +17,11 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.annotations.concurrent.Blocking; import com.google.firebase.annotations.concurrent.Lightweight; -import java.util.concurrent.Executor; -import javax.inject.Singleton; import dagger.BindsInstance; import dagger.Component; import dagger.Module; +import java.util.concurrent.Executor; +import javax.inject.Singleton; @Singleton @Component(modules = ProviderComponent.MainModule.class) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java index d625235b9b5..5df2cf2a1c5 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java @@ -15,13 +15,12 @@ package com.google.firebase.appcheck.recaptchaenterprise.internal; import androidx.annotation.NonNull; - +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Singleton; -import dagger.assisted.Assisted; -import dagger.assisted.AssistedFactory; /** Multi-resource container for RecaptchaEnterpriseAppCheckProvider */ @Singleton diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 51729df45fd..8adbb0c484e 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -31,11 +31,11 @@ import com.google.firebase.appcheck.internal.DefaultAppCheckToken; import com.google.firebase.appcheck.internal.NetworkClient; import com.google.firebase.appcheck.internal.RetryManager; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedInject; import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.Executor; -import dagger.assisted.Assisted; -import dagger.assisted.AssistedInject; /** * An implementation of {@link AppCheckProvider} that uses reCAPTCHA Enterprise for device diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java index 6a336b7a1d2..19cbdc4957b 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProviderTest.java @@ -79,10 +79,7 @@ public void testPublicConstructor_nullFirebaseApp_expectThrows() { NullPointerException.class, () -> new RecaptchaEnterpriseAppCheckProvider( - null, - siteKey, - TestOnlyExecutors.lite(), - TestOnlyExecutors.blocking())); + null, siteKey, TestOnlyExecutors.lite(), TestOnlyExecutors.blocking())); } @Test @@ -91,10 +88,7 @@ public void testPublicConstructor_nullSiteKey_expectThrows() { NullPointerException.class, () -> new RecaptchaEnterpriseAppCheckProvider( - mockFirebaseApp, - null, - TestOnlyExecutors.lite(), - TestOnlyExecutors.blocking())); + mockFirebaseApp, null, TestOnlyExecutors.lite(), TestOnlyExecutors.blocking())); } @Test From 1f56a6afd3668fce29fdb49e721f7169f1695c16 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Tue, 22 Jul 2025 17:57:00 +0000 Subject: [PATCH 20/23] Resolves Comments --- ...ptchaEnterpriseAppCheckProviderFactory.java | 14 ++++++++++++-- .../ProviderMultiResourceComponent.java | 12 +++++++++++- .../RecaptchaEnterpriseAppCheckProvider.java | 18 +++++++++++------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index a41ae335278..4cf9d1fd146 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -42,8 +42,17 @@ private RecaptchaEnterpriseAppCheckProviderFactory(@NonNull String siteKey) { /** Gets an instance of this class for installation into a {@link FirebaseAppCheck} instance. */ @NonNull public static RecaptchaEnterpriseAppCheckProviderFactory getInstance(@NonNull String siteKey) { - return factoryInstances.computeIfAbsent( - siteKey, RecaptchaEnterpriseAppCheckProviderFactory::new); + RecaptchaEnterpriseAppCheckProviderFactory factory = factoryInstances.get(siteKey); + if (factory == null) { + synchronized (factoryInstances) { + factory = factoryInstances.get(siteKey); + if (factory == null) { + factory = new RecaptchaEnterpriseAppCheckProviderFactory(siteKey); + factoryInstances.put(siteKey, factory); + } + } + } + return factory; } @NonNull @@ -56,6 +65,7 @@ public AppCheckProvider create(@NonNull FirebaseApp firebaseApp) { ProviderMultiResourceComponent component = firebaseApp.get(ProviderMultiResourceComponent.class); provider = component.get(siteKey); + provider.initializeRecaptchaClient(); } } } diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java index 5df2cf2a1c5..93861c937a0 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/ProviderMultiResourceComponent.java @@ -37,7 +37,17 @@ public final class ProviderMultiResourceComponent { @NonNull public RecaptchaEnterpriseAppCheckProvider get(@NonNull String siteKey) { - return instances.computeIfAbsent(siteKey, providerFactory::create); + RecaptchaEnterpriseAppCheckProvider provider = instances.get(siteKey); + if (provider == null) { + synchronized (instances) { + provider = instances.get(siteKey); + if (provider == null) { + provider = providerFactory.create(siteKey); + instances.put(siteKey, provider); + } + } + } + return provider; } @AssistedFactory diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java index 8adbb0c484e..961ab7defa8 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/internal/RecaptchaEnterpriseAppCheckProvider.java @@ -89,6 +89,17 @@ public RecaptchaEnterpriseAppCheckProvider( this.recaptchaTasksClientTask = Tasks.forResult(recaptchaTasksClient); } + public void initializeRecaptchaClient() { + if (recaptchaTasksClientTask == null) { + synchronized (this) { + if (recaptchaTasksClientTask == null) { + Log.d(TAG, "Initializing RecaptchaTasksClient for siteKey: " + siteKey); + recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey); + } + } + } + } + @NonNull @Override public Task getToken() { @@ -116,13 +127,6 @@ public Task getToken() { @NonNull private Task getRecaptchaEnterpriseAttestation() { - if (recaptchaTasksClientTask == null) { - synchronized (this) { - if (recaptchaTasksClientTask == null) { - recaptchaTasksClientTask = Recaptcha.fetchTaskClient(application, siteKey); - } - } - } return recaptchaTasksClientTask.continueWithTask( blockingExecutor, task -> { From 8d0f5ee2895d2d5a88e0d0abd853716f7aadf1d3 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Wed, 23 Jul 2025 19:21:58 +0000 Subject: [PATCH 21/23] Removes Map from RecaptchaEnterpriseAppCheckProviderFactory --- ...tchaEnterpriseAppCheckProviderFactory.java | 18 +---- ...EnterpriseAppCheckProviderFactoryTest.java | 67 ++++++++++++++----- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java index 4cf9d1fd146..e051f54d0e9 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/main/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactory.java @@ -21,8 +21,7 @@ import com.google.firebase.appcheck.FirebaseAppCheck; import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Objects; /** * Implementation of an {@link AppCheckProviderFactory} that builds
@@ -30,8 +29,6 @@ */ public class RecaptchaEnterpriseAppCheckProviderFactory implements AppCheckProviderFactory { - private static final Map factoryInstances = - new ConcurrentHashMap<>(); private final String siteKey; private volatile RecaptchaEnterpriseAppCheckProvider provider; @@ -42,17 +39,8 @@ private RecaptchaEnterpriseAppCheckProviderFactory(@NonNull String siteKey) { /** Gets an instance of this class for installation into a {@link FirebaseAppCheck} instance. */ @NonNull public static RecaptchaEnterpriseAppCheckProviderFactory getInstance(@NonNull String siteKey) { - RecaptchaEnterpriseAppCheckProviderFactory factory = factoryInstances.get(siteKey); - if (factory == null) { - synchronized (factoryInstances) { - factory = factoryInstances.get(siteKey); - if (factory == null) { - factory = new RecaptchaEnterpriseAppCheckProviderFactory(siteKey); - factoryInstances.put(siteKey, factory); - } - } - } - return factory; + Objects.requireNonNull(siteKey, "siteKey cannot be null"); + return new RecaptchaEnterpriseAppCheckProviderFactory(siteKey); } @NonNull diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java index f65d0613d41..f2e5bb914ed 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java @@ -14,37 +14,72 @@ package com.google.firebase.appcheck.recaptchaenterprise; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import com.google.firebase.FirebaseApp; +import com.google.firebase.appcheck.AppCheckProvider; +import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; +import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; /** Tests for {@link RecaptchaEnterpriseAppCheckProviderFactory}. */ -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) +@RunWith(MockitoJUnitRunner.class) public class RecaptchaEnterpriseAppCheckProviderFactoryTest { static final String SITE_KEY_1 = "siteKey1"; - static final String SITE_KEY_2 = "siteKey2"; + + @Mock private FirebaseApp mockFirebaseApp; + @Mock private ProviderMultiResourceComponent mockComponent; + @Mock private RecaptchaEnterpriseAppCheckProvider mockProvider; + + @Before + public void setUp() { + when(mockFirebaseApp.get(eq(ProviderMultiResourceComponent.class))).thenReturn(mockComponent); + when(mockComponent.get(anyString())).thenReturn(mockProvider); + } @Test - public void testGetInstance_callTwiceSameSiteKey_sameInstance() { - RecaptchaEnterpriseAppCheckProviderFactory firstInstance = - RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); - RecaptchaEnterpriseAppCheckProviderFactory secondInstance = + public void getInstance_nonNullSiteKey_returnsNonNullInstance() { + RecaptchaEnterpriseAppCheckProviderFactory factory = RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); + assertNotNull(factory); + } - assertThat(firstInstance).isEqualTo(secondInstance); + @Test + public void getInstance_nullSiteKey_expectThrows() { + assertThrows( + NullPointerException.class, + () -> RecaptchaEnterpriseAppCheckProviderFactory.getInstance(null)); + } + + @Test + public void create_nonNullFirebaseApp_returnsRecaptchaEnterpriseAppCheckProvider() { + RecaptchaEnterpriseAppCheckProviderFactory factory = + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); + AppCheckProvider provider = factory.create(mockFirebaseApp); + assertNotNull(provider); + assertEquals(RecaptchaEnterpriseAppCheckProvider.class, provider.getClass()); } @Test - public void testGetInstance_callTwiceDifferentSiteKey_differentInstance() { - RecaptchaEnterpriseAppCheckProviderFactory firstInstance = + public void create_callMultipleTimes_providerIsInitializedOnlyOnce() { + RecaptchaEnterpriseAppCheckProviderFactory factory = RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_1); - RecaptchaEnterpriseAppCheckProviderFactory secondInstance = - RecaptchaEnterpriseAppCheckProviderFactory.getInstance(SITE_KEY_2); - assertThat(firstInstance).isNotEqualTo(secondInstance); + factory.create(mockFirebaseApp); + factory.create(mockFirebaseApp); + factory.create(mockFirebaseApp); + verify(mockProvider, times(1)).initializeRecaptchaClient(); } } From beac8e37a675ee6f413f89008a776381b8da119c Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Thu, 24 Jul 2025 02:47:42 +0000 Subject: [PATCH 22/23] Resolves Spotless formatting issue --- .../RecaptchaEnterpriseAppCheckProviderFactoryTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java index f2e5bb914ed..865b69e409c 100644 --- a/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java +++ b/appcheck/firebase-appcheck-recaptchaenterprise/src/test/java/com/google/firebase/appcheck/recaptchaenterprise/RecaptchaEnterpriseAppCheckProviderFactoryTest.java @@ -27,7 +27,6 @@ import com.google.firebase.appcheck.AppCheckProvider; import com.google.firebase.appcheck.recaptchaenterprise.internal.ProviderMultiResourceComponent; import com.google.firebase.appcheck.recaptchaenterprise.internal.RecaptchaEnterpriseAppCheckProvider; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; From 4160a2ae6eeb406677c4e5e5286f98bf534696d0 Mon Sep 17 00:00:00 2001 From: hiteshmaurya Date: Thu, 24 Jul 2025 07:55:28 +0000 Subject: [PATCH 23/23] Adds existing_api.txt file --- .../existing_api.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 appcheck/firebase-appcheck-recaptchaenterprise/existing_api.txt diff --git a/appcheck/firebase-appcheck-recaptchaenterprise/existing_api.txt b/appcheck/firebase-appcheck-recaptchaenterprise/existing_api.txt new file mode 100644 index 00000000000..99d21ff038c --- /dev/null +++ b/appcheck/firebase-appcheck-recaptchaenterprise/existing_api.txt @@ -0,0 +1,10 @@ +// Signature format: 3.0 +package com.google.firebase.appcheck.recaptchaenterprise { + + public class RecaptchaEnterpriseAppCheckProviderFactory implements com.google.firebase.appcheck.AppCheckProviderFactory { + method public com.google.firebase.appcheck.AppCheckProvider create(com.google.firebase.FirebaseApp); + method public static com.google.firebase.appcheck.recaptchaenterprise.RecaptchaEnterpriseAppCheckProviderFactory getInstance(String); + } + +} +