Integrating ReCaptcha With Django-AllAuth

Background

Django-allauth is a popular user authentication add-on for Django. Its rich set of features and functionality provides most of the common end-user initiated tasks such as registration and subsequent account management. However with the modern internet not being all rainbows and unicorns, additional layers of security are necessary to safeguard your systems from abuse.

Automated bots creating fake accounts are a persistent problem targeting any user self-registration systems and django-allauth is no exception. The most common approach to discourage these bots is to incorporate a captcha on your signup page that adds a layer of validation, with Google's ReCaptcha being the industry-standard implementation. Integrating recaptcha into django-allauth can be tricky so in this post I'll cover the steps to add a captcha to the django-auth signup page.

Software Versions

Main software and versions used:

Obtain Google ReCaptcha Keys

The first step is to register your site in Google's ReCaptcha service. You'll have the option to register for either V2 or V3 of recaptcha. V2 is the familiar 'select all traffic signals' type of challenges that we've all seen before, V3 is Google's newer style that doesn't interact directly with the user and instead operates in the background and provides a confidence score that you can use to filter out likely bots. For this post we will be using V2 with the 'I am not a robot' option.

Google ReCaptcha Add Site

Install django-recaptcha

Assuming you already have django-allauth installed, you will need to install django-recaptcha using you're preferred method (pip or in your IDE).

Code Changes

settings.py
We add the recaptcha keys provided when registering the site to the app's settings.py file. Additionally we redirect the signup form class to a custom one that will include an addition field of type ReCaptchaField.

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.MyCustomSignupForm'
RECAPTCHA_PUBLIC_KEY = '<your recaptcha site key>'
RECAPTCHA_PRIVATE_KEY =  '<your recaptcha secret ke>'

forms.py
And here is that custom form, the widget and related attrs parameter are the default values, but they've been added here in case you'd like to change them.

from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV2Checkbox

...

class MyCustomSignupForm(forms.Form):

    captcha = ReCaptchaField(
            widget=ReCaptchaV2Checkbox(
                attrs={
                        'data-theme': 'light',  # default=light
                        'data-size': 'normal',  # default=normal
                },
            ),
    )

    def signup(self, request, user):
        """ This function is required otherwise you will get an ImproperlyConfigured exception """
        pass

Optional: Cleaning Up Form Display

Using the above method as-is will result in the captcha field showing up in the middle of the form which doesn't look right. One way around this is to override the default django-allauth signup page and then list each field in the desired order. Below are the changes that need to be made to implement this.

yourapp/account_signup.html
This is the form that will override the default allauth signup template. Note: I've only included the form section here, it is assumed this file would have your normal template structure.

...

<form class="signup" id="signup_form" method="post" action="/accounts/signup/">

    {% csrf_token %}

    <div class="form-group">
            {{ form.email.errors }}
            {{ form.email }}
    </div>
    <div class="form-group">
            {{ form.username.errors }}
            {{ form.username }}
    </div>
    <div class="form-group">
            {{ form.password1.errors }}
            {{ form.password1 }}
    </div>
    <div class="form-group">
            {{ form.password2.errors }}
            {{ form.password2 }}
    </div>
    <div class="form-group">
            {{ form.captcha.errors }}
            {{ form.captcha }}
    </div>

    <button type="submit">Sign Up</button>

</form>

...

views.py
Below is a small class-based view to repoint the template to use for signups. It should be noted that you can also override default django-allauth pages by putting them in a particular directory structure under your project. However I prefer to make this explicit so as to not trip up someone reading it down the road who is unaware or forgets this mechanism (myself included!)

from allauth.account.views import SignupView

...

class MySignupView(SignupView):
    template_name = 'yourapp/account_signup.html'

urls.py
Finally point the /accounts/signup to our custom view and template. The key thing to remember here is that this needs to be added above where allauth urls are included.

url(r'^accounts/signup/', v.MySignupView.as_view(), name='account_signup'),

Final Thoughts

This example covered adding a V2 recaptcha to the django-allauth signup page to hamper the ability of bots to create fake users in your system. Using it as a reference you should be able to add the captcha to other pages with relative ease as you see fit. Keep in mind changing to use a V3 recaptcha or using other V2 modes will require adjustments to the instructions above. Good luck and happy coding!