DEV Community

george_pollock
george_pollock

Posted on • Originally published at hvitis.dev on

How to implement reCAPTCHA v3

reCAPTCHA v3 - prepare yourself for bots!

I will just quickly explain you what is reCAPTCHA, give you some explanation on how it works and we will go to examples and usage.

What and how ? 👨‍🎓

reCAPTCHA is a technology that assesses probability that the entity that uses your web-code (page, app, portal etc) is a human and not a bot ( or the other way around ). Grabbing information of behaviour (of a user or a bot) it encapsulates it in the token that gets send to your server. On your server, the token is being send again to Google for returning the assessment on how probable is that the token was generated by a human. Part of response returned from Google to your server:

  "score": number // the score for request (0.0 likely bot - 1.0 likely human)
Enter fullscreen mode Exit fullscreen mode

Having this information you could use it to later in your logic e.g. to return 404 for suspicious requests.

It uses AI and information gathering under the hood which may be disliked by certain people. You can learn more on that matter and check the history & usage of reCAPTCHA to get better knowledge on how to use it and have your own opinion. Maybe you don´t really want to use it?

Last but not least, quote from docs on where to use it and where it can get usage data from:

For this reason, we recommend including reCAPTCHA verification on forms or actions as well as in the background of pages for analytics.

Let´s implement it - example and usage 🛠️

Have a quick look at the flow we will go through:

reCAPTCHA-flow-explanation-tutorial

We will be following the programmatic steps from the official guide

GENERATE

To generate the token first you need to generate the SECRET_SITE_KEY and SECRET_KEY. Then the steps are straightforward:

  • Add the JS script to the page (index.html). Front end frameworks have a folder called public that has index.html file inside. Open it and put the script on the bottom of the body, just like you would be adding bootstrap.
<body class="h-100">
  ...
  <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SIT_KEY"></script>
  <div id="app"></div>
</body>
Enter fullscreen mode Exit fullscreen mode
  • Add the function to methods available in the component.
 methods: {
    verifyCaptcha() {
      e.preventDefault();
      grecaptcha.ready(() => {
        grecaptcha
        // Don't forget about putting your unique SECRET_SITE_KEY.
          .execute("SECRET_SITE_KEY", {
            action: "submit"
          })
          .then(token => {
            // Try loggin the token first time to see if it got generated.
            console.log(token);
            // Add your logic to submit to your backend server here.
            // Here we will send the token to our backend using axios (install and import it to the component).
            // You could also use a Vue built in $http
            const body = { token: token };
            axios
            // We are POSTing to a dedicated URL we have on backend (it can be any URL that will need the reCAPTCHA token)
              .post("http://localhost:8000/api/verify-captcha/", body)
              .then(response => {
                // THe is_human part is our custom parameter comming from our backend. You can call it other name or just return something completly different. This is 100% to developer how will the reCAPTCHA returned probability be used.
                if (response.data.is_human) {
                 // Your front-end logic fired-up on success.
                } else {
                 // Your front-end logic fired-up on error.
                }
              })
              .catch(error => {
                console.log("Error : ", error);
              })
              // Boolean that could e.g. stop page loader.
              .finally(() => (this.loading = false));
          });
      });
    }
  }
Enter fullscreen mode Exit fullscreen mode
  • Add the method to an element on your page (the method where you execute reCAPTCHA token generation - verifyCaptcha - in our case)
<b-button @click="verifyCaptcha">Submit</b-button>
Enter fullscreen mode Exit fullscreen mode

( Here I use Buefy library for VueJS but your button tag may be called different way or just )

VERIFY

Verification is happening on backend, since we want to use the assessment to decide what to do on that particular request. Should we let the request add e-mail address to DB or not etc., any kind of server-logic.

Let’s have a look at the quote from docs:

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.

What does it mean? It means that we can generate token on Front End > console.log it in developer tools > copy it and use in POSTMAN using Google’s endpoint ( or our backend but this makes sense only if axios or communication front-end==back-end doesn´t work for some reason ).

Let’s start to test the token generated on front-end. The URL for checking it is:

METHOD: POST
https://www.google.com/recaptcha/api/siteverify
Enter fullscreen mode Exit fullscreen mode

and we need to provide a following body:

{
  "response": "TOKEN_GENERATED_ON_FRONT_END_ACTION",
  "secret": "YOUR_SECRET_KEY"
}
Enter fullscreen mode Exit fullscreen mode

or we could put those values as encoded form:

postman-recaptcha-token-verify

This very request is the request you have to send from your backend to later use the score value in your logic.

I wanted to start with POSTMAN because it has very useful feature that let’s you export request in any language you want. I will export it in python´s requests:

postman-export-request

If you execute this script in Python e.g. in CMD / Bash you will get this:

{
  "success": true,
  "challenge_ts": "2020-06-13T22:58:18Z",
  "hostname": "localhost",
  "score": 0.9,
  "action": "submit"
}
Enter fullscreen mode Exit fullscreen mode

Let´s jump into Django now.

PROCESS & RETURN

I will show you that in one code snippet. For learning purposes everything shown here is as simple as possible. In real life you would have a process function somewhere in a separate file and custom return afterwards.

As I mentioned, verifying will be done in a custom, dedicated endpoint but you can do it in any of your endpoints (as long as you pass the token there)

# urls.py

urlpatterns = [
    # ...
    path('api/verify-captcha/', VerifyCaptcha.as_view(), name='verify-captcha'),
]

# api.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

import json
import requests

YOUR_SECRET_KEY = "YOUR_SECRET_KEY"
GOOGLE_URL = 'https://www.google.com/recaptcha/api/siteverify'

class VerifyCaptcha(APIView):
    """Calls Google endpoint to verify front-end TOKEN and returns information whether user is a human.
    """
    # ...

        # This is POST endpoint that we were calling with axios in the first front-end example.
    def post(self, request, *args, **kwargs):
        # Decoding the payload
        request = json.loads(request.body.decode('utf-8'))
        # Taking out the token from the payload
        TOKEN_GENERATED_ON_FRONT_END_ACTION = request['token']
        # Creating body to send to Google for verification. You could also pass user's IP as an optional parameter but I never do that.
        body = {
            "secret": YOUR_SECRET_KEY,
            "response": TOKEN_GENERATED_ON_FRONT_END_ACTION
        }
        # Sending the request
        r = requests.post(GOOGLE_URL, data=body, json=body,)
        # Receiving the response
        google_response = r.json()

        # Analyzing the response
        if google_response['success'] == True:
            # Preparing our response that will be send to our front-end
            response = {"is_human": True}

            # This is our custom logic in case the request was initiated by a bot.
            if google_response['score'] < 0.5:
                response['is_human'] = False
            return Response(
                data=response, status=status.HTTP_200_OK, content_type="application/json")
        else:
            return Response(
                data={"is_human": None, "message": "Validation of FE token went wront."}, status=status.HTTP_404_NOT_FOUND, content_type="application/json")
Enter fullscreen mode Exit fullscreen mode

In this case official docs give you all the info on possible responses from Google

You did it! đź‘Ź

Now you can avoid bots 🤖



Did you make any mistakes when using reCAPTCHA or you’ve seen one here? Tell me about your insights. Leave a comment with YOUR opinion.

Top comments (0)