Google Recaptcha v3 example demo

199,551

Solution 1

Simple code to implement ReCaptcha v3

The basic JS code

<script src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"></script>
<script>
    grecaptcha.ready(function() {
    // do request for recaptcha token
    // response is promise with passed token
        grecaptcha.execute('your reCAPTCHA site key here', {action:'validate_captcha'})
                  .then(function(token) {
            // add token value to form
            document.getElementById('g-recaptcha-response').value = token;
        });
    });
</script>

The basic HTML code

<form id="form_id" method="post" action="your_action.php">
    <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
    <input type="hidden" name="action" value="validate_captcha">
    .... your fields
</form>

The basic PHP code

if (isset($_POST['g-recaptcha-response'])) {
    $captcha = $_POST['g-recaptcha-response'];
} else {
    $captcha = false;
}

if (!$captcha) {
    //Do something with error
} else {
    $secret   = 'Your secret key here';
    $response = file_get_contents(
        "https://www.google.com/recaptcha/api/siteverify?secret=" . $secret . "&response=" . $captcha . "&remoteip=" . $_SERVER['REMOTE_ADDR']
    );
    // use json_decode to extract json response
    $response = json_decode($response);

    if ($response->success === false) {
        //Do something with error
    }
}

//... The Captcha is valid you can continue with the rest of your code
//... Add code to filter access using $response . score
if ($response->success==true && $response->score <= 0.5) {
    //Do something to denied access
}

You have to filter access using the value of $response.score. It can takes values from 0.0 to 1.0, where 1.0 means the best user interaction with your site and 0.0 the worst interaction (like a bot). You can see some examples of use in ReCaptcha documentation.

Solution 2

I thought a fully-functioning reCaptcha v3 example demo in PHP, using a Bootstrap 4 form, might be useful to some.

Reference the shown dependencies, swap in your email address and keys (create your own keys here), and the form is ready to test and use. I made code comments to better clarify the logic and also included commented-out console log and print_r lines to quickly enable viewing the validation token and data generated from Google.

The included jQuery function is optional, though it does create a much better user prompt experience in this demo.


PHP file (mail.php):

Add secret key (2 places) and email address where noted.

<?php

if ($_SERVER["REQUEST_METHOD"] == "POST") {

  # BEGIN Setting reCaptcha v3 validation data
  $url = "https://www.google.com/recaptcha/api/siteverify";
  $data = [
    'secret' => "your-secret-key-here",
    'response' => $_POST['token'],
    'remoteip' => $_SERVER['REMOTE_ADDR']
  ];

  $options = array(
    'http' => array(
      'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
      'method'  => 'POST',
      'content' => http_build_query($data)
    )
    );
  
  # Creates and returns stream context with options supplied in options preset 
  $context  = stream_context_create($options);
  # file_get_contents() is the preferred way to read the contents of a file into a string
  $response = file_get_contents($url, false, $context);
  # Takes a JSON encoded string and converts it into a PHP variable
  $res = json_decode($response, true);
  # END setting reCaptcha v3 validation data
   
    // print_r($response); 
# Post form OR output alert and bypass post if false. NOTE: score conditional is optional
# since the successful score default is set at >= 0.5 by Google. Some developers want to
# be able to control score result conditions, so I included that in this example.

  if ($res['success'] == true && $res['score'] >= 0.5) {
 
    # Recipient email
    $mail_to = "[email protected]";
    
    # Sender form data
    $subject = trim($_POST["subject"]);
    $name = str_replace(array("\r","\n"),array(" "," ") , strip_tags(trim($_POST["name"])));
    $email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
    $phone = trim($_POST["phone"]);
    $message = trim($_POST["message"]);
    
    if (empty($name) OR !filter_var($email, FILTER_VALIDATE_EMAIL) OR empty($phone) OR empty($subject) OR empty($message)) {
      # Set a 400 (bad request) response code and exit
      http_response_code(400);
      echo '<p class="alert-warning">Please complete the form and try again.</p>';
      exit;
    }

    # Mail content
    $content = "Name: $name\n";
    $content .= "Email: $email\n\n";
    $content .= "Phone: $phone\n";
    $content .= "Message:\n$message\n";

    # Email headers
    $headers = "From: $name <$email>";

    # Send the email
    $success = mail($mail_to, $subject, $content, $headers);
    
    if ($success) {
      # Set a 200 (okay) response code
      http_response_code(200);
      echo '<p class="alert alert-success">Thank You! Your message has been successfully sent.</p>';
    } else {
      # Set a 500 (internal server error) response code
      http_response_code(500);
      echo '<p class="alert alert-warning">Something went wrong, your message could not be sent.</p>';
    }   

  } else {

    echo '<div class="alert alert-danger">
        Error! The security token has expired or you are a bot.
       </div>';
  }  

} else {
  # Not a POST request, set a 403 (forbidden) response code
  http_response_code(403);
  echo '<p class="alert-warning">There was a problem with your submission, please try again.</p>';
} ?>

