8

I have been trying to use the Google reCAPTCHA on a website that I've been making. The captcha loads on the webpage but I've been unable to validate it using several methods. I've tried the recaptcha validation using the method given at How to use Python plugin reCaptcha client for validation? but I think it's outdated as it no longer works and it is referring to challenges whereas the one I'm trying to use is the new 'checkbox' reCAPTCHA v2 by Google or maybe I need to make changes in my settings after installing recaptcha-client or django-recaptcha.

Please help!

Community
  • 1
  • 1
Keshav Agarwal
  • 811
  • 1
  • 10
  • 28

6 Answers6

27

Here is a simple example to verify Google reCAPTCHA v2 within Django view using requests library (http://docs.python-requests.org/en/latest/):

import requests
from django.conf import settings

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def grecaptcha_verify(request):
    if request.method == 'POST':
        response = {}
        data = request.POST
        captcha_rs = data.get('g-recaptcha-response')
        url = "https://www.google.com/recaptcha/api/siteverify"
        params = {
            'secret': settings.RECAPTCHA_SECRET_KEY,
            'response': captcha_rs,
            'remoteip': get_client_ip(request)
        }
        verify_rs = requests.get(url, params=params, verify=True)
        verify_rs = verify_rs.json()
        response["status"] = verify_rs.get("success", False)
        response['message'] = verify_rs.get('error-codes', None) or "Unspecified error."
        return HttpResponse(response)
isherwood
  • 58,414
  • 16
  • 114
  • 157
Toan Nguyen
  • 631
  • 4
  • 10
10

There is a third-party Django app to implement the new reCAPTCHA v2 here:

https://github.com/ImaginaryLandscape/django-nocaptcha-recaptcha

After installing it, add the following lines to the following files:

# settings.py
NORECAPTCHA_SITE_KEY = <the Google provided site_key>
NORECAPTCHA_SECRET_KEY = <the Google provided secret_key>

INSTALLED_APPS = (
    ....
    'nocaptcha_recaptcha'
)


#forms.py
from nocaptcha_recaptcha.fields import NoReCaptchaField

class YourForm(forms.Form):
    .....
    captcha = NoReCaptchaField()


# In your template, add the following script tag:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
Christian Abbott
  • 6,497
  • 2
  • 21
  • 26
  • I tried this. The field is not rendering in my html template. I'm using a custom form which serves data to my django form and I've used a separate form to use the django `{{ from.as_p }}`, but it doesn't render. Although, the form has stopped taking registrations so I know that it's working and just rendering is left. – Keshav Agarwal Apr 10 '15 at 06:07
  • 1
    In that case, I'd suggest asking a new, specific question related to your problem. Your current question asks how you set up reCAPTCHA v2 with Django (adding that you've tried reCAPTCHA apps that were designed for reCAPTCHA v1), and this answers that question. You should post a separate question listing your specific issue, your code, and what troubleshooting you've done. You can also, if you like, mark this answer as correct for the existing question for the benefit of others who find it. – Christian Abbott Apr 10 '15 at 06:32
9

Google has changed the API around, we need to use a POST request now. Here a re-usable solution in case you need to do the validation in more than one django view:

utils.py

# django imports
from django.conf import settings
from django.views.generic.base import View
from django.http import HttpResponseForbidden

# 3rd-party imports
import requests
from ipware import get_client_ip


def is_recaptcha_valid(request):
    """
    Verify if the response for the Google recaptcha is valid.
    """
    return requests.post(
        settings.GOOGLE_VERIFY_RECAPTCHA_URL,
        data={
            'secret': settings.RECAPTCHA_SECRET_KEY,
            'response': request.POST.get('g-recaptcha-response'),
            'remoteip': get_client_ip(request)
        },
        verify=True
    ).json().get("success", False)



def human_required(view_func):
    """
    This decorator is aimed to verify Google recaptcha in the backend side.
    """
    def wrapped(request, *args, **kwargs):
        if is_recaptcha_valid(request):
            return view_func(request, *args, **kwargs)
        else:
            return HttpResponseForbidden()
    return wrapped

then:

views.py

 from utils import human_required

 class MyView(View):

     @human_required
     def post(request, *args, **args):
        pass

Note we are using django-ipware in this solution to get the ip address, but this is up to you. Also, don't forget to add GOOGLE_VERIFY_RECAPTCHA_URL and RECAPTCHA_SECRET_KEY to the django settings file!

trinchet
  • 6,753
  • 4
  • 37
  • 60
2

views.py

def login(request):
    if request.method == 'POST':
    username = request.POST['username']
    password = request.POST['password']
    user = auth.authenticate(request, username=username, password=password)

     if user is not None:
        if user.is_active:
            auth.login(request, user)
            ''' Begin reCAPTCHA validation '''
            recaptcha_response = request.POST.get('g-recaptcha-response')
            url = 'https://www.google.com/recaptcha/api/siteverify'
            values = {
            'secret' : settings.GOOGLE_RECAPTCHA_SECRET_KEY,
            'response' :  recaptcha_response
            }
            data = urllib.parse.urlencode(values).encode("utf-8")
            req = urllib2.Request(url, data)
            response = urllib2.urlopen(req)
            result = json.load(response)
            ''' End reCAPTCHA validation '''

            if result['success']:
              return redirect('index')
            else:
              messages.error(request, 'Invalid reCAPTCHA. Please try again.')
              return redirect('login')
    else:
        messages.info(request, 'Wrong Credentials!!! enter right username or password')
        return redirect('login')
