Overview

Google’s reCAPTCHA is one of the tool we can use to stop malicious internet bots from abusing our web applications.


It comes in two versions, reCAPTCHA v2 and v3. Version 3 uses a score based and no-interaction approach to handle bots from humans. Version 2 uses use a checkbox that will require users to answer a question. In this tutorial we will focus on reCAPTCHA v3.

Prerequisite

This tutorial requires:

  • You are already a registered google webmaster
  • Added your website property into your Google webmaster account
  • You know how to build a basic web app using the language Go

Registration Steps

1. Let’s begin by heading to https://www.google.com/recaptcha/ to register our site with reCAPTCHA.

2. Add a site for reCAPTCHA

3. Register site details

If you want to configure and test using your localhost just add localhost in the domain list

3.1 Generated Site Keys
Once you’ve successfully registered your website with reCAPTCHA. It will generate client and server side keys. We will use this in our codes.

Okay now we are ready to create our client- and server-side verification calls we need for the reCAPTCHA-process.

Client:

I am using axios for creating the call once to google and to our backend-service.

  1. Implement the reCaptcha api from Google into your page:
<script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key"></script>

2. Create the function for the initial call to Google to get the client-token.

<script>
  function onClick(e) {
    e.preventDefault();
    grecaptcha.ready(function() {
      grecaptcha.execute('reCAPTCHA_site_key', {action: 'submit'}).then(function(token) {
          // Add your logic to submit to your backend server here.
      });
    });
  }
</script>

After executing the initial call to Google we are receiving the token for the next call we need to do. But first we need to combine the given token from the response with our CaptchaSecret. Of course this should not happen in the frontend. Everybody could read our secret - would make no sense to call it secret and using it in the frontend... okay, we need a backend-service wich will combine the token with our secret and will build another post to the Google reCaptcha Service.

Example: logic to submit the token to your backend service:

api.post({
      url: 'http://backendserver.hostname/verification',
      params: {
        Response: token,
      },
    }).then(resp => {
      if (resp.data.success === true) {
        return true;
      }
      debug.log(resp.data['error-codes']);
      return false;
    }).catch(err => {
      debug.log(err);
      return false;
    });
  };

more details on: https://developers.google.com/recaptcha/docs/v3

Backend:

Don't forget to replace the placeholder with your Google Captcha Secret.

package google

import (
	"encoding/json"
	"frontend-service/internal/api/utility"
	"frontend-service/internal/models"
	"github.com/labstack/echo/v4"
	"io/ioutil"
	"net/http"
)

// @Summary Google Captcha Verification
// @Description post to google recaptcha API
// @Tags recaptcha
// @Accept application/form-data
// @Produce json
// @Param token body string true "token generated/loaded by google js frontend"
// @Param X-CSRF-Token header string true "Token from your init session"
// @Success 200
// @Router /verification [post]
func CheckGoogleCaptcha(c echo.Context) error {

	googleCaptchaSecret := "YOUR_GOOGLE_CAPTCHA_SECRET"
	url := "https://www.google.com/recaptcha/api/siteverify"

	request := new(models.GoogleVerificationRequest)
	if err := c.Bind(request); err != nil {
		return err
	}

	client := &http.Client{}
	req, err := http.NewRequest("POST", url, nil)
	q := req.URL.Query()
	q.Add("secret", googleCaptchaSecret)
	q.Add("response", request.Response)
	req.URL.RawQuery = q.Encode()

	resp, err := client.Do(req)
	if err != nil {
		return c.JSON(http.StatusBadRequest, utility.NewError(err.Error()))
	}
	defer resp.Body.Close()

	// unmarshall the response into a GoogleResponse
	var googleResponse models.GoogleCaptcha
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return c.JSON(http.StatusBadRequest, utility.NewError(err.Error()))
	}
	err = json.Unmarshal(body, &googleResponse)
	if err != nil {
		return c.JSON(http.StatusBadRequest, utility.NewError(err.Error()))
	}

	return c.JSON(http.StatusOK, googleResponse)
}

That's it :)

If you wanna start with Go and need a basic skeleton, check out following repository:

devjalo/golang-web-app
Web app skeleton in Go. Contribute to devjalo/golang-web-app development by creating an account on GitHub.