HTML <head>

Bootstrap CSS dependency and reCaptcha client-side validation Place between <head> tags - paste your own site-key where noted.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="https://www.google.com/recaptcha/api.js?render=your-site-key-here"></script>

HTML <body>

Place between <body> tags.

<!-- contact form demo container -->
<section style="margin: 50px 20px;">
  <div style="max-width: 768px; margin: auto;">
    
    <!-- contact form -->
    <div class="card">
      <h2 class="card-header">Contact Form</h2>
      <div class="card-body">
        <form class="contact_form" method="post" action="mail.php">

          <!-- form fields -->
          <div class="row">
            <div class="col-md-6 form-group">
              <input name="name" type="text" class="form-control" placeholder="Name" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="email" type="email" class="form-control" placeholder="Email" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="phone" type="text" class="form-control" placeholder="Phone" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="subject" type="text" class="form-control" placeholder="Subject" required>
            </div>
            <div class="col-12 form-group">
              <textarea name="message" class="form-control" rows="5" placeholder="Message" required></textarea>
            </div>

            <!-- form message prompt -->
            <div class="row">
              <div class="col-12">
                <div class="contact_msg" style="display: none">
                  <p>Your message was sent.</p>
                </div>
              </div>
            </div>

            <div class="col-12">
              <input type="submit" value="Submit Form" class="btn btn-success" name="post">
            </div>

            <!-- hidden reCaptcha token input -->
            <input type="hidden" id="token" name="token">
          </div>

        </form>
      </div>
    </div>

  </div>
</section>
<script>
  grecaptcha.ready(function() {
    grecaptcha.execute('your-site-key-here', {action: 'homepage'}).then(function(token) {
       // console.log(token);
       document.getElementById("token").value = token;
    });
    // refresh token every minute to prevent expiration
    setInterval(function(){
      grecaptcha.execute('your-site-key-here', {action: 'homepage'}).then(function(token) {
        console.log( 'refreshed token:', token );
        document.getElementById("token").value = token;
      });
    }, 60000);

  });
</script>

<!-- References for the optional jQuery function to enhance end-user prompts -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="form.js"></script>

Optional jQuery function for enhanced UX (form.js):

(function ($) {
'use strict';

var form = $('.contact_form'),
  message = $('.contact_msg'),
  form_data;

// Success function
function done_func(response) {
  message.fadeIn()
  message.html(response);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
  form.find('input:not([type="submit"]), textarea').val('');
}

// fail function
function fail_func(data) {
  message.fadeIn()
  message.html(data.responseText);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
}

form.submit(function (e) {
  e.preventDefault();
  form_data = $(this).serialize();
  $.ajax({
    type: 'POST',
    url: form.attr('action'),
    data: form_data
  })
  .done(done_func)
  .fail(fail_func);
}); })(jQuery);

Solution 3

I am assuming you have site key and secret in place. Follow this step.

In your HTML file, add the script.

 <script src="https://www.google.com/recaptcha/api.js?render=put your site key here"></script>

Also, do use jQuery for easy event handling.

Here is the simple form.

 <form id="comment_form" action="form.php" method="post" >
      <input type="email" name="email" placeholder="Type your email" size="40"><br><br>
      <textarea name="comment" rows="8" cols="39"></textarea><br><br>
      <input type="submit" name="submit" value="Post comment"><br><br>
    </form>

