[Guide] Adding Recaptcha V3 to Frappe Web Page Forms

Quick draft for someone who might find this useful

Form

I am submitting my form without reloading the page so cannot use the submit event. Instead, my form has a Save button

<div id='captcha_container'></div>

<form method='post'>
    <input placeholder="First Name" type="text" id="first_name" name="first_name" autocomplete="given-name">
    <button id='save'>Save</button>
</form>

Scripts

1. Add to Website Script doctype

   frappe.require("https://www.google.com/recaptcha/api.js")

2. Web Page Script

// add save button attributes which wouldn't work when using the page builder 
// but work when using pure HTML

$(document).ready( ()=>{
    $('#save').attr({
        'class': 'g-recaptcha btn',
        'data-sitekey':"RECAPTCHA_SITE_KEY", 
        'data-callback': 'onClick',
        'data-action': 'click'
    });
});

function onClick(token) {
    
    // all mandatory
    if(! args.first_name) {
       msgprint("All fields are necessary", "Hang on buddy...");
       return;
    }
    
    //verify captcha and perform your desired action
    frappe.call({
        "method": "my_app.api.verify_captcha",
        "args": {
            'token': token
        },
        freeze: true,
        callback: (r) => {
            let verified = r.message; //result is score 0.1 - 1 or false
            
            if (verified){
                if (parseFloat(verified) > 0.3){
                    // finally, perform your desired action
                    // e.g submit your form with frappe.call() or similar
                }
                else {
                    msgprint("Looks like you're a robot 🤖", "Wait a minute...");
                }
            }
            else {
                msgprint("Something's wrong with robot checker 🤖, the developers will be notified", "Uh oh...");
            }
            
        }
    });
    
}

Python

"""file location: <frappe-bench>/apps/my_app/my_app/api.py"""


from urllib.parse import urlencode
from urllib.request import urlopen
import json


@frappe.whitelist(allow_guest=True)
def verify_captcha(token):
    URL = 'https://www.google.com/recaptcha/api/siteverify'
    private_key = 'RECAPTCHA_SERVER_KEY'
    params = urlencode({
        'secret': private_key,
        'response': token,
    })

    data = urlopen(URL, params.encode('utf-8')).read()
    result = json.loads(data)
    success = result.get('success', None)

    # frappe.errprint( float(result.get('score', 1)) )

    if success == True:
        # return the score if verification was successful
        return float( result.get('score', 1) ) 
    else:
        frappe.errprint( 'reCaptcha failed ' + str(result) )
        return False

Possible improvements

  • Keys can be saved in a frappe doctype
  • I’m running two calls to the server, first to verify the token, and a second to submit my form. One call to the server can handle both

NOTE: This is working perfectly but still not giving me data on the google analytics UI, its a known issue but I’ve given up trying

UPDATE: Report data are streaming in after a few days. The first two charts at least.

6 Likes

@adam26d Thanks for sharing. It’s for Web page. is it possible to add for web form?

I added a script in website script. Attached a image for the reference.

frappe.require("https://www.google.com/recaptcha/api.js")
In web page am getting the error

Uncaught DOMException: Failed to read a named property 'FencedFrameElement' from 'Window': Blocked a frame with origin "https://www.google.com" from accessing a cross-origin frame.

So I added a “allow_cors”:“https://google.com” in common_site_config.json but still getting the same error.