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(); + } +}