Giter Club home page Giter Club logo

django-recaptcha3's Introduction

Django reCaptcha v3 Build Status


This integration app implements a recaptcha field for Google reCaptcha v3.

Warning: this package is not compatible with django-recaptcha2


How to install

Install the required package from pip (or take the source and install it by yourself):

pip install django-recaptcha3

Then add django-recaptcha3 to your installed apps:

INSTALLED_APPS = (
    ...
    'snowpenguin.django.recaptcha3',
    ...
)

And add your reCaptcha private and public key to your django settings.py and the default action name, recaptcha score threshold:

RECAPTCHA_PRIVATE_KEY = 'your private key'
RECAPTCHA_PUBLIC_KEY = 'your public key'
RECAPTCHA_DEFAULT_ACTION = 'generic'
RECAPTCHA_SCORE_THRESHOLD = 0.5
RECAPTCHA_LANGUAGE = 'en' # for auto detection language, remove this from your settings
# If you require reCaptcha to be loaded from somewhere other than https://google.com
# (e.g. to bypass firewall restrictions), you can specify what proxy to use.
# RECAPTCHA_FRONTEND_PROXY_HOST = 'https://recaptcha.net'

If you have to create the apikey for the domains managed by your django project, you can visit this website.

Usage

Form and Widget

You can simply create a reCaptcha enabled form with the field provided by this app:

from snowpenguin.django.recaptcha3.fields import ReCaptchaField

class ExampleForm(forms.Form):
    [...]
    captcha = ReCaptchaField()
    [...]

Form validation of the ReCaptchaField causes us to verify the token returned from the client against the ReCaptcha servers and populates a dictionary containing the score, action, hostname, and challenge_ts fields as the form fields cleaned_data:

    def formview(request):
        if request.method == "POST":
            form = ExampleForm(request.POST)
            if form.is_valid():
              captcha_score = form.cleaned_data['captcha'].get('score')

If a communication problem occurs, the token supplied by the client is invalid or has expired then a ValidationError is raised.

Automatic Enforcement

If you want low scores to cause a ValidationError, pass an appropriate score_threshold to the ReCaptchaField, or set the configuration variable settings.RECAPTCHA_SCORE_THRESHOLD.

The default value for the threshold is 0.0, which allows all successful capture responses through for you to later check the value of score.

from snowpenguin.django.recaptcha3.fields import ReCaptchaField

class ExampleForm(forms.Form):
    [...]
    captcha = ReCaptchaField(score_threshold=0.5)
    [...]

You can also set the private key on the "private_key" argument of the ReCaptchaField contructor if you want to override the one inside your configuration.

Templating

You can use some template tags to simplify the reCaptcha adoption:

  • recaptcha_init: add the script tag for reCaptcha api. You have to put this tag somewhere in your "head" element
  • recaptcha_ready: call the execute function when the api script is loaded
  • recaptcha_execute: start the reCaptcha check and set the token from the api in your django forms. Token is valid for 120s, after this time it is automatically regenerated.
  • recaptcha_key: if you want to use reCaptcha manually in your template, you will need the sitekey (a.k.a. public api key). This tag returns a string with the configured public key.

You can use the form as usual.

Samples

Simple

Just create a form with the reCaptcha field and follow this template example:

{% load recaptcha3 %}
<html>
  <head>
      {% recaptcha_init %}
      {% recaptcha_ready action_name='homepage' %}
  </head>
  <body>
    <form action="?" method="POST">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

Custom callback

The callback can be used to allow to use the token received from the api in ajax calls or whatever

{% load recaptcha3 %}
<html>
  <head>
      <script>
          function alertToken(token) {
              alert(token);
          }
      </script>
      {% recaptcha_init %}
      {% recaptcha_ready action_name='homepage' custom_callback='alertToken' %}
  </head>
  <body>
    <form action="?" method="POST">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

Multiple render example

You can render multiple reCaptcha without any extra effort:

{% load recaptcha3 %}
<html>
  <head>
      {% recaptcha_init %}
      {% recaptcha_ready action_name='homepage' %}
  </head>
  <body>
    <form action="?" method="POST">
      {% csrf_token %}
      {{ form1 }}
      <input type="submit" value="Submit">
    </form>
    <form action="?" method="POST">
      {% csrf_token %}
      {{ form2 }}
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

Bare metal!

You can use the plain javascript, just remember to set the correct value for the hidden field in the form

<html>
  <head>
      <script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key"></script>
      <script>
        grecaptcha.ready(function() {
          var grecaptcha_execute = function(){
            grecaptcha.execute('reCAPTCHA_site_key', {action: 'homepage'}).then(function(token) {
              document.querySelectorAll('input.django-recaptcha-hidden-field').forEach(function (value) {
                  value.value = token;
              });
              return token;
            })
          };
          grecaptcha_execute()
          setInterval(grecaptcha_execute, 120000);
        });
      </script>
  </head>
  <body>
    <form action="?" method="POST">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

Testing

Test unit support

You can disable recaptcha field validation in unit tests by setting the RECAPTCHA_DISABLE env variable. This will skip the external call to Recaptca servers, returning a valid field with no data.

os.environ['RECAPTCHA_DISABLE'] = 'True'

You can use any word in place of "True", the clean function will check only if the variable exists.

If you set RECAPTCHA_DISABLE to be valid json, it will be interpreted as a mock captcha server response allowing you to mock score/hostname/action as required:

os.environ['RECAPTCHA_DISABLE'] = json.dumps({'score': 0.4, 'hostname': 'localhost', 'action': 'homepage'})

Test unit with recaptcha3 disabled

