diff --git a/recaptcha_enterprise/pom.xml b/recaptcha_enterprise/pom.xml
new file mode 100644
index 00000000000..86240c555be
--- /dev/null
+++ b/recaptcha_enterprise/pom.xml
@@ -0,0 +1,61 @@
+
+
+ 4.0.0
+ com.google.cloud
+ recaptcha-enterprise-snippets
+ jar
+ Google reCAPTCHA Enterprise Snippets
+ https://github.com/googleapis/java-recaptchaenterprise
+
+
+
+ com.google.cloud.samples
+ shared-configuration
+ 1.2.0
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+
+
+ com.google.cloud
+ libraries-bom
+ 26.1.4
+ pom
+ import
+
+
+
+
+
+
+ com.google.cloud
+ google-cloud-recaptchaenterprise
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ com.google.truth
+ truth
+ 1.1.3
+ test
+
+
+
+
+
diff --git a/recaptcha_enterprise/snippets/src/main/java/app/Main.java b/recaptcha_enterprise/snippets/src/main/java/app/Main.java
new file mode 100644
index 00000000000..d51c4dcb4eb
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/app/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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 app;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
diff --git a/recaptcha_enterprise/snippets/src/main/java/app/MainController.java b/recaptcha_enterprise/snippets/src/main/java/app/MainController.java
new file mode 100644
index 00000000000..473772ea4a4
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/app/MainController.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 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 app;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+@RequestMapping
+public class MainController {
+
+ @GetMapping(value = "/")
+ public static ModelAndView landingPage(
+ @RequestParam("recaptchaSiteKey") String recaptchaSiteKey) {
+ ModelMap map = new ModelAndView().getModelMap();
+ map.put("siteKey", recaptchaSiteKey);
+ return new ModelAndView("index", map);
+ }
+}
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/AnnotateAssessment.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/AnnotateAssessment.java
new file mode 100644
index 00000000000..8c3e8d5306c
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/AnnotateAssessment.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_annotate_assessment]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest.Annotation;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest.Reason;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentResponse;
+import com.google.recaptchaenterprise.v1.AssessmentName;
+import java.io.IOException;
+
+public class AnnotateAssessment {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "project-id";
+ String assessmentId = "assessment-id";
+ annotateAssessment(projectID, assessmentId);
+ }
+
+ /**
+ * Pre-requisite: Create an assessment before annotating.
+ *
+ *
Annotate an assessment to provide feedback on the correctness of recaptcha prediction.
+ *
+ * @param projectID: GCloud Project id
+ * @param assessmentId: Value of the 'name' field returned from the CreateAssessment call.
+ */
+ public static void annotateAssessment(String projectID, String assessmentId) throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+ // Build the annotation request.
+ // For more info on when/how to annotate, see:
+ // https://cloud.google.com/recaptcha-enterprise/docs/annotate-assessment#when_to_annotate
+ AnnotateAssessmentRequest annotateAssessmentRequest =
+ AnnotateAssessmentRequest.newBuilder()
+ .setName(AssessmentName.of(projectID, assessmentId).toString())
+ .setAnnotation(Annotation.FRAUDULENT)
+ .addReasons(Reason.FAILED_TWO_FACTOR)
+ .build();
+
+ // Empty response is sent back.
+ AnnotateAssessmentResponse response = client.annotateAssessment(annotateAssessmentRequest);
+ System.out.println("Annotated response sent successfully ! " + response);
+ }
+ }
+}
+// [END recaptcha_enterprise_annotate_assessment]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateAssessment.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateAssessment.java
new file mode 100644
index 00000000000..8fed3a1c8ca
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateAssessment.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_create_assessment]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.Assessment;
+import com.google.recaptchaenterprise.v1.CreateAssessmentRequest;
+import com.google.recaptchaenterprise.v1.Event;
+import com.google.recaptchaenterprise.v1.ProjectName;
+import com.google.recaptchaenterprise.v1.RiskAnalysis.ClassificationReason;
+import java.io.IOException;
+
+public class CreateAssessment {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "project-id";
+ String recaptchaSiteKey = "recaptcha-site-key";
+ String token = "action-token";
+ String recaptchaAction = "action-name";
+
+ createAssessment(projectID, recaptchaSiteKey, token, recaptchaAction);
+ }
+
+ /**
+ * Create an assessment to analyze the risk of an UI action. Assessment approach is the same for
+ * both 'score' and 'checkbox' type recaptcha site keys.
+ *
+ * @param projectID : GCloud Project ID
+ * @param recaptchaSiteKey : Site key obtained by registering a domain/app to use recaptcha
+ * services. (score/ checkbox type)
+ * @param token : The token obtained from the client on passing the recaptchaSiteKey.
+ * @param recaptchaAction : Action name corresponding to the token.
+ */
+ public static void createAssessment(
+ String projectID, String recaptchaSiteKey, String token, String recaptchaAction)
+ throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the properties of the event to be tracked.
+ Event event = Event.newBuilder().setSiteKey(recaptchaSiteKey).setToken(token).build();
+
+ // Build the assessment request.
+ CreateAssessmentRequest createAssessmentRequest =
+ CreateAssessmentRequest.newBuilder()
+ .setParent(ProjectName.of(projectID).toString())
+ .setAssessment(Assessment.newBuilder().setEvent(event).build())
+ .build();
+
+ Assessment response = client.createAssessment(createAssessmentRequest);
+
+ // Check if the token is valid.
+ if (!response.getTokenProperties().getValid()) {
+ System.out.println(
+ "The CreateAssessment call failed because the token was: "
+ + response.getTokenProperties().getInvalidReason().name());
+ return;
+ }
+
+ // Check if the expected action was executed.
+ // (If the key is checkbox type and 'action' attribute wasn't set, skip this check.)
+ if (!response.getTokenProperties().getAction().equals(recaptchaAction)) {
+ System.out.println(
+ "The action attribute in reCAPTCHA tag is: "
+ + response.getTokenProperties().getAction());
+ System.out.println(
+ "The action attribute in the reCAPTCHA tag "
+ + "does not match the action ("
+ + recaptchaAction
+ + ") you are expecting to score");
+ return;
+ }
+
+ // Get the reason(s) and the risk score.
+ // For more information on interpreting the assessment,
+ // see: https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
+ for (ClassificationReason reason : response.getRiskAnalysis().getReasonsList()) {
+ System.out.println(reason);
+ }
+
+ float recaptchaScore = response.getRiskAnalysis().getScore();
+ System.out.println("The reCAPTCHA score is: " + recaptchaScore);
+
+ // Get the assessment name (id). Use this to annotate the assessment.
+ String assessmentName = response.getName();
+ System.out.println(
+ "Assessment name: " + assessmentName.substring(assessmentName.lastIndexOf("/") + 1));
+ }
+ }
+}
+// [END recaptcha_enterprise_create_assessment]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateSiteKey.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateSiteKey.java
new file mode 100644
index 00000000000..635a6c5e4a7
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/CreateSiteKey.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_create_site_key]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.CreateKeyRequest;
+import com.google.recaptchaenterprise.v1.Key;
+import com.google.recaptchaenterprise.v1.ProjectName;
+import com.google.recaptchaenterprise.v1.WebKeySettings;
+import com.google.recaptchaenterprise.v1.WebKeySettings.IntegrationType;
+import java.io.IOException;
+
+public class CreateSiteKey {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "your-project-id";
+ String domainName = "domain-name";
+
+ createSiteKey(projectID, domainName);
+ }
+
+ /**
+ * Create reCAPTCHA Site key which binds a domain name to a unique key.
+ *
+ * @param projectID : GCloud Project ID.
+ * @param domainName : Specify the domain name in which the reCAPTCHA should be activated.
+ */
+ public static String createSiteKey(String projectID, String domainName) throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the type of reCAPTCHA to be displayed.
+ // For different types, see: https://cloud.google.com/recaptcha-enterprise/docs/keys
+ Key scoreKey =
+ Key.newBuilder()
+ .setDisplayName("any_descriptive_name_for_the_key")
+ .setWebSettings(
+ WebKeySettings.newBuilder()
+ .addAllowedDomains(domainName)
+ .setAllowAmpTraffic(false)
+ .setIntegrationType(IntegrationType.SCORE)
+ .build())
+ .build();
+
+ CreateKeyRequest createKeyRequest =
+ CreateKeyRequest.newBuilder()
+ .setParent(ProjectName.of(projectID).toString())
+ .setKey(scoreKey)
+ .build();
+
+ // Get the name of the created reCAPTCHA site key.
+ Key response = client.createKey(createKeyRequest);
+ String keyName = response.getName();
+ String recaptchaSiteKey = keyName.substring(keyName.lastIndexOf("/") + 1);
+ System.out.println("reCAPTCHA Site key created successfully. Site Key: " + recaptchaSiteKey);
+ return recaptchaSiteKey;
+ }
+ }
+}
+// [END recaptcha_enterprise_create_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/DeleteSiteKey.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/DeleteSiteKey.java
new file mode 100644
index 00000000000..9089c5ee4e6
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/DeleteSiteKey.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_delete_site_key]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.DeleteKeyRequest;
+import com.google.recaptchaenterprise.v1.KeyName;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class DeleteSiteKey {
+
+ public static void main(String[] args)
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "your-project-id";
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ deleteSiteKey(projectID, recaptchaSiteKey);
+ }
+
+ /**
+ * Delete the given reCAPTCHA site key present under the project ID.
+ *
+ * @param projectID: GCloud Project ID.
+ * @param recaptchaSiteKey: Specify the site key to be deleted.
+ */
+ public static void deleteSiteKey(String projectID, String recaptchaSiteKey)
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the project ID and reCAPTCHA site key.
+ DeleteKeyRequest deleteKeyRequest =
+ DeleteKeyRequest.newBuilder()
+ .setName(KeyName.of(projectID, recaptchaSiteKey).toString())
+ .build();
+
+ client.deleteKeyCallable().futureCall(deleteKeyRequest).get(5, TimeUnit.SECONDS);
+ System.out.println("reCAPTCHA Site key successfully deleted !");
+ }
+ }
+}
+// [END recaptcha_enterprise_delete_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetMetrics.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetMetrics.java
new file mode 100644
index 00000000000..018dc73faac
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetMetrics.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_get_metrics_site_key]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.GetMetricsRequest;
+import com.google.recaptchaenterprise.v1.Metrics;
+import com.google.recaptchaenterprise.v1.MetricsName;
+import com.google.recaptchaenterprise.v1.ScoreMetrics;
+import java.io.IOException;
+
+public class GetMetrics {
+
+ public static void main(String[] args) throws IOException {
+ String projectId = "project-id";
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ getMetrics(projectId, recaptchaSiteKey);
+ }
+
+ /**
+ * Get metrics specific to a recaptcha site key. E.g: score bucket count for a key or number of
+ * times the checkbox key failed/ passed etc.,
+ *
+ * @param projectId: Google Cloud Project Id.
+ * @param recaptchaSiteKey: Specify the site key to get metrics.
+ */
+ public static void getMetrics(String projectId, String recaptchaSiteKey) throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ GetMetricsRequest getMetricsRequest =
+ GetMetricsRequest.newBuilder()
+ .setName(MetricsName.of(projectId, recaptchaSiteKey).toString())
+ .build();
+
+ Metrics response = client.getMetrics(getMetricsRequest);
+
+ // Retrieve the metrics you want from the key.
+ // If the site key is checkbox type: then use response.getChallengeMetricsList() instead of
+ // response.getScoreMetricsList()
+ for (ScoreMetrics scoreMetrics : response.getScoreMetricsList()) {
+ // Each ScoreMetrics is in the granularity of one day.
+ int scoreBucketCount = scoreMetrics.getOverallMetrics().getScoreBucketsCount();
+ System.out.println(scoreBucketCount);
+ }
+ System.out.printf("Retrieved the bucket count for score based key: %s", recaptchaSiteKey);
+ }
+ }
+}
+// [END recaptcha_enterprise_get_metrics_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetSiteKey.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetSiteKey.java
new file mode 100644
index 00000000000..74eda6a1b2f
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/GetSiteKey.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_get_site_key]
+
+import com.google.api.core.ApiFuture;
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.GetKeyRequest;
+import com.google.recaptchaenterprise.v1.Key;
+import com.google.recaptchaenterprise.v1.KeyName;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class GetSiteKey {
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException, ExecutionException, TimeoutException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "your-project-id";
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ getSiteKey(projectID, recaptchaSiteKey);
+ }
+
+ /**
+ * Get the reCAPTCHA site key present under the project ID.
+ *
+ * @param projectID: GCloud Project ID.
+ * @param recaptchaSiteKey: Specify the site key to get the details.
+ */
+ public static void getSiteKey(String projectID, String recaptchaSiteKey)
+ throws IOException, InterruptedException, ExecutionException, TimeoutException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Construct the "GetSiteKey" request.
+ GetKeyRequest getKeyRequest =
+ GetKeyRequest.newBuilder()
+ .setName(KeyName.of(projectID, recaptchaSiteKey).toString())
+ .build();
+
+ // Wait for the operation to complete.
+ ApiFuture futureCall = client.getKeyCallable().futureCall(getKeyRequest);
+ Key key = futureCall.get(5, TimeUnit.SECONDS);
+
+ System.out.println("Successfully obtained the key !" + key.getName());
+ }
+ }
+}
+// [END recaptcha_enterprise_get_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/ListSiteKeys.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/ListSiteKeys.java
new file mode 100644
index 00000000000..504de8225ba
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/ListSiteKeys.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_list_site_keys]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient.ListKeysPagedResponse;
+import com.google.recaptchaenterprise.v1.Key;
+import com.google.recaptchaenterprise.v1.ListKeysRequest;
+import com.google.recaptchaenterprise.v1.ProjectName;
+import java.io.IOException;
+
+public class ListSiteKeys {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "your-project-id";
+
+ listSiteKeys(projectID);
+ }
+
+ /**
+ * List all keys present under the given project ID.
+ *
+ * @param projectID : GCloud Project ID.
+ */
+ public static ListKeysPagedResponse listSiteKeys(String projectID) throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+ // Set the project ID to list the keys present in it.
+ ListKeysRequest listKeysRequest =
+ ListKeysRequest.newBuilder().setParent(ProjectName.of(projectID).toString()).build();
+
+ ListKeysPagedResponse response = client.listKeys(listKeysRequest);
+ System.out.println("Listing reCAPTCHA site keys: ");
+ for (Key key : response.iterateAll()) {
+ System.out.println(key.getName());
+ }
+ return response;
+ }
+ }
+}
+// [END recaptcha_enterprise_list_site_keys]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/MigrateKey.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/MigrateKey.java
new file mode 100644
index 00000000000..e529339e1d3
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/MigrateKey.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_migrate_site_key]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.Key;
+import com.google.recaptchaenterprise.v1.KeyName;
+import com.google.recaptchaenterprise.v1.MigrateKeyRequest;
+import java.io.IOException;
+
+public class MigrateKey {
+
+ public static void main(String[] args) throws IOException {
+ String projectId = "project-id";
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ migrateKey(projectId, recaptchaSiteKey);
+ }
+
+ /**
+ * Migrate a key from reCAPTCHA (non-Enterprise) to reCAPTCHA Enterprise. If you created the key
+ * using Admin console: https://www.google.com/recaptcha/admin/site, then use this API to migrate
+ * to reCAPTCHA Enterprise. For more info, see:
+ * https://cloud.google.com/recaptcha-enterprise/docs/migrate-recaptcha
+ *
+ * @param projectId: Google Cloud Project Id.
+ * @param recaptchaSiteKey: Specify the site key to migrate.
+ */
+ public static void migrateKey(String projectId, String recaptchaSiteKey) throws IOException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Specify the key name to migrate.
+ MigrateKeyRequest migrateKeyRequest =
+ MigrateKeyRequest.newBuilder()
+ .setName(KeyName.of(projectId, recaptchaSiteKey).toString())
+ .build();
+
+ Key response = client.migrateKey(migrateKeyRequest);
+
+ // To verify if the site key has been migrated, use 'ListSiteKeys' and check if the
+ // key is present.
+ for (Key key : recaptcha.ListSiteKeys.listSiteKeys(projectId).iterateAll()) {
+ if (key.equals(response)) {
+ System.out.printf("Key migrated successfully: %s", recaptchaSiteKey);
+ }
+ }
+ }
+ }
+}
+// [END recaptcha_enterprise_migrate_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/UpdateSiteKey.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/UpdateSiteKey.java
new file mode 100644
index 00000000000..c0e7d3e1311
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/UpdateSiteKey.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 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 recaptcha;
+
+// [START recaptcha_enterprise_update_site_key]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.GetKeyRequest;
+import com.google.recaptchaenterprise.v1.Key;
+import com.google.recaptchaenterprise.v1.KeyName;
+import com.google.recaptchaenterprise.v1.UpdateKeyRequest;
+import com.google.recaptchaenterprise.v1.WebKeySettings;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+public class UpdateSiteKey {
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException, ExecutionException, TimeoutException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectID = "your-project-id";
+ String recaptchaSiteKeyID = "recaptcha-site-key-id";
+ String domainName = "domain-name";
+
+ updateSiteKey(projectID, recaptchaSiteKeyID, domainName);
+ }
+
+ /**
+ * Update the properties of the given site key present under the project id.
+ *
+ * @param projectID: GCloud Project ID.
+ * @param recaptchaSiteKeyID: Specify the site key.
+ * @param domainName: Specify the domain name for which the settings should be updated.
+ */
+ public static void updateSiteKey(String projectID, String recaptchaSiteKeyID, String domainName)
+ throws IOException, InterruptedException, ExecutionException, TimeoutException {
+ // Initialize client that will be used to send requests. This client only needs to be created
+ // once, and can be reused for multiple requests. After completing all of your requests, call
+ // the `client.close()` method on the client to safely
+ // clean up any remaining background resources.
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the name and the new settings for the key.
+ UpdateKeyRequest updateKeyRequest =
+ UpdateKeyRequest.newBuilder()
+ .setKey(
+ Key.newBuilder()
+ .setDisplayName("any descriptive name for the key")
+ .setName(KeyName.of(projectID, recaptchaSiteKeyID).toString())
+ .setWebSettings(
+ WebKeySettings.newBuilder()
+ .setAllowAmpTraffic(true)
+ .addAllowedDomains(domainName)
+ .build())
+ .build())
+ .build();
+
+ client.updateKeyCallable().futureCall(updateKeyRequest).get();
+
+ // Check if the key has been updated.
+ GetKeyRequest getKeyRequest =
+ GetKeyRequest.newBuilder()
+ .setName(KeyName.of(projectID, recaptchaSiteKeyID).toString())
+ .build();
+ Key response = client.getKey(getKeyRequest);
+
+ // Get the changed property.
+ boolean allowedAmpTraffic = response.getWebSettings().getAllowAmpTraffic();
+ if (!allowedAmpTraffic) {
+ System.out.println(
+ "Error! reCAPTCHA Site key property hasn't been updated. Please try again !");
+ return;
+ }
+ System.out.println("reCAPTCHA Site key successfully updated !");
+ }
+ }
+}
+// [END recaptcha_enterprise_update_site_key]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AccountDefenderAssessment.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AccountDefenderAssessment.java
new file mode 100644
index 00000000000..b51b292c24f
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AccountDefenderAssessment.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2022 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 account_defender;
+
+// [START recaptcha_enterprise_account_defender_assessment]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.protobuf.ByteString;
+import com.google.recaptchaenterprise.v1.AccountDefenderAssessment.AccountDefenderLabel;
+import com.google.recaptchaenterprise.v1.Assessment;
+import com.google.recaptchaenterprise.v1.CreateAssessmentRequest;
+import com.google.recaptchaenterprise.v1.Event;
+import com.google.recaptchaenterprise.v1.ProjectName;
+import com.google.recaptchaenterprise.v1.RiskAnalysis.ClassificationReason;
+import com.google.recaptchaenterprise.v1.TokenProperties;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.UUID;
+
+public class AccountDefenderAssessment {
+
+ public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
+ // TODO(developer): Replace these variables before running the sample.
+ // projectId: Google Cloud Project ID
+ String projectId = "project-id";
+
+ // recaptchaSiteKey: Site key obtained by registering a domain/app to use recaptcha
+ // services.
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ // token: The token obtained from the client on passing the recaptchaSiteKey.
+ // To get the token, integrate the recaptchaSiteKey with frontend. See,
+ // https://cloud.google.com/recaptcha-enterprise/docs/instrument-web-pages#frontend_integration_score
+ String token = "recaptcha-token";
+
+ // recaptchaAction: The action name corresponding to the token.
+ String recaptchaAction = "recaptcha-action";
+
+ // Unique ID of the customer, such as email, customer ID, etc.
+ String userIdentifier = "default" + UUID.randomUUID().toString().split("-")[0];
+
+ // Hash the unique customer ID using HMAC SHA-256.
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] hashBytes = digest.digest(userIdentifier.getBytes(StandardCharsets.UTF_8));
+ ByteString hashedAccountId = ByteString.copyFrom(hashBytes);
+
+ accountDefenderAssessment(projectId, recaptchaSiteKey, token, recaptchaAction, hashedAccountId);
+ }
+
+ /**
+ * This assessment detects account takeovers. See,
+ * https://cloud.google.com/recaptcha-enterprise/docs/account-takeovers The input is the hashed
+ * account id. Result tells if the action represents an account takeover. You can optionally
+ * trigger a Multi-Factor Authentication based on the result.
+ */
+ public static void accountDefenderAssessment(
+ String projectId,
+ String recaptchaSiteKey,
+ String token,
+ String recaptchaAction,
+ ByteString hashedAccountId)
+ throws IOException {
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the properties of the event to be tracked.
+ Event event =
+ Event.newBuilder()
+ .setSiteKey(recaptchaSiteKey)
+ .setToken(token)
+ // Set the hashed account id (of the user).
+ // Recommended approach: HMAC SHA256 along with salt (or secret key).
+ .setHashedAccountId(hashedAccountId)
+ .build();
+
+ // Build the assessment request.
+ CreateAssessmentRequest createAssessmentRequest =
+ CreateAssessmentRequest.newBuilder()
+ .setParent(ProjectName.of(projectId).toString())
+ .setAssessment(Assessment.newBuilder().setEvent(event).build())
+ .build();
+
+ Assessment response = client.createAssessment(createAssessmentRequest);
+
+ // Check integrity of the response token.
+ if (!checkTokenIntegrity(response.getTokenProperties(), recaptchaAction)) {
+ return;
+ }
+
+ // Get the reason(s) and the reCAPTCHA risk score.
+ // For more information on interpreting the assessment,
+ // see: https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment
+ for (ClassificationReason reason : response.getRiskAnalysis().getReasonsList()) {
+ System.out.println(reason);
+ }
+ float recaptchaScore = response.getRiskAnalysis().getScore();
+ System.out.println("The reCAPTCHA score is: " + recaptchaScore);
+ String assessmentName = response.getName();
+ System.out.println(
+ "Assessment name: " + assessmentName.substring(assessmentName.lastIndexOf("/") + 1));
+
+ // Get the Account Defender result.
+ com.google.recaptchaenterprise.v1.AccountDefenderAssessment accountDefenderAssessment =
+ response.getAccountDefenderAssessment();
+ System.out.println(accountDefenderAssessment);
+
+ // Get Account Defender label.
+ List defenderResult =
+ response.getAccountDefenderAssessment().getLabelsList();
+ // Based on the result, can you choose next steps.
+ // If the 'defenderResult' field is empty, it indicates that Account Defender did not have
+ // anything to add to the score.
+ // Few result labels: ACCOUNT_DEFENDER_LABEL_UNSPECIFIED, PROFILE_MATCH,
+ // SUSPICIOUS_LOGIN_ACTIVITY, SUSPICIOUS_ACCOUNT_CREATION, RELATED_ACCOUNTS_NUMBER_HIGH.
+ // For more information on interpreting the assessment, see:
+ // https://cloud.google.com/recaptcha-enterprise/docs/account-defender#interpret-assessment-details
+ System.out.println("Account Defender Assessment Result: " + defenderResult);
+ }
+ }
+
+ private static boolean checkTokenIntegrity(
+ TokenProperties tokenProperties, String recaptchaAction) {
+ // Check if the token is valid.
+ if (!tokenProperties.getValid()) {
+ System.out.println(
+ "The Account Defender Assessment call failed because the token was: "
+ + tokenProperties.getInvalidReason().name());
+ return false;
+ }
+
+ // Check if the expected action was executed.
+ if (!tokenProperties.getAction().equals(recaptchaAction)) {
+ System.out.printf(
+ "The action attribute in the reCAPTCHA tag '%s' does not match "
+ + "the action '%s' you are expecting to score",
+ tokenProperties.getAction(), recaptchaAction);
+ return false;
+ }
+ return true;
+ }
+}
+// [END recaptcha_enterprise_account_defender_assessment]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AnnotateAccountDefenderAssessment.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AnnotateAccountDefenderAssessment.java
new file mode 100644
index 00000000000..ca8129f3c0b
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/AnnotateAccountDefenderAssessment.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 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 account_defender;
+
+// [START recaptcha_enterprise_annotate_account_defender_assessment]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.protobuf.ByteString;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest.Annotation;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentRequest.Reason;
+import com.google.recaptchaenterprise.v1.AnnotateAssessmentResponse;
+import com.google.recaptchaenterprise.v1.AssessmentName;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+
+public class AnnotateAccountDefenderAssessment {
+
+ public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
+ // TODO(developer): Replace these variables before running the sample.
+ // projectID: GCloud Project id.
+ String projectID = "project-id";
+
+ // assessmentId: Value of the 'name' field returned from the CreateAssessment call.
+ String assessmentId = "account-defender-assessment-id";
+
+ // hashedAccountId: Set the hashedAccountId corresponding to the assessment id.
+ ByteString hashedAccountId = ByteString.copyFrom(new byte[] {});
+
+ annotateAssessment(projectID, assessmentId, hashedAccountId);
+ }
+
+ /**
+ * Pre-requisite: Create an assessment before annotating. Annotate an assessment to provide
+ * feedback on the correctness of recaptcha prediction.
+ */
+ public static void annotateAssessment(
+ String projectID, String assessmentId, ByteString hashedAccountId) throws IOException {
+
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+ // Build the annotation request.
+ // For more info on when/how to annotate, see:
+ // https://cloud.google.com/recaptcha-enterprise/docs/annotate-assessment#when_to_annotate
+ AnnotateAssessmentRequest annotateAssessmentRequest =
+ AnnotateAssessmentRequest.newBuilder()
+ .setName(AssessmentName.of(projectID, assessmentId).toString())
+ .setAnnotation(Annotation.LEGITIMATE)
+ .addReasons(Reason.PASSED_TWO_FACTOR)
+ .setHashedAccountId(hashedAccountId)
+ .build();
+
+ // Empty response is sent back.
+ AnnotateAssessmentResponse response = client.annotateAssessment(annotateAssessmentRequest);
+ System.out.println("Annotated response sent successfully ! " + response);
+ }
+ }
+}
+// [END recaptcha_enterprise_annotate_account_defender_assessment]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroupMemberships.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroupMemberships.java
new file mode 100644
index 00000000000..a8a545d812e
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroupMemberships.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 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 account_defender;
+
+// [START recaptcha_enterprise_list_related_account_group_membership]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.ListRelatedAccountGroupMembershipsRequest;
+import com.google.recaptchaenterprise.v1.RelatedAccountGroupMembership;
+import java.io.IOException;
+
+public class ListRelatedAccountGroupMemberships {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ // projectId: Google Cloud Project Id.
+ String projectId = "project-id";
+
+ // relatedAccountGroup: Name of the account group.
+ String relatedAccountGroup = "related-account-group-name";
+
+ listRelatedAccountGroupMemberships(projectId, relatedAccountGroup);
+ }
+
+ /** Given a group name, list memberships in the group. */
+ public static void listRelatedAccountGroupMemberships(
+ String projectId, String relatedAccountGroup) throws IOException {
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Construct the request.
+ ListRelatedAccountGroupMembershipsRequest request =
+ ListRelatedAccountGroupMembershipsRequest.newBuilder()
+ .setParent(
+ String.format(
+ "projects/%s/relatedaccountgroups/%s", projectId, relatedAccountGroup))
+ .build();
+
+ for (RelatedAccountGroupMembership relatedAccountGroupMembership :
+ client.listRelatedAccountGroupMemberships(request).iterateAll()) {
+ System.out.println(relatedAccountGroupMembership.getName());
+ }
+ System.out.println("Finished listing related account group memberships.");
+ }
+ }
+}
+// [END recaptcha_enterprise_list_related_account_group_membership]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroups.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroups.java
new file mode 100644
index 00000000000..841a04d0aa4
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/ListRelatedAccountGroups.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 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 account_defender;
+
+// [START recaptcha_enterprise_list_related_account_group]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.recaptchaenterprise.v1.ListRelatedAccountGroupsRequest;
+import com.google.recaptchaenterprise.v1.RelatedAccountGroup;
+import java.io.IOException;
+
+public class ListRelatedAccountGroups {
+
+ public static void main(String[] args) throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ // projectId : Google Cloud Project Id.
+ String projectId = "project-id";
+
+ listRelatedAccountGroups(projectId);
+ }
+
+ // List related account groups in the project.
+ public static void listRelatedAccountGroups(String projectId) throws IOException {
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ ListRelatedAccountGroupsRequest request =
+ ListRelatedAccountGroupsRequest.newBuilder().setParent("projects/" + projectId).build();
+
+ System.out.println("Listing related account groups..");
+ for (RelatedAccountGroup group : client.listRelatedAccountGroups(request).iterateAll()) {
+ System.out.println(group.getName());
+ }
+ }
+ }
+}
+// [END recaptcha_enterprise_list_related_account_group]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/SearchRelatedAccountGroupMemberships.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/SearchRelatedAccountGroupMemberships.java
new file mode 100644
index 00000000000..9f55670ef88
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/account_defender/SearchRelatedAccountGroupMemberships.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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 account_defender;
+
+// [START recaptcha_enterprise_search_related_account_group_membership]
+
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.protobuf.ByteString;
+import com.google.recaptchaenterprise.v1.RelatedAccountGroupMembership;
+import com.google.recaptchaenterprise.v1.SearchRelatedAccountGroupMembershipsRequest;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+
+public class SearchRelatedAccountGroupMemberships {
+
+ public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
+ // TODO(developer): Replace these variables before running the sample.
+ // projectId: Google Cloud Project Id.
+ String projectId = "project-id";
+
+ // HMAC SHA-256 hashed unique id of the customer.
+ ByteString hashedAccountId = ByteString.copyFrom(new byte[] {});
+
+ searchRelatedAccountGroupMemberships(projectId, hashedAccountId);
+ }
+
+ // List group memberships for the hashed account id.
+ public static void searchRelatedAccountGroupMemberships(
+ String projectId, ByteString hashedAccountId) throws IOException {
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ SearchRelatedAccountGroupMembershipsRequest request =
+ SearchRelatedAccountGroupMembershipsRequest.newBuilder()
+ .setParent("projects/" + projectId)
+ .setHashedAccountId(hashedAccountId)
+ .build();
+
+ for (RelatedAccountGroupMembership groupMembership :
+ client.searchRelatedAccountGroupMemberships(request).iterateAll()) {
+ System.out.println(groupMembership.getName());
+ }
+ System.out.printf(
+ "Finished searching related account group memberships for %s!", hashedAccountId);
+ }
+ }
+}
+// [END recaptcha_enterprise_search_related_account_group_membership]
diff --git a/recaptcha_enterprise/snippets/src/main/java/recaptcha/passwordleak/CreatePasswordLeakAssessment.java b/recaptcha_enterprise/snippets/src/main/java/recaptcha/passwordleak/CreatePasswordLeakAssessment.java
new file mode 100644
index 00000000000..e0caf63a423
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/main/java/recaptcha/passwordleak/CreatePasswordLeakAssessment.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2022 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 passwordleak;
+
+// [START recaptcha_enterprise_password_leak_verification]
+
+import com.google.cloud.recaptcha.passwordcheck.PasswordCheckResult;
+import com.google.cloud.recaptcha.passwordcheck.PasswordCheckVerification;
+import com.google.cloud.recaptcha.passwordcheck.PasswordCheckVerifier;
+import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
+import com.google.protobuf.ByteString;
+import com.google.recaptchaenterprise.v1.Assessment;
+import com.google.recaptchaenterprise.v1.CreateAssessmentRequest;
+import com.google.recaptchaenterprise.v1.Event;
+import com.google.recaptchaenterprise.v1.PrivatePasswordLeakVerification;
+import com.google.recaptchaenterprise.v1.TokenProperties;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+public class CreatePasswordLeakAssessment {
+
+ public static void main(String[] args)
+ throws IOException, ExecutionException, InterruptedException {
+ // TODO(developer): Replace these variables before running the sample.
+ // GCloud Project ID.
+ String projectID = "project-id";
+
+ // Site key obtained by registering a domain/app to use recaptcha Enterprise.
+ String recaptchaSiteKey = "recaptcha-site-key";
+
+ // The token obtained from the client on passing the recaptchaSiteKey.
+ // To get the token, integrate the recaptchaSiteKey with frontend. See,
+ // https://cloud.google.com/recaptcha-enterprise/docs/instrument-web-pages#frontend_integration_score
+ String token = "recaptcha-token";
+
+ // Action name corresponding to the token.
+ String recaptchaAction = "recaptcha-action";
+
+ checkPasswordLeak(projectID, recaptchaSiteKey, token, recaptchaAction);
+ }
+
+ /*
+ * Detect password leaks and breached credentials to prevent account takeovers (ATOs)
+ * and credential stuffing attacks.
+ * For more information, see: https://cloud.google.com/recaptcha-enterprise/docs/getting-started
+ * and https://security.googleblog.com/2019/02/protect-your-accounts-from-data.html
+
+ * Steps:
+ * 1. Use the 'createVerification' method to hash and Encrypt the hashed username and password.
+ * 2. Send the hash prefix (2-byte) and the encrypted credentials to create the assessment.
+ * (Hash prefix is used to partition the database.)
+ * 3. Password leak assessment returns a database whose prefix matches the sent hash prefix.
+ * Create Assessment also sends back re-encrypted credentials.
+ * 4. The re-encrypted credential is then locally verified to see if there is a
+ * match in the database.
+ *
+ * To perform hashing, encryption and verification (steps 1, 2 and 4),
+ * reCAPTCHA Enterprise provides a helper library in Java.
+ * See, https://github.com/GoogleCloudPlatform/java-recaptcha-password-check-helpers
+
+ * If you want to extend this behavior to your own implementation/ languages,
+ * make sure to perform the following steps:
+ * 1. Hash the credentials (First 2 bytes of the result is the 'lookupHashPrefix')
+ * 2. Encrypt the hash (result = 'encryptedUserCredentialsHash')
+ * 3. Get back the PasswordLeak information from reCAPTCHA Enterprise Create Assessment.
+ * 4. Decrypt the obtained 'credentials.getReencryptedUserCredentialsHash()'
+ * with the same key you used for encryption.
+ * 5. Check if the decrypted credentials are present in 'credentials.getEncryptedLeakMatchPrefixesList()'.
+ * 6. If there is a match, that indicates a credential breach.
+ */
+ public static void checkPasswordLeak(
+ String projectID, String recaptchaSiteKey, String token, String recaptchaAction)
+ throws ExecutionException, InterruptedException, IOException {
+ // Set the username and password to be checked.
+ String username = "username";
+ String password = "password123";
+
+ // Instantiate the java-password-leak-helper library to perform the cryptographic functions.
+ PasswordCheckVerifier passwordLeak = new PasswordCheckVerifier();
+
+ // Create the request to obtain the hash prefix and encrypted credentials.
+ PasswordCheckVerification verification =
+ passwordLeak.createVerification(username, password).get();
+
+ byte[] lookupHashPrefix = verification.getLookupHashPrefix();
+ byte[] encryptedUserCredentialsHash = verification.getEncryptedLookupHash();
+
+ // Pass the credentials to the createPasswordLeakAssessment() to get back
+ // the matching database entry for the hash prefix.
+ PrivatePasswordLeakVerification credentials =
+ createPasswordLeakAssessment(
+ projectID,
+ recaptchaSiteKey,
+ token,
+ recaptchaAction,
+ lookupHashPrefix,
+ encryptedUserCredentialsHash);
+
+ // Convert to appropriate input format.
+ List leakMatchPrefixes =
+ credentials.getEncryptedLeakMatchPrefixesList().stream()
+ .map(ByteString::toByteArray)
+ .collect(Collectors.toList());
+
+ // Verify if the encrypted credentials are present in the obtained match list.
+ PasswordCheckResult result =
+ passwordLeak
+ .verify(
+ verification,
+ credentials.getReencryptedUserCredentialsHash().toByteArray(),
+ leakMatchPrefixes)
+ .get();
+
+ // Check if the credential is leaked.
+ boolean isLeaked = result.areCredentialsLeaked();
+ System.out.printf("Is Credential leaked: %s", isLeaked);
+ }
+
+ // Create a reCAPTCHA Enterprise assessment.
+ // Returns: PrivatePasswordLeakVerification which contains
+ // reencryptedUserCredentialsHash and credential breach database
+ // whose prefix matches the lookupHashPrefix.
+ private static PrivatePasswordLeakVerification createPasswordLeakAssessment(
+ String projectID,
+ String recaptchaSiteKey,
+ String token,
+ String recaptchaAction,
+ byte[] lookupHashPrefix,
+ byte[] encryptedUserCredentialsHash)
+ throws IOException {
+ try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {
+
+ // Set the properties of the event to be tracked.
+ Event event = Event.newBuilder().setSiteKey(recaptchaSiteKey).setToken(token).build();
+
+ // Set the hashprefix and credentials hash.
+ // Setting this will trigger the Password leak protection.
+ PrivatePasswordLeakVerification passwordLeakVerification =
+ PrivatePasswordLeakVerification.newBuilder()
+ .setLookupHashPrefix(ByteString.copyFrom(lookupHashPrefix))
+ .setEncryptedUserCredentialsHash(ByteString.copyFrom(encryptedUserCredentialsHash))
+ .build();
+
+ // Build the assessment request.
+ CreateAssessmentRequest createAssessmentRequest =
+ CreateAssessmentRequest.newBuilder()
+ .setParent(String.format("projects/%s", projectID))
+ .setAssessment(
+ Assessment.newBuilder()
+ .setEvent(event)
+ // Set request for Password leak verification.
+ .setPrivatePasswordLeakVerification(passwordLeakVerification)
+ .build())
+ .build();
+
+ // Send the create assessment request.
+ Assessment response = client.createAssessment(createAssessmentRequest);
+
+ // Check validity and integrity of the response.
+ if (!checkTokenIntegrity(response.getTokenProperties(), recaptchaAction)) {
+ return passwordLeakVerification;
+ }
+
+ // Get the reCAPTCHA Enterprise score.
+ float recaptchaScore = response.getRiskAnalysis().getScore();
+ System.out.println("The reCAPTCHA score is: " + recaptchaScore);
+
+ // Get the assessment name (id). Use this to annotate the assessment.
+ String assessmentName = response.getName();
+ System.out.println(
+ "Assessment name: " + assessmentName.substring(assessmentName.lastIndexOf("/") + 1));
+
+ return response.getPrivatePasswordLeakVerification();
+ }
+ }
+
+ // Check for token validity and action integrity.
+ private static boolean checkTokenIntegrity(
+ TokenProperties tokenProperties, String recaptchaAction) {
+ // Check if the token is valid.
+ if (!tokenProperties.getValid()) {
+ System.out.println(
+ "The Password check call failed because the token was: "
+ + tokenProperties.getInvalidReason().name());
+ return false;
+ }
+
+ // Check if the expected action was executed.
+ if (!tokenProperties.getAction().equals(recaptchaAction)) {
+ System.out.printf(
+ "The action attribute in the reCAPTCHA tag '%s' does not match "
+ + "the action '%s' you are expecting to score",
+ tokenProperties.getAction(), recaptchaAction);
+ return false;
+ }
+ return true;
+ }
+}
+// [END recaptcha_enterprise_password_leak_verification]
diff --git a/recaptcha_enterprise/snippets/src/pom.xml b/recaptcha_enterprise/snippets/src/pom.xml
new file mode 100644
index 00000000000..091ee4905ad
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/pom.xml
@@ -0,0 +1,129 @@
+
+
+ 4.0.0
+ com.example.recaptchaenterprise
+ recaptcha-enterprise-snippets
+ jar
+ Google reCAPTCHA Enterprise Snippets
+ https://github.com/GoogleCloudPlatform/java-docs-samples/tree/main/recaptchaenterprise
+
+
+
+ com.google.cloud.samples
+ shared-configuration
+ 1.2.0
+
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+
+ com.google.cloud
+ libraries-bom
+ 26.1.4
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
+
+ com.google.cloud
+ google-cloud-recaptchaenterprise
+
+
+ com.google.cloud
+ recaptcha-password-check-helpers
+ 1.0.1
+
+
+
+
+
+ org.seleniumhq.selenium
+ selenium-java
+ 4.5.0
+
+
+ org.seleniumhq.selenium
+ selenium-chrome-driver
+ 4.5.0
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+
+
+
+ io.github.bonigarcia
+ webdrivermanager
+ 5.3.0
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ com.google.truth
+ truth
+ 1.1.3
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.7.4
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.7.4
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+ 2.7.4
+
+
+ com.google.api
+ api-common
+
+
+
+
+
+
diff --git a/recaptcha_enterprise/snippets/src/resources/templates/index.html b/recaptcha_enterprise/snippets/src/resources/templates/index.html
new file mode 100644
index 00000000000..77e1ed6f659
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/resources/templates/index.html
@@ -0,0 +1,79 @@
+
+
+
+
+ reCAPTCHA-Enterprise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/recaptcha_enterprise/snippets/src/test/java/app/SnippetsIT.java b/recaptcha_enterprise/snippets/src/test/java/app/SnippetsIT.java
new file mode 100644
index 00000000000..95958adf933
--- /dev/null
+++ b/recaptcha_enterprise/snippets/src/test/java/app/SnippetsIT.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2021 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 app;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import account_defender.AccountDefenderAssessment;
+import account_defender.AnnotateAccountDefenderAssessment;
+import account_defender.ListRelatedAccountGroupMemberships;
+import account_defender.ListRelatedAccountGroups;
+import account_defender.SearchRelatedAccountGroupMemberships;
+import com.google.protobuf.ByteString;
+import io.github.bonigarcia.wdm.WebDriverManager;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.util.UriComponentsBuilder;
+import recaptcha.AnnotateAssessment;
+import recaptcha.GetMetrics;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@EnableAutoConfiguration
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+public class SnippetsIT {
+
+ private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT");
+ private static final String DOMAIN_NAME = "localhost";
+ private static String RECAPTCHA_SITE_KEY_1 = "recaptcha-site-key1";
+ private static String RECAPTCHA_SITE_KEY_2 = "recaptcha-site-key2";
+ private static WebDriver browser;
+ @LocalServerPort private int randomServerPort;
+ private ByteArrayOutputStream stdOut;
+
+ @Test
+ public void testCreateAnnotateAssessment()
+ throws JSONException, IOException, InterruptedException, NoSuchAlgorithmException,
+ ExecutionException {
+ // Create an assessment.
+ String testURL = "http://localhost:" + randomServerPort + "/";
+ JSONObject createAssessmentResult =
+ createAssessment(testURL, ByteString.EMPTY, AssessmentType.ASSESSMENT);
+ String assessmentName = createAssessmentResult.getString("assessmentName");
+ // Verify that the assessment name has been modified post the assessment creation.
+ assertThat(assessmentName).isNotEmpty();
+
+ // Annotate the assessment.
+ AnnotateAssessment.annotateAssessment(PROJECT_ID, assessmentName);
+ assertThat(stdOut.toString()).contains("Annotated response sent successfully ! ");
+ }
+
+ // Check if the required environment variables are set.
+ public static void requireEnvVar(String envVarName) {
+ assertWithMessage(String.format("Missing environment variable '%s' ", envVarName))
+ .that(System.getenv(envVarName))
+ .isNotEmpty();
+ }
+
+ @BeforeClass
+ public static void setUp() throws IOException, InterruptedException {
+ requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS");
+ requireEnvVar("GOOGLE_CLOUD_PROJECT");
+
+ // Create reCAPTCHA Site key and associate the given domain.
+ RECAPTCHA_SITE_KEY_1 = recaptcha.CreateSiteKey.createSiteKey(PROJECT_ID, DOMAIN_NAME);
+ RECAPTCHA_SITE_KEY_2 = recaptcha.CreateSiteKey.createSiteKey(PROJECT_ID, DOMAIN_NAME);
+ TimeUnit.SECONDS.sleep(5);
+
+ // Set Selenium Driver to Chrome.
+ WebDriverManager.chromedriver().setup();
+ browser = new ChromeDriver();
+ }
+
+ @AfterClass
+ public static void cleanUp()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(stdOut));
+
+ recaptcha.DeleteSiteKey.deleteSiteKey(PROJECT_ID, RECAPTCHA_SITE_KEY_1);
+ assertThat(stdOut.toString()).contains("reCAPTCHA Site key successfully deleted !");
+
+ browser.quit();
+
+ stdOut.close();
+ System.setOut(null);
+ }
+
+ @Before
+ public void beforeEach() {
+ stdOut = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(stdOut));
+ }
+
+ @After
+ public void afterEach() {
+ stdOut = null;
+ System.setOut(null);
+ }
+
+ @Test
+ public void testCreateSiteKey() {
+ // Test if the recaptcha site key has already been successfully created, as part of the setup.
+ Assert.assertFalse(RECAPTCHA_SITE_KEY_1.isEmpty());
+ }
+
+ @Test
+ public void testGetKey()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ recaptcha.GetSiteKey.getSiteKey(PROJECT_ID, RECAPTCHA_SITE_KEY_1);
+ assertThat(stdOut.toString()).contains(RECAPTCHA_SITE_KEY_1);
+ }
+
+ @Test
+ public void testListSiteKeys() throws IOException {
+ recaptcha.ListSiteKeys.listSiteKeys(PROJECT_ID);
+ assertThat(stdOut.toString()).contains(RECAPTCHA_SITE_KEY_1);
+ }
+
+ @Test
+ public void testUpdateSiteKey()
+ throws IOException, InterruptedException, ExecutionException, TimeoutException {
+ recaptcha.UpdateSiteKey.updateSiteKey(PROJECT_ID, RECAPTCHA_SITE_KEY_1, DOMAIN_NAME);
+ assertThat(stdOut.toString()).contains("reCAPTCHA Site key successfully updated !");
+ }
+
+ @Test
+ public void testDeleteSiteKey()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ recaptcha.DeleteSiteKey.deleteSiteKey(PROJECT_ID, RECAPTCHA_SITE_KEY_2);
+ assertThat(stdOut.toString()).contains("reCAPTCHA Site key successfully deleted !");
+ }
+
+ @Test
+ public void testCreateAnnotateAccountDefender()
+ throws JSONException, IOException, InterruptedException, NoSuchAlgorithmException,
+ ExecutionException {
+
+ String testURL = "http://localhost:" + randomServerPort + "/";
+ // Create a random SHA-256 Hashed account id.
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] hashBytes =
+ digest.digest(
+ ("default-" + UUID.randomUUID().toString().split("-")[0])
+ .getBytes(StandardCharsets.UTF_8));
+ ByteString hashedAccountId = ByteString.copyFrom(hashBytes);
+
+ // Create the assessment.
+ JSONObject createAssessmentResult =
+ createAssessment(testURL, hashedAccountId, AssessmentType.ACCOUNT_DEFENDER);
+ String assessmentName = createAssessmentResult.getString("assessmentName");
+ // Verify that the assessment name has been modified post the assessment creation.
+ assertThat(assessmentName).isNotEmpty();
+
+ // Annotate the assessment.
+ AnnotateAccountDefenderAssessment.annotateAssessment(
+ PROJECT_ID, assessmentName, hashedAccountId);
+ assertThat(stdOut.toString()).contains("Annotated response sent successfully ! ");
+
+ // NOTE: The below assert statements have no significant effect,
+ // since reCAPTCHA doesn't generate response.
+ // To generate response, reCAPTCHA needs a threshold number of unique userIdentifier points
+ // to cluster results.
+ // Hence, re-running the test 'n' times is currently out of scope.
+
+ // List related account groups in the project.
+ ListRelatedAccountGroups.listRelatedAccountGroups(PROJECT_ID);
+ assertThat(stdOut.toString()).contains("Listing related account groups..");
+
+ // List related account group memberships.
+ ListRelatedAccountGroupMemberships.listRelatedAccountGroupMemberships(PROJECT_ID, "legitimate");
+ assertThat(stdOut.toString()).contains("Finished listing related account group memberships.");
+
+ // Search related group memberships for a hashed account id.
+ SearchRelatedAccountGroupMemberships.searchRelatedAccountGroupMemberships(
+ PROJECT_ID, hashedAccountId);
+ assertThat(stdOut.toString())
+ .contains(
+ String.format(
+ "Finished searching related account group memberships for %s", hashedAccountId));
+ }
+
+ @Test
+ public void testGetMetrics() throws IOException {
+ GetMetrics.getMetrics(PROJECT_ID, RECAPTCHA_SITE_KEY_1);
+ assertThat(stdOut.toString())
+ .contains("Retrieved the bucket count for score based key: " + RECAPTCHA_SITE_KEY_1);
+ }
+
+ @Test
+ public void testPasswordLeakAssessment()
+ throws JSONException, IOException, ExecutionException, InterruptedException {
+ String testURL = "http://localhost:" + randomServerPort + "/";
+ createAssessment(testURL, ByteString.EMPTY, AssessmentType.PASSWORD_LEAK);
+ assertThat(stdOut.toString()).contains("Is Credential leaked: ");
+ }
+
+ public JSONObject createAssessment(
+ String testURL, ByteString hashedAccountId, AssessmentType assessmentType)
+ throws IOException, JSONException, InterruptedException, ExecutionException {
+
+ // Setup the automated browser test and retrieve the token and action.
+ JSONObject tokenActionPair = initiateBrowserTest(testURL);
+
+ // Send the token for analysis. The analysis score ranges from 0.0 to 1.0
+ switch (assessmentType) {
+ case ACCOUNT_DEFENDER:
+ {
+ AccountDefenderAssessment.accountDefenderAssessment(
+ PROJECT_ID,
+ RECAPTCHA_SITE_KEY_1,
+ tokenActionPair.getString("token"),
+ tokenActionPair.getString("action"),
+ hashedAccountId);
+ break;
+ }
+ case ASSESSMENT:
+ {
+ recaptcha.CreateAssessment.createAssessment(
+ PROJECT_ID,
+ RECAPTCHA_SITE_KEY_1,
+ tokenActionPair.getString("token"),
+ tokenActionPair.getString("action"));
+ break;
+ }
+ case PASSWORD_LEAK:
+ {
+ passwordleak.CreatePasswordLeakAssessment.checkPasswordLeak(
+ PROJECT_ID,
+ RECAPTCHA_SITE_KEY_1,
+ tokenActionPair.getString("token"),
+ tokenActionPair.getString("action"));
+ break;
+ }
+ }
+
+ // Assert the response.
+ String response = stdOut.toString();
+ assertThat(response).contains("Assessment name: ");
+ assertThat(response).contains("The reCAPTCHA score is: ");
+ if (!hashedAccountId.isEmpty()) {
+ assertThat(response).contains("Account Defender Assessment Result: ");
+ }
+
+ // Retrieve the results.
+ float recaptchaScore = 0;
+ String assessmentName = "";
+ for (String line : response.split("\n")) {
+ if (line.contains("The reCAPTCHA score is: ")) {
+ recaptchaScore = Float.parseFloat(substr(line));
+ } else if (line.contains("Assessment name: ")) {
+ assessmentName = substr(line);
+ }
+ }
+
+ // Set the score.
+ browser.findElement(By.cssSelector("#assessment")).sendKeys(String.valueOf(recaptchaScore));
+ return new JSONObject()
+ .put("recaptchaScore", recaptchaScore)
+ .put("assessmentName", assessmentName);
+ }
+
+ enum AssessmentType {
+ ASSESSMENT,
+ ACCOUNT_DEFENDER,
+ PASSWORD_LEAK;
+
+ AssessmentType() {}
+ }
+
+ public JSONObject initiateBrowserTest(String testURL)
+ throws IOException, JSONException, InterruptedException {
+ // Construct the URL to call for validating the assessment.
+ URI url =
+ UriComponentsBuilder.fromUriString(testURL)
+ .queryParam("recaptchaSiteKey", RECAPTCHA_SITE_KEY_1)
+ .build()
+ .encode()
+ .toUri();
+
+ browser.get(url.toURL().toString());
+
+ // Wait until the page is loaded.
+ JavascriptExecutor js = (JavascriptExecutor) browser;
+ new WebDriverWait(browser, Duration.ofSeconds(10))
+ .until(webDriver -> js.executeScript("return document.readyState").equals("complete"));
+
+ // Pass the values to be entered.
+ browser.findElement(By.id("username")).sendKeys("username");
+ browser.findElement(By.id("password")).sendKeys("password");
+
+ // On clicking the button, the request params will be sent to reCAPTCHA.
+ browser.findElement(By.id("recaptchabutton")).click();
+
+ TimeUnit.SECONDS.sleep(1);
+
+ // Retrieve the reCAPTCHA token response.
+ WebElement element = browser.findElement(By.cssSelector("#assessment"));
+ String token = element.getAttribute("data-token");
+ String action = element.getAttribute("data-action");
+
+ return new JSONObject().put("token", token).put("action", action);
+ }
+
+ public String substr(String line) {
+ return line.substring((line.lastIndexOf(":") + 1)).trim();
+ }
+}