else:
    return render(request, 'login.html')

login.html

<form action="{% url 'login' %}" method="post">
            {% csrf_token %}
            <div class="body bg-gray">
                <div class="form-group">
                    <input type="text" name="username" class="form-control" placeholder="Username"/>
                </div>
                <div class="form-group">
                    <input type="password" name="password" class="form-control" placeholder="Password"/>
                </div>          
                <div class="form-group">
                    <input type="checkbox" name="remember_me"/> Remember me
                </div>
            </div>
            <div class="footer">                                                               
                 <button type="submit" class="btn bg-olive btn-block">Sign me in</button>

                <p><a href="#">I forgot my password</a></p>

                <a href="{% url 'register' %}" class="text-center">Register a new membership</a>
            </div>
            <br><br>
            <script src='https://www.google.com/recaptcha/api.js'></script>
            <div class="g-recaptcha" data-sitekey="(enter your key here that is private or authenticated on google recapthcha)"></div>
        </form>

settings.py

INSTALLED_APPS = [
   ....
   ....
   'captcha'
               ]

GOOGLE_RECAPTCHA_SECRET_KEY ='6LdXBLAUAMlGYqqyDESeHKI7-'
RECAPTCHA_PUBLIC_KEY = '6LdXBLAUAAAAAP3oI1VPJgA-VHXoj'
RECAPTCHA_PRIVATE_KEY = '6LdXBLAUAAAAAGYqqyDESeHKI7-'

''' you have to register your domain to get the access of these keys. or you can 
register your localhost also to test this after uploading on the server you can 
register with real domain and change the keys.

don't forget to like if you find it helpful.'''

 'https://www.google.com/recaptcha/intro/v3.html' -> 'admin console' where you can 
 register your domain or localhost and get your key. 
Mohit Verma
  • 53
  • 1
  • 1
  • 10
  • 1
    Welcome to SO! Thank you for providing such a great answer. It would be great if you could include some additional comments regarding what you did to achieve the desired results. – technogeek1995 Jul 29 '19 at 13:13
1

Expanding on the answer given by @trinchet, here is a simple modification of the FormView Django class to automatically handle Google's reCAPTCHA v2.

class RecaptchaFormView(FormView):
    """ This class handles Google's reCAPTCHA v2. """
    recaptcha_ok = None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['grecaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
        return context

    def get_form(self):
        form = super().get_form()
        if self.recaptcha_ok == False:
            form.add_error(None, "Invalid reCAPTCHA, please try again.")
        return form

    def post(self, request, *args, **kwargs):
        self.recaptcha_ok = is_recaptcha_valid(request)
        return super().post(self, request, *args, **kwargs)

Don't forget to include the is_recaptcha_valid function provided by @trinchet (see his answer), reCAPTCHA keys in settings.py and the reCAPTCHA code in the template (use {{ grecaptcha_site_key }} as the site key).

Lazward
  • 205
  • 2
  • 8
0

This is how I handle the proposed question:

views.py

from django.contrib.auth.views import LoginView, LogoutView
from django.conf import settings

from authentication.forms import MyAuthenticationForm


class MyLoginView(LoginView):
   template_name = 'authentication/login.html'
   form_class = MyAuthenticationForm

   def get_client_ip(self):
       x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
       if x_forwarded_for:
           ip = x_forwarded_for.split(',')[0]
       else:
           ip = self.request.META.get('REMOTE_ADDR')
       return ip

   def get_form_kwargs(self):
       kwargs = super(MyLoginView, self).get_form_kwargs()
       if self.request.method in 'POST':
           kwargs['g-recaptcha-response'] = self.request.POST.get('g-recaptcha-response')
           kwargs['remote_ip'] = self.get_client_ip()
       return kwargs

   def get_context_data(self, **kwargs):
       context = super(MyLoginView, self).get_context_data(**kwargs)
       # To use in the template
       context['recaptcha_challenge_secret'] = settings.G_RECAPTCHA_CHALLENGE_SECRET
       return context

forms.py

import requests

from django.contrib.auth.forms import AuthenticationForm
from django.conf import settings
from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _


class MyAuthenticationForm(AuthenticationForm):
    def __init__(self, *args, **kwargs):
        self.g_recaptcha_response = kwargs.pop('g-recaptcha-response', None)
        self.remote_ip = kwargs.pop('remote_ip', None)
        super(MyAuthenticationForm, self).__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super(MyAuthenticationForm, self).clean()
        self.verify_captcha()
        return cleaned_data

    def verify_captcha(self):
        if self.g_recaptcha_response:
            data = {
                'secret': settings.G_RECAPTCHA_VERIFY_SECRET,
                'response': self.g_recaptcha_response,
                'remoteip': self.remote_ip
            }

            response = requests.post(settings.G_RECAPTCHA_VERIFICATION_URL, data=data)
            result = response.json()

            if result['success']:
                return

        raise ValidationError(
            _('Invalid reCAPTCHA challenge.'),
            code='invalid_recaptcha_challenge'
        )
Iman Kermani
  • 919
  • 9
  • 14