You need to initialize the Google recaptcha and listen for the ready event. Here is how to do that.

     <script>
       // when form is submit
    $('#comment_form').submit(function() {
        // we stoped it
        event.preventDefault();
        var email = $('#email').val();
        var comment = $("#comment").val();
        // needs for recaptacha ready
        grecaptcha.ready(function() {
            // do request for recaptcha token
            // response is promise with passed token
            grecaptcha.execute('put your site key here', {action: 'create_comment'}).then(function(token) {
                // add token to form
                $('#comment_form').prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
                $.post("form.php",{email: email, comment: comment, token: token}, function(result) {
                        console.log(result);
                        if(result.success) {
                                alert('Thanks for posting comment.')
                        } else {
                                alert('You are spammer ! Get the @$%K out.')
                        }
                });
            });
        });
  });
  </script>

Here is the sample PHP file. You can use Servlet or Node or any backend language in place of it.

<?php

        $email;$comment;$captcha;
        if(isset($_POST['email'])){
          $email=$_POST['email'];
        }if(isset($_POST['comment'])){
          $comment=$_POST['comment'];
        }if(isset($_POST['token'])){
          $captcha=$_POST['token'];
          }
        if(!$captcha){
          echo '<h2>Please check the the captcha form.</h2>';
          exit;
        }
        $secretKey = "put your secret key here";
        $ip = $_SERVER['REMOTE_ADDR'];

        // post request to server

        $url =  'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secretKey) .  '&response=' . urlencode($captcha);
        $response = file_get_contents($url);
        $responseKeys = json_decode($response,true);
        header('Content-type: application/json');
        if($responseKeys["success"]) {
                echo json_encode(array('success' => 'true'));
        } else {
                echo json_encode(array('success' => 'false'));
        }
?>

Here is the tutorial link: https://codeforgeek.com/2019/02/google-recaptcha-v3-tutorial/

Hope it helps.

Solution 4

We use recaptcha-V3 only to see site traffic quality, and used it as non blocking. Since recaptcha-V3 doesn't require to show on site and can be used as hidden but you have to show recaptcha privacy etc links (as recommended)

Script Tag in Head

<script src="https://www.google.com/recaptcha/api.js?onload=ReCaptchaCallbackV3&render='SITE KEY' async defer></script>

Note: "async defer" make sure its non blocking which is our specific requirement

JS Code:

<script>
    ReCaptchaCallbackV3 = function() {
        grecaptcha.ready(function() {
            grecaptcha.execute("SITE KEY").then(function(token) {
                $.ajax({
                    type: "POST",
                    url: `https://api.${window.appInfo.siteDomain}/v1/recaptcha/score`,
                    data: {
                        "token" : token,
                    },
                    success: function(data) {
                        if(data.response.success) {
                            window.recaptchaScore = data.response.score;
                            console.log('user score ' + data.response.score)
                        }
                    },
                    error: function() {
                        console.log('error while getting google recaptcha score!')
                    }
                });

            });
        });
    };
</script> 

HTML/Css Code:

there is no html code since our requirement is just to get score and don't want to show recaptcha badge.

Backend - Laravel Code:

Route:

Route::post('/recaptcha/score', 'Api\\ReCaptcha\\RecaptchaScore@index');


Class:

class RecaptchaScore extends Controller
{
    public function index(Request $request)
    {
        $score = null;

        $response = (new Client())->request('post', 'https://www.google.com/recaptcha/api/siteverify', [
            'form_params' => [
                'response' => $request->get('token'),
                'secret' => 'SECRET HERE',
            ],
        ]);

        $score = json_decode($response->getBody()->getContents(), true);

        if (!$score['success']) {
            Log::warning('Google ReCaptcha Score', [
                'class' => __CLASS__,
                'message' => json_encode($score['error-codes']),
            ]);
        }

        return [
            'response' => $score,
        ];
    }
} 

we get back score and save in variable which we later user when submit form.

Reference: https://developers.google.com/recaptcha/docs/v3 https://developers.google.com/recaptcha/

Solution 5

I have seen most of the articles that don't work properly that's why new developers and professional developers get confused about it.

I am explaining to you in a very simple way. In this code, I am generating a google Recaptcha token at the client side at every 3 seconds of time interval because the token is valid for only a few minutes that's why if any user takes time to fill the form then it may be expired.

First I have an index.php file where I am going to write HTML and JavaScript code.

    <!DOCTYPE html>
<html>
   <head>
      <title>Google Recaptcha V3</title>
   </head>
   <body>
      <h1>Google Recaptcha V3</h1>
      <form action="recaptcha.php" method="post">
         <label>Name</label>
         <input type="text" name="name" id="name">
         <input type="hidden" name="token" id="token" /> 
         <input type="hidden" name="action" id="action" /> 
         <input type="submit" name="submit">
      </form>
      <script src="https://www.google.com/recaptcha/api.js?render=put your site key here"></script>
      <script  src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
      <script type="text/javascript">
         $(document).ready(function(){
            setInterval(function(){
            grecaptcha.ready(function() {
                grecaptcha.execute('put your site key here', {action: 'application_form'}).then(function(token) {
                    $('#token').val(token);
                    $('#action').val('application_form');
                });
            });
            }, 3000);
         });

      </script>
   </body>
</html>

Next, I have created recaptcha.php file to execute it at the server side

<?php

if ($_POST['submit']) {
    $name   = $_POST['name'];
    $token  = $_POST['token'];
    $action = $_POST['action'];

    $curlData = array(
        'secret' => 'put your secret key here',
        'response' => $token
    );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($curlData));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $curlResponse = curl_exec($ch);

    $captchaResponse = json_decode($curlResponse, true);

    if ($captchaResponse['success'] == '1' && $captchaResponse['action'] == $action && $captchaResponse['score'] >= 0.5 && $captchaResponse['hostname'] == $_SERVER['SERVER_NAME']) {
        echo 'Form Submitted Successfully';
    } else {
        echo 'You are not a human';
    }
}

Source of this code. If you would like to know the explanation of this code please visit. Google reCAPTCHA V3 integration in PHP

Share:
199,551
Ommadawn
Author by

Ommadawn

I code by fun, not for work, so when I code it's because I'm happy :)

Updated on July 08, 2022

Comments

  • Ommadawn
    Ommadawn almost 2 years

    Until now, I was working with Google Recaptcha v2, but now I want to update my WebApp using the lastest version (v3).

    Is it possible to anyone add a fully working Google Recaptcha v3 example for a basic form as I can't find any working demos of it?

    I'd really appreciate it.

    Thank you very much.

    PS: I'm using Java Servlets on the server side, but it doesn't matter if you explain using PHP or whatever.

  • user4520
    user4520 almost 5 years
    The code you've posted doesn't check the value of the score field; if I understand the docs correctly, success only indicates whether the posted request was valid; the actual information about the interaction (i.e. legitimate or not) is contained in the score field.
  • kikerrobles
    kikerrobles almost 5 years
    You are right, this is just a basic code for ReCaptcha to work. But warn that the filter is done through the "score" is necessary, thanks.
  • xinthose
    xinthose over 4 years
    to avoid the timeout-or-duplicate error, I suggest using setInterval to refresh the token (example)
  • Adam
    Adam over 4 years
    Its in the docs: Note: reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action. However, you call execute once the library is loaded. I would fix that.
  • cdosborn
    cdosborn over 4 years
    One has to wonder, why they require the developer to pass their key twice.
  • Luke A. Leber
    Luke A. Leber over 4 years
    Ditto what @Adam said, however I've noticed that calling grecaptcha.execute on form submit can add a VERY notable lag while the token is retrieved from Google. I've seen response times peak over 1000ms (tested today). Any suggestions on how to get around this?
  • tony
    tony about 4 years
    This is wrong and doesn't take into account the score which is needed in v3. Don't follow this guide, read in comments on the page linked.
  • Adam
    Adam almost 4 years
    @LukeA.Leber you can use setInterval to refresh the token.
  • Luke A. Leber
    Luke A. Leber almost 4 years
    @Adam - is there any guidance available from Google as to whether or not this would negatively affect scores for certain users? Imagine if a user walks away for a few hours and comes back to complete the form. The same user would have requested over 100 tokens in the same browsing session.
  • sktguha
    sktguha almost 4 years
    A bit non-related but is it possible to make only one request to the server using Google recaptcha. Because in current flow there are two request first to Google to get the token and then to the server. Instead can we just request our backend server directly and the backend server can make request on client behalf.
  • Pedro Lobito
    Pedro Lobito almost 4 years
    I wonder how a BAD example like this contains so many up-votes.
  • ashleedawg
    ashleedawg over 3 years
    very thorough; I got this one quickly working after giving up on the other (higher scored) answers. Thanks!
  • ashleedawg
    ashleedawg over 3 years
    After having no success with several answers (including this one), I had more luck with this answer.
  • ashleedawg
    ashleedawg over 3 years
    After having no success with several answers (including this one), I had more luck with this answer.
  • ashleedawg
    ashleedawg over 3 years
    After having no success with several answers (including this one), I had more luck with this answer.
  • ashleedawg
    ashleedawg over 3 years
    I agree with your first sentence... and this didn't work for me either. ("You are not a human"). The only answer that worked for me was this.
  • ashleedawg
    ashleedawg over 3 years
    Welcome to SO! More info or links would be helpful. (Check out How to Answer.)
  • ashleedawg
    ashleedawg over 3 years
    Note: "reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action rather than on page load."(src) This is especially important on a comment form -- I've had that happen as a user on live sites, after typing out detailed (lengthy) angry feedback, and then the site's form doesn't let me submit it. It can be very frustrating!
  • Furqan Freed
    Furqan Freed over 3 years
    @ashleedawg sorry if that didn't work for you ! i've just tested again and look like all good ! your reference is simple php implementation if you use this colutin which i've mentioned is written for #Laravel but it should also work if you simply use RecaptchaScore class.
  • Sumit Kumar Gupta
    Sumit Kumar Gupta over 3 years
    Hi @clayray, I have already applied score in code.
  • clayRay
    clayRay over 3 years
    Ah yes, so you have @SumitKumarGupta. Apologies, I will delete my comment.
  • Talk Nerdy To Me
    Talk Nerdy To Me over 3 years
    @ashleedawg edited to include refreshing token every 60 seconds
  • Albert
    Albert over 3 years
    @TalkNerdyToMe excuse my ignorance, in what part of your code are you refreshing the token every 60 seconds?
  • Talk Nerdy To Me
    Talk Nerdy To Me over 3 years
    @Albert No ignorance at all - it appears that the edit that I submitted was rejected. I guess SO only lets you know when an edit has been accepted?? That edit would really have helped so I don't know why it would have been thrown out. I have submitted the edit again so hopefully it won't be rejected this time, and if it is, I'll just do my best to explain it.
  • Gatsby
    Gatsby over 3 years
    @TalkNerdyToMe Sorry for the oversight on approving the edit, guys. I meant to test that out before approving a while back. Just tested it out - good to go - nice functionality improvement. Fun watching the token refresh in the console log too :).
  • Talk Nerdy To Me
    Talk Nerdy To Me over 3 years
    @Gatsy I also just submitted another edit to fix another issue that when using the optional jQuery for enhanced UX the token needs to be refreshed or else it fails unless you wait to resubmit the form until the token refreshes by itself from the previous edit's addition.
  • Gatsby
    Gatsby over 3 years
    @TalkNerdyToMe Hmm. I haven't been able to reproduce that error, unless I'm misunderstanding you. After initial page load, with the non-refreshed first token visible in the console, I'm able to submit forms successfully, even multiple form submissions work fine after the refresh function triggers a new token. And I'm referencing jQuery 3.3.1. So, can you elaborate? Also, since the Google token expires after 2 minutes, I'm curious why you want to refresh at 60 seconds? No big deal, I left it, yet I wanted to mention.
  • Talk Nerdy To Me
    Talk Nerdy To Me over 3 years
    @Gatsby I just thought to refresh it every minute instead of two purely for safety's sake. This answer does it every three seconds, but I personally think that's way overkill. The error occurs after you submit the form and then immediately submit it again before the token refresh cycle happens again. You just need to refresh the token again on successful form completion (run grecaptcha.execute() inside done_func()). I just tested it again to confirm.
  • Abe
    Abe over 3 years
    I get <code>Failed to load resource: the server responded with a status of 500 (Internal Server Error)</code> I am not sure what is the reason for this because it is linked to mail.php. ANy idea is highly appreciated!
  • timgavin
    timgavin about 3 years
    You can get your SITE key and SECRET key here: google.com/recaptcha/admin/create
  • Chris
    Chris about 3 years
    this worked for me. there are two places for site key and one place for secret. dont miss that out guys
  • kikerrobles
    kikerrobles almost 3 years
    @Jack this evaluation is for denied access, not for allow it.