import os
import unittest

from yourpackage.forms import MyForm

class TestCase(unittest.TestCase):
    def setUp(self):
        os.environ['RECAPTCHA_DISABLE'] = 'True'

    def test_myform(self):
        form = MyForm({
            'field1': 'field1_value'
        })
        self.assertTrue(form.is_valid())

    def tearDown(self):
        del os.environ['RECAPTCHA_DISABLE']

django-recaptcha3's People

Contributors

kbytesys avatar marekomilo avatar mavenium avatar mtb-beta avatar sharpek avatar shuckc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-recaptcha3's Issues

Don't require RECAPTCHA_PRIVATE_KEY to be set if RECAPTCHA_DISABLE is set

Currently, RECAPTCHA_PRIVATE_KEY has to be defined, even when RECAPTCHA_DISABLE is set, otherwise this stack trace happens:

  File "/home/vagrant/env/lib/python2.7/site-packages/snowpenguin/django/recaptcha3/fields.py", line 18, in __init__
    self._private_key = kwargs.pop('private_key', settings.RECAPTCHA_PRIVATE_KEY)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/conf/__init__.py", line 57, in __getattr__
    val = getattr(self._wrapped, name)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/conf/__init__.py", line 172, in __getattr__
    return getattr(self.default_settings, name)
AttributeError: 'Settings' object has no attribute 'RECAPTCHA_PRIVATE_KEY'
--------------------- >> end captured logging << ---------------------

This applies to RECAPTCHA_PUBLIC_KEY, but a different stack trace:

  File "/home/vagrant/env/lib/python2.7/site-packages/django/shortcuts.py", line 30, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/loader.py", line 68, in render_to_string
    return template.render(context, request)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/backends/django.py", line 66, in render
    return self.template.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 207, in render
    return self._render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/test/utils.py", line 107, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/loader_tags.py", line 177, in render
    return compiled_parent._render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/test/utils.py", line 107, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/loader_tags.py", line 72, in render
    result = block.nodelist.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/template/library.py", line 225, in render
    _dict = self.func(*resolved_args, **resolved_kwargs)
  File "/home/vagrant/env/lib/python2.7/site-packages/snowpenguin/django/recaptcha3/templatetags/recaptcha3.py", line 14, in recaptcha_init
    return {'public_key': public_key or settings.RECAPTCHA_PUBLIC_KEY}
  File "/home/vagrant/env/lib/python2.7/site-packages/django/conf/__init__.py", line 57, in __getattr__
    val = getattr(self._wrapped, name)
  File "/home/vagrant/env/lib/python2.7/site-packages/django/conf/__init__.py", line 172, in __getattr__
    return getattr(self.default_settings, name)
AttributeError: 'Settings' object has no attribute 'RECAPTCHA_PUBLIC_KEY'

ReCaptchaField doesn't fail validation when there is a robot

Hi,
Thanks a lot for this awesome package!
I noticed a small problem while I was using it (I'm not sure I'm using it correctly).

I was expecting the captcha field to fail during the form clean() when a robot filled the form. But it seems it doesn't. After looking into the source code of fields.py, we are only looking to the 'success' part of the google response:

if bool(json_response['success']):
return values[0]

But on the google/recaptcha3 documentation: https://developers.google.com/recaptcha/docs/v3
It says -> "success": true|false, // whether this request was a valid reCAPTCHA token for your site
So we are only telling if the token is valid, but not if a bot is around. I was not expecting that.
I think it would be better to change:

if bool(json_response['success']):
return json_response

And let the user define an error on the returned score value in a django clean() method.
Or am I missing something?

Support for Django4.0

ugettext_lazy (used on line 7 of recaptcha3/fields.py) is deprecated since Django3.0 and removed in Django4.0. So django-recaptcha3 is unusable after upgrading to Django4.0

The only change required to fix this is replacing ugettext_lazy with gettext_lazy in that one place.

Just a typo

Hi on your main page/Readme it says

from snowpenguin.django.recaptcha2.fields import ReCaptchaField

when it should say
from snowpenguin.django.recaptcha3.fields import ReCaptchaField

otherwise this seems to work nicely so thanks.

On another matter : Any idea how to force robot detection ?

No support for django < 1.11

In django < 1.11 Input widget has not attribute "template_name".
Input rendered as <input id="id_captcha" name="captcha" type="hidden">

Release latest on PYPI

Hi - would it be possible for you to update the release on PYPI to match what's on Github?

I could really use the error code/score functionality :-)

Thanks,

Russell.

Connection to reCaptcha server failed

image
I tested reCAPTCHA V3 and reCAPTCHA V2 according to the readme file, both of which can be used normally on the browser, but the form can't pass is_valid() . At the same time, I have also tested with and without proxy. I'm not sure if the problem is network or configuration.

ModuleNotFoundError on Linux System

Getting this error while coverage run and deployment on my linux-server:

App 3430 output: django.template.exceptions.TemplateSyntaxError: 'recaptcha3' is not a registered tag library. Must be one of: App 14170 output: ModuleNotFoundError: No module named 'snowpenguin'

Is there a other way to install that app in the settings?

I did like in the ReadMe in my settings.py:

'snowpenguin.django.recaptcha3',

Allow option to raise SuspiciousOperation or PermissionDenied on low score

IMHO I don't think telling a bad actor their reCAPTCHA score is a security best practice, as happens now in fields.py. A configurable option to optionally throw SuspiciousOperation and/or PermissionDenied instead, with a customizable message, would be preferable in a lot of use cases, and definitely not inform the bad actor of their score, unless say DEBUG = True.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.