diff --git a/.gitignore b/.gitignore index 81a80bf..e3dc8b9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ build/ **/example/ios/.symlinks/ **/example/ios/Pods/ +**/example/android/app/.cxx + **/ios/Flutter/ephemeral diff --git a/README.md b/README.md index 90e80d7..901d39d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # reCAPTCHA Enterprise Flutter Module Please note that issues filed in this repository are not an official Google @@ -11,9 +10,9 @@ in [https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk](https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk). For general documentation on reCAPTCHA Enterprise for mobile applications, see -[Android](https://cloud.google.com/recaptcha-enterprise/docs/instrument-android-apps) -and -[iOS](https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps). +[Android](https://cloud.google.com/recaptcha-enterprise/docs/instrument-android-apps), +[iOS](https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps), +and [Web](https://cloud.google.com/recaptcha-enterprise/docs/instrument-web-apps). ## Integrating reCAPTCHA Enterprise with your Flutter Application @@ -30,16 +29,25 @@ Flutter application for enhanced security. flutter pub add recaptcha_enterprise_flutter ``` - **Note:** This library supports only iOS and Android platforms. + **Note:** This library supports iOS, Android, and Web platforms. ### 2. Client Initialization 1. **Obtain Site Keys:** - Acquire your reCAPTCHA Enterprise site keys for both Android and iOS + Acquire your reCAPTCHA Enterprise site keys for Android, iOS, and Web platforms from the Google Cloud Console. -2. **Instantiate the `RecaptchaClient`:** +2. **Web Setup:** + + For web, you must include the reCAPTCHA Enterprise script in your + `index.html` file. Replace `[MYWEBSITEKEY]` with your actual web site key: + + ```html + + ``` + +3. **Instantiate the `RecaptchaClient`:** Initialize the client using the appropriate site key based on the platform. It's crucial to initialize the client as early as possible within your @@ -53,9 +61,11 @@ Flutter application for enhanced security. void main() async { WidgetsFlutterBinding.ensureInitialized(); - final siteKey = Platform.isAndroid - ? "" - : ""; + final siteKey = kIsWeb + ? "" + : Platform.isAndroid + ? "" + : ""; RecaptchaClient client = await Recaptcha.fetchClient(siteKey); @@ -78,15 +88,12 @@ Flutter application for enhanced security. ``` **Important:** + * Replace ``, ``, and `` with your actual reCAPTCHA Enterprise site keys. + * For web, make sure the ` + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000..4e21d14 --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "recaptcha_flutter_example", + "short_name": "recaptcha_flutter_example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/lib/recaptcha_enterprise_web.dart b/lib/recaptcha_enterprise_web.dart new file mode 100644 index 0000000..a30040b --- /dev/null +++ b/lib/recaptcha_enterprise_web.dart @@ -0,0 +1,66 @@ +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:recaptcha_enterprise_flutter/recaptcha_enterprise_platform_interface.dart'; + +import 'dart:async'; +import 'dart:js_interop'; + +@JS('grecaptcha.enterprise.ready') +external void _ready(JSFunction callback); + +@JS('grecaptcha.enterprise.execute') +external JSPromise _execute(String recaptchaKey, ExecuteOptions options); + +@JS() +@anonymous +@staticInterop +class ExecuteOptions { + external factory ExecuteOptions({String action}); +} + +class RecaptchaEnterpriseWeb extends RecaptchaEnterprisePlatform { + String? recaptchaKey; + + RecaptchaEnterpriseWeb(); + + static void registerWith(Registrar registrar) { + RecaptchaEnterprisePlatform.instance = RecaptchaEnterpriseWeb(); + } + + @override + Future initClient(String siteKey, {double? timeout}) async { + recaptchaKey = siteKey; + return true; + } + + @override + Future fetchClient(String siteKey) async { + recaptchaKey = siteKey; + return true; + } + + @override + Future execute(String action, {double? timeout}) async { + Map opts = { + 'action': action, + }; + + if (timeout != null) { + opts['timeout'] = timeout; + } + + final completer = Completer(); + + if (recaptchaKey == null) { + throw Exception("please initialize the client"); + } + + _ready(() { + final promise = _execute(recaptchaKey!, ExecuteOptions(action: action)); + promise.toDart + .then((value) => completer.complete(value.toString())) + .catchError(completer.completeError); + }.toJS); + + return completer.future; + } +} diff --git a/pubspec.lock b/pubspec.lock index f896eb6..421f94a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: @@ -67,30 +67,35 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -148,10 +153,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: @@ -172,42 +177,42 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.4" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "15.0.0" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index ece7c35..e1321eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_web_plugins: + sdk: flutter plugin_platform_interface: ^2.0.2 dev_dependencies: @@ -53,6 +55,9 @@ flutter: pluginClass: RecaptchaEnterprisePlugin ios: pluginClass: RecaptchaEnterprisePlugin + web: + pluginClass: RecaptchaEnterpriseWeb + fileName: recaptcha_enterprise_web.dart # To add assets to your plugin package, add an assets section, like this: # assets: