Search code examples
flutterrecaptcha

How to implement reCaptcha into a flutter app


I am trying to implement the reCaptcha function to my flutter app, but in the captcha registration I need to provide a domain which I don't have one for a mobile app. I have browsed several guides teaching how to implement reCaptcha into a mobile app, yet those guides registered their reCaptcha with package names but not domains. What is the correct way to implement reCaptcha in a flutter app, or any mobile app in 2020?


Solution

  • You can use this plugin, flutter_recaptcha.

    For the domain, I had the same issue. I first found that I needed to use the "I'm not a robot" checkbox option from here and I had to check the github repository to find this information, "!!! Remember to add this domain into the reCaptcha setting: recaptcha-flutter-plugin.firebaseapp.com," which explains it.

    Edit

    I noticed something after trying it out, that I'd like to mention. The plugin does not provide a captcha response for using to authenticate the user server-side, so it does not seem very useful as it is. However, it is a simple plugin, so it may be possible to use it as an example. The steps, I think, would be to create a webpage with the captcha. As with the plugin, use a webview to open the page, then capture the post output of the form and ip address of user submitting the form, using something like this, then send it to flutter and then submit your request with that information, and use the Google library to verify the captcha.

    Instructions

    I just finished implementing this and I found a good way that works. First, create an html page, like this:

    <html>
      <head>
        <title>reCAPTCHA</title>
        <script src="https://www.google.com/recaptcha/api.js" async defer></script>
      </head>
      <body style='background-color: aqua;'>
        <div style='height: 60px;'></div>
        <form action="?" method="POST">
          <div class="g-recaptcha" 
            data-sitekey="YOUR-SITE-KEY"
            data-callback="captchaCallback"></div>
          
        </form>
        <script>
          function captchaCallback(response){
            //console.log(response);
            if(typeof Captcha!=="undefined"){
              Captcha.postMessage(response);
            }
          }
        </script>
      </body>
    </html>
    

    Then, host that on your domain, say example.com/captcha.

    Then, create a flutter Widget, like this:

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:webview_flutter/webview_flutter.dart';
    
    class Captcha extends StatefulWidget{
      Function callback;
      Captcha(this.callback);
    
      @override
      State<StatefulWidget> createState() {
        return CaptchaState();
      }
    
    }
    class CaptchaState extends State<Captcha>{
      WebViewController webViewController;
      @override
      initState(){
        super.initState();
      }
    
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: WebView(
            initialUrl: "https://example.com/captcha.html",
            javascriptMode: JavascriptMode.unrestricted,
            javascriptChannels: Set.from([
              JavascriptChannel(
                name: 'Captcha',
                onMessageReceived: (JavascriptMessage message) {
                  //This is where you receive message from
                  //javascript code and handle in Flutter/Dart
                  //like here, the message is just being printed
                  //in Run/LogCat window of android studio
                  //print(message.message);
                  widget.callback(message.message);
                  Navigator.of(context).pop();
                })
            ]),
            onWebViewCreated: (WebViewController w) {
              webViewController = w;
            },
          )
        );
      }
    
    }
    

    Make sure you registered for a captcha key at https://www.google.com/recaptcha (click on "Admin Console" at the top-right).

    Then, you have the front-end built. To call a captcha, just run:

    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (context){
                          return Captcha((String code)=>print("Code returned: "+code));
                        }
                      ),
                    );
    

    You can use whatever callback you want to, like this:

    class GenericState extends State<Generic>{
    void methodWithCaptcha(String captchaCode){
      // Do something with captchaCode
    }
    
    @override
      Widget build(BuildContext context) {
        return Center(child:FlatButton(
            child: Text("Click here!"),
            onPressed: (){
                Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (context){
                          return Captcha(methodWithCaptcha);
                        }
                      ),
                    );
            }
      }
    }
    

    Server-side, you can follow the instructions here (I followed the sections "Direct Download" and "Usage"). I found that for the usage, I could simply use the code:

    $recaptcha = new \ReCaptcha\ReCaptcha($secret);
    $resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);
    if ($resp->isSuccess()) {
        // Verified!
    } else {
        $errors = $resp->getErrorCodes();
    }
    

    Using setExpectedHostname, like in the example, was unnecessary.

    After that, everything works! I think this is currently the best way to implement Google reCaptcha V2 in flutter (for both iOS and Android).