3

I want to implement Google Recaptcha v3 into a working Django contact form.

I tried following these tutorials:

https://simpleisbetterthancomplex.com/tutorial/2017/02/21/how-to-add-recaptcha-to-django-site.html

https://foxrow.com/using-recaptcha-v3-with-django

https://medium.com/@mihfazhillah/how-to-implement-google-recaptcha-v3-on-your-django-app-3e4cc5b65013

But have had no luck trying to figure it out. Can someone please help me out?

Settings.py:

GOOGLE_RECAPTCHA_SECRET_KEY = '<KEY>'

Html:

<script>
    grecaptcha.ready(function() {
        grecaptcha.execute('<SITE_KEY>', {action: 'contact'})
        .then(function(token) {
            document.getElementById("form").appendChild(document.CreateElement(`<input type="hidden" name="g-recaptcha-response" value=${token}`);
        });
    });
</script>

<form role="form" action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <script src='https://www.google.com/recaptcha/api.js?render=<SITE_KEY>'></script>
    <div class="g-recaptcha" data-sitekey="<SITE_KEY>"></div>

    <button type="submit">Submit</button>
</form>

Views.py:

def contact(request):
    form_class = ContactForm

    # new logic!
    if request.method == 'POST':
        form = form_class(data=request.POST)

        if form.is_valid():

            contact_name = request.POST.get(
                'contact_name'
            , '')
            contact_email = request.POST.get(
                'contact_email'
            , '')
            form_content = request.POST.get('content', '')

            # Email the profile with the
            # contact information
            template = get_template('contact_template.txt')
            context = {
                'contact_name': contact_name,
                'contact_email': contact_email,
                'form_content': form_content,
            }
            content = template.render(context)

            email = EmailMessage(
                "New contact form submission",
                content,
                "Your website" +'',
                ['GMAIL_ADDRESS'],
                headers = {'Reply-To': contact_email }
            )
            email.send()
            messages.info(request, "Success")

    return render(request, 'contact.html', {
        'form': form_class,
    })

Forms.py:

from django import forms

class ContactForm(forms.Form):
    contact_name = forms.CharField(required=True)
    contact_email = forms.EmailField(required=True)
    content = forms.CharField(
        required=True,
        widget=forms.Textarea
    )

    # the new bit we're adding
    def __init__(self, *args, **kwargs):
        super(ContactForm, self).__init__(*args, **kwargs)
        self.fields['contact_name'].label = "Name:"
        self.fields['contact_email'].label = "Email:"
        self.fields['content'].label = "Message:"
saltworts
  • 33
  • 3
  • What's the issue with your current code? Can you describe what your code is currently doing (error, unexpected behaviour, etc...)? – dirkgroten Oct 20 '19 at 20:07
  • @dirkgroten The current code is working where it sends an email when the form gets submitted. The "protected by reCAPTCHA" is displayed at the bottom right using html. Currently, no recaptcha logic is being used in the views.py so I need some guidance on that. When I tried following some of the tutorials I posted, I usually get "Invalid reCAPTCHA. Please try again" – saltworts Oct 20 '19 at 20:18
  • You're submitting the `g-recaptcha-response` in your POST request (the value is the token). You need to submit that to the Google API from within your view to verify that the token is valid and to receive the [score](https://developers.google.com/recaptcha/docs/v3#site_verify_response). Then once you have the score, you can decide how to proceed (e.g. score > 0.5 is ok to send the email, score < 0.5 you should consider asking the user some additional verification). [Here's the request](https://developers.google.com/recaptcha/docs/verify) you should submit. – dirkgroten Oct 20 '19 at 20:24
  • @dirkgroten Please see pastebin here of views.py with the updated views.py using a tutorial above -> https://pastebin.com/7BVVupR4 I get the following messages: Invalid reCAPTCHA. Please try again. Success – saltworts Oct 20 '19 at 20:31
  • show us the `result` (line 20), the error message is what's in your code, but it's not what Google sends back. It would help to see the actual response. – dirkgroten Oct 20 '19 at 20:36
  • I don't understand `or (not result['action'] == 'signup')`. Your action is `contact` so this will always fail. – dirkgroten Oct 20 '19 at 20:38
  • @dirkgroten I am getting this when printing result -> {'success': False, 'error-codes': ['invalid-input-response']}. I actually do not want the action to be anything. I changed action in both html and views.py to be '' – saltworts Oct 20 '19 at 22:46
  • And what is the value of `recaptcha_response` in your view? – dirkgroten Oct 21 '19 at 06:24
  • Your HTML form doesn’t have an `id` attribute so `getElementById` won’t work. You don’t see any js errors in your debug tools? – dirkgroten Oct 21 '19 at 06:25
  • @dirkgroten I am getting "None" for recaptcha_response. The only error I have in the console is "SyntaxError: missing ) after argument list". This is the code I am using -> https://pastebin.com/HkGXk13w – saltworts Oct 21 '19 at 18:53
  • Well, your line creating the extra hidden element misses a ), as the error says. Also it's `createElement` not `CreateElement`. – dirkgroten Oct 21 '19 at 19:18
  • @dirkgroten Now I am getting a different result -> {'success': True, 'challenge_ts': '2019-10-21T21:53:37Z', 'hostname': '127.0.0.1', 'score': 0.9} for result and a long recaptcha_response string. Now I am getting an error revolving around how the action in views.py cannot be blank. Is there a way to keep it blank? I prefer not to go to another URL when the form gets submitted and instead stay on the page. I am still getting "ReferenceError: grecaptcha is not defined" and "Invalid reCAPTCHA. Please try again." – saltworts Oct 21 '19 at 21:59
  • @dirkgroten I think I figured out the action being blank part (still not 100% sure if correct though). I only left the form action blank but the captcha javascript and views.py parts I put 'contact' for the action. For the "ReferenceError: grecaptcha is not defined" I tried this: https://stackoverflow.com/questions/29822607/uncaught-referenceerror-grecaptcha-is-not-defined. However, now I am getting "InvalidCharacterError: String contains an invalid character" and the Recaptcha admin page still shows total requests = 0. My code: https://pastebin.com/YdfY8gie – saltworts Oct 22 '19 at 01:49
  • I don't understand the code you're showing has the script loaded twice and two `recaptcha.ready()` functions. Remove the top part of your code (the ` – dirkgroten Oct 22 '19 at 07:04
  • Ok I think it worked I completely removed the top javascript and kept the form's javascript only. Now the Recaptcha admin is showing requests. But in the console I am getting: Content Security Policy: Ignoring “'unsafe-inline'” within script-src: ‘strict-dynamic’ specified Content Security Policy: Ignoring “https:” within script-src: ‘strict-dynamic’ specified Content Security Policy: Ignoring “http:” within script-src: ‘strict-dynamic’ specified – saltworts Oct 22 '19 at 14:28

1 Answers1

2

I encountered the same problem today. It seems the javascript snippet is incorrect:

  1. Your element needs an id. (Mine is "cform" now.)
  2. CreateElement does not exist, the "c" is lower case.
  3. I could not create an element with attributes, I had to do it in several steps.

I don't usually code in javascript so I don't know the best practices, but here is what worked for me:

<script src='https://www.google.com/recaptcha/api.js?render=<KEY>'></script>
<script>
    grecaptcha.ready(function() {
        grecaptcha.execute(<KEY>, {action: 'contact'})
        .then(function(token) {
            ginput = document.createElement('input');
            ginput.type = "hidden";
            ginput.name = "g-recaptcha-response";
            ginput.value = token;
            document.getElementById("cform").appendChild(ginput);
        });
    });
</script>
ppouchin
  • 21
  • 2
  • With your code I am getting "ReferenceError: grecaptcha is not defined" in the console. Unfortunately implementing your code did not work. Here is my code -> https://pastebin.com/BWNU1WHk – saltworts Oct 21 '19 at 18:59
  • You need to put this code **after** loading the recaptcha script from Google. – dirkgroten Oct 21 '19 at 19:21
  • Indeed. I added this line to my answer for clarity. – ppouchin Oct 22 '19 at 08:12
  • Yes I believe it did. I am receiving requests on the recaptcha admin page now and my form is not producing errors. The only thing though is the console is showing some weird stuff as I mentioned to dirkgroten above and would appreciate some help on that as well. Otherwise, I would like to thank you ppouchin and dirkgroten for all the help :). I will leave the question up so it can help others who have the same type of issue. – saltworts Oct 23 '19 at 17:55