DEV Community

Cover image for The complete django-allauth guide
Gajesh
Gajesh

Posted on

Django Allauth The complete django-allauth guide

User registration and authentication is one of the most essential components of a web application. This tutorial deals with setup, configuration, and customization of django-allauth along with advanced tweaks and social login setup for your django web app. This tutorial intends to serve as a guide for new users who want to get started quickly with django-allauth and make useful customizations along the way without much pain.

Why this guide?

django-allauth is a very well written library thanks to Raymond Penners. However, it can be overwhelming to a django novice or advanced users using django-allauthfor the first time. Although it is well documented, due to time and resource constraints of the developers involved, there has not been many articles and in-depth tutorials on the library. So this tutorial tries to solve that problem by making a comprehensive guide to get started quickly and also learn about advanced customization.

Basic Setup

You can download the files used in the tutorial to get a head start. The steps below guide you through the setup in detail.

  1. Create a Django project if you already don’t have one.

  2. Install django-allauth using the command pip install django-allauth

Add,allauthallauth.account, allauth.socialaccount and all the social login features you need to INSTALLED_APPS section in settings.py.

You can view the entire list of supported API's here.

The social login feature is described later in the post. You can scroll down to that subheading if you would like to just read that. After you configure your installed apps section, it should be similar to the code given below.



INSTALLED_APPS = [
    'django.contrib.admin',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.facebook',
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]


Enter fullscreen mode Exit fullscreen mode

Configure the template context processor settings in settings.py and also add the url pattern in the project’s urls.py.



TEMPLATES = [
  {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.normpath(os.path.join(BASE_DIR, 'templates')),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.request',
            ],
        },
    },
]


Enter fullscreen mode Exit fullscreen mode

Add the following authentication backend.



AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
)


Enter fullscreen mode Exit fullscreen mode

Copy the template files from the django-allauth repository or you can also use my custom repository(I have made some modifications and some good structuring) and paste it in the folder templates in your project directory.

Add the allauth urls in urls.py of your main project directory. After adding the allauth urls the urls file should look similar to,



from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^accounts/', include('allauth.urls')),
]


Enter fullscreen mode Exit fullscreen mode

You can also add the custom CSS yourself or my CSS (Well commented and documented but simple) that I have created during my use of the allauth templates. It includes styling for almost all the pages, and even mobile-friendly email templates for confirmation and password reset emails. You can do that by creating a static folder in the project directory and placing the CSS in the folder accounts.

Run python manage.py makemigrations and python manage.py migrate to make all the necessary migrations and run python manage.py runserver to start the django server.

Follow the URL patterns to display the registration form.
Eg: Visit localhost:8000/accounts/login to display the login page.

Basic Configuration

Most django-allauth features are can be configured using the built-in adapters and variables by placing them in the file settings.py. Although the documentation has tons of such options with good explanations, I have highlighted some important ones below.

Email confirmation expiry: Sets the number of days within which an account should be activated.

Eg: ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS=7

Email required for activation: This option allows you to set whether the email address should be verified to register. Set False to disable email requirement.

Eg: ACCOUNT_EMAIL_REQUIRED = True

Account email verification: This option can be used to set whether an email verification is necessary for a user to log in after he registers an account. You can use ‘mandatory’ to block a user from logging in until the email gets verified. You can set options for sending the email but allowing the user to log in without an email. You can also set none to send no verification email. (Not Recommended)

Eg: ACCOUNT_EMAIL_VERIFICATION = "mandatory"

Login Attempt Limit: This is an important feature which can be used to prevent brute force attacks on the user login page on your website. The maximum number of login attempts can be set, and the user gets blocked from logging back in until a timeout. This feature makes use of ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT setting.

Eg: ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 5

Login Attempt Limit timeout: This setting should be used with setting ACCOUNT_LOGIN_ATTEMPTS_LIMIT. The value set is in seconds from the last unsuccessful login attempt. Please note that this does not protect admin login.

Eg: ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 86400 # 1 day in seconds

Login and Logout URL redirection: When user logs in or logs out, you might want to redirect the user to a particular URL or page and the below settings can be used to set that URL. By default, allauth redirects login to /accounts/profile/ URL and logout to the localhost:8000 or any localhost homepage.

Eg: ACCOUNT_LOGOUT_REDIRECT_URL ='/accounts/login/'
Eg: LOGIN_REDIRECT_URL = '/accounts/email/'

When you are done, your allauth settings should look similar to the below settings.



ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS =1
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 5
ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 86400 # 1 day in seconds
ACCOUNT_LOGOUT_REDIRECT_URL ='/accounts/login/'
LOGIN_REDIRECT_URL = '/accounts/email/' # default to /accounts/profile 


Enter fullscreen mode Exit fullscreen mode

Extending the django-alluth class and advanced customizations

This section deals with customizing django-allauth signup forms, intervening in registration flow to add custom process and validations of your own.

One of the most common queries about allauth is about adding additional fields or custom fields to the signup form. You can extend the SignupForm class from allauth.account.forms. All you need to do is create a custom class pass the SignupForm to the custom class and define the custom fields and save it. It's vital to return the user object as it will be passed to other modules for validations. You also need to include variables ACCOUNT_FORMS in settings.py.

Let us see this using an example. In forms.py.



from allauth.account.forms import SignupForm
from django import forms

class CustomSignupForm(SignupForm):
    first_name = forms.CharField(max_length=30, label='First Name')
    last_name = forms.CharField(max_length=30, label='Last Name')
    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()
        return user


Enter fullscreen mode Exit fullscreen mode

In the above snippet CustomSignupForm is an extended class which inherits all the features of SignupForm class and adds the necessary features. Here custom fields by the name first_name and last_name are created and saved in using the signup module in the same class.

The ACCOUNT_FORMS in settings.py for the above code is



ACCOUNT_FORMS = {
'signup': 'YourProject.forms.CustomSignupForm',
}


Enter fullscreen mode Exit fullscreen mode

Any other custom forms created can be extended in ACCOUNT_FORMS. The list is illustrated in the documentation.

Similarly, LoginForm UserForm AddEmailForm and others can be extended. However, remember that when you extend these forms and link them in settings.py. Do Not forget to pass the original form (For example SignupForm) as a parameter to your class and sometimes you might have to handle custom validations and return user or some other value. Please refer to the source code to follow the correct flow.

User Intervention and Custom validations

I recently encountered some good questions on stack overflow and on the repository issue section with regards to adding custom validations and intervening in the user registration flow. When researching for a solution and scanning through the documentation, I found the DefaultAccountAdapter very useful and indeed can be used to solve most of the customization problems that a user might encounter while using django-allauth. I reproduce the answers and with some instructions below.

Example 1: Allow restricted list of email’s

After figuring out a way to store and fetch the restricted list, you can use the adapters and raise validation error in the registration form when a restricted email tries to register. Extend a DefaultAccountAdapter and override the clean_email method. Create an adapter.py in your project directory and extend the default adapter class. You can also restrict the email providers (such as temporary email providers) from registering using Regular expressions. I will not cover identifying the email prover (user@provider.com) as it is out of the scope of this article. But the flow remains the same.



from allauth.account.adapter import DefaultAccountAdapter
from django.forms import ValidationError

class RestrictEmailAdapter(DefaultAccountAdapter):
    def clean_email(self,email):
        RestrictedList = ['Your restricted list goes here.']
        if email in RestrictedList
            raise ValidationError('You are restricted from registering. Please contact admin.')
        return email


Enter fullscreen mode Exit fullscreen mode

Finally, point the account adapter in the settings.py file to your extended class. ACCOUNT_ADAPTER = 'YourProject.adapter.RestrictEmailAdapter'

Example 2: Add a Maximum length to a username

As ACCOUNT_USERNAME_MAX_LENGTH does not exist in the allauth library, DefaultAccountAdapter can be used to achieve this feature without much pain. Extend the DefaultAccountAdapter class and overriding the clean_username method. You need to also reference the clean_username once again after the custom validation to complete other inbuilt validations.

Note: The Last sentence in the above paragraph is the key to work with DefaultAccountAdapter. You should never forget to reference the original module name for the module to complete other validations.



from allauth.account.adapter import DefaultAccountAdapter
from django.forms import ValidationError

class UsernameMaxAdapter(DefaultAccountAdapter):
    def clean_username(self, username):
        if len(username) > 'Your Max Size':
            raise ValidationError('Please enter a username value less than the current one')
        return DefaultAccountAdapter.clean_username(self,username) # For other default validations.


Enter fullscreen mode Exit fullscreen mode

Finally, point to the subclass in your settings.py
ACCOUNT_ADAPTER = 'YourProject.adapter.UsernameMaxAdapter'

You can refer to the adapters.py file in the source code and extend other modules and add a processor to change its flow. The modules such as populate_username, clean_password (which can be customized to restrict commonly used passwords) can have a custom process and flow without rewriting them.

DefaultAccountAdapter can be a potent tool if used in the right circumstances to intervene in allauth's default process. allauth comes with a vast variety of inbuilt settings, and they can all be viewed here. You can also download the entire source code from the link in the references.

Setting up social login using django-allauth

Adding the necessary apps:

As illustrated in the allauth-documentation and in the previous section we need to add the necessary apps related to logins that we are going to be using. This section demonstrates the use of Google API configuration using allauth.socialaccount.providers.google and Facebook API configuration using allauth.socialaccount.providers.facebook. You need to add them to INSTALLED_APPS.

After you do so, your installed apps should look like,



INSTALLED_APPS = [
    'django.contrib.admin',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.facebook',
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]


Enter fullscreen mode Exit fullscreen mode

Set SOCIALACCOUNT_QUERY_EMAIL=ACCOUNT_EMAIL_REQUIRED , SOCIALACCOUNT_EMAIL_REQUIRED=ACCOUNT_EMAIL_REQUIRED and SOCIALACCOUNT_STORE_TOKENS=False.if required and you can read more about those settings here. We will also configure some settings for Facebook and Google under SOCIALACCOUNT_PROVIDERS. On details about settings see the documentation here.

Your SOCIALACCOUNT_PROVIDERS settings should look similar to,



SOCIALACCOUNT_PROVIDERS = {
    'facebook': {
        'METHOD': 'oauth2',
        'SCOPE': ['email', 'public_profile', 'user_friends'],
        'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
        'INIT_PARAMS': {'cookie': True},
        'FIELDS': [
            'id',
            'email',
            'name',
            'first_name',
            'last_name',
            'verified',
            'locale',
            'timezone',
            'link',
            'gender',
            'updated_time',
        ],
        'EXCHANGE_TOKEN': True,
        'LOCALE_FUNC': 'path.to.callable',
        'VERIFIED_EMAIL': False,
        'VERSION': 'v2.12',
    },
     'google': {
        'SCOPE': [
            'profile',
            'email',
        ],
        'AUTH_PARAMS': {
            'access_type': 'online',
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Run python manage.py make makemigrations and python manage.py migrate to create database entries. We can now create Facebook and Google API keys. I have briefly described the process below. I recommend you follow instructions on their respective official websites as their services and policies are subjected to change.

API Creation and Configuration:

Follow the below steps and seek out extra referees or tutorials on how to do this on the internet. A brief description is below.

Facebook

  1. Create a test app by visiting developers.facebook.com and filling in all the details.

  2. Copy the App Id and Secret Key.

Click Setup on Facebook Social Login option.<br>
Leave all the other settings default and finish app creation.<br>
In basic settings add localhost to App Domains.<br>

Google

  1. Create Login API and copy the secret credentials.
  2. Don’t forget to place the correct callback URL while creating the API. Place http://localhost:8000/accounts/google/login/callback/ in callback URL.

Adding Google API

Admin Configuration
  1. Create a superuser(admin) by running python manage.py createsuperuser and fill out the details.
  2. Go to http://localhost:8000/admin and log in to the admin console.
  3. Go to the site and change example.com to http://localhost:8000 in both domain name and display name.
  4. Now go to Social Applications and Add social applications and fill in the details.
  5. Repeat the same for Facebook and Google.

Adding localhost to Sites

You are set and done. You can now go ahead and use Facebook and Google logins.

This is one of the first most comprehensive posts on dev.to. Feel free to send hugs or bugs my way.

References and further reading:

  1. django-allauth GitHub page
  2. django-allauth official documentation
  3. My custom templates and CSS
  4. Stack Overflow thread on Example 1
  5. Stack Overflow thread on Example 2
  6. Adapters.py File in Source code
  7. Facebook API and Google API all-auth documentation.
  8. Full source code for the guide

Top comments (29)

Collapse
 
treygec profile image
George Corple

Hi! Thanks for the obvious hard work you put into this. Tried working through it, and it wasn't working. Discovered that you need to add "SITE_ID =1" into your settings file and it all starts working.

Haven't gotten passed the initial setup, but looking forward to the rest!

Collapse
 
devanghingu profile image
Devang Hingu

why "SITE_ID =1" required?

Collapse
 
gajesh profile image
Gajesh

I will fix it. Thanks.

Collapse
 
vaneclavijo profile image
VaneClavijo

when I clik on Google login it redirects me to

Image description

and i want it to redirect me directly to provider login page

Collapse
 
nguepi profile image
Idriss Nguepi

I have This issue to,

Collapse
 
frederickteye profile image
Frederick-Teye

It is default, because the example project that allauth has has the same setup... They just customized it looks. I think that is the best thing for me to do at the moment.

Collapse
 
alfarosalvador profile image
Alfaro

Great blog. It helped a lot setting user authentication in place.

One recommendation, I would encourage users to create a "user authentication" app within the DJANGO project in order to have a cleaner main folder. Just a thought.

Collapse
 
af profile image
AF uarabei

Hi! How would I go about changing the way "Remember Me" checkbox is displayed?
I have tried this:

class UserLoginForm(LoginForm):
    def __init__(self, *args, **kwargs):
        super(UserLoginForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        # self.helper.form_show_labels = False # Doesn't work though it should

        self.fields["login"].label = ""
        self.fields["password"].label = ""

        self.fields["remember"].widget.attrs.update({
            "input": {
                "type": "checkbox",
                "name": "checkbox-fill-1",
                "id": "checkbox-fill-a1",
                "checked": "",
            },
        })
Enter fullscreen mode Exit fullscreen mode

But it does not seem to work. I want to have a custom "Remember me" checkbox that nothing I do changes it

Collapse
 
aakas profile image
aakas

I recently got into a issue where user cannot login whose facebook id is made using mobile number and no email address is connected to it.

Is it due to that i kept ACCOUNT_EMAIL_REQUIRED = True ?

looking forward for solution.

Collapse
 
mistermaker profile image
Mohit Aggarwal

How can i add custom phone number field in django allauth

Collapse
 
shivamrohilla profile image
Shivam Rohilla

I don't know why facebook integration is not working on my live website, it's perfectly run on localhost but not on live website, can you please explain how to assign a user profile while we signup through facebook, I have 2 user types and I want to assign them one user profile can you please tell me how to do this :)

Collapse
 
herchila profile image
Hernán Chilabert

Hey Gajesh, nice post!
Quick question: how can I exclude fields in my CustomSignUp view? Because, If I create my CustomSignUp adding some extra field (like 'role' or whatever), the username, email and password always are there.
Thanks!

Collapse
 
mralexwinkler profile image
Alex Winkler

Really nice in-depth tutorial. I was wondering if you've ever encountered a KeyError at /accounts/login/ 'BACKEND' issue and if so how did you resolve it? I've been trying to find a solution for over a week and just can't seem to resolve it.

Even posted a SOF question..

stackoverflow.com/questions/602517...

Collapse
 
jhoniewebappdev profile image
JhonieWebAppDev

this is great, but do you have a logical explanation why is the TEMPLATES setting is like this?

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.normpath(os.path.join(BASE_DIR, 'templates')),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request', => # "same as below"
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.request', => # "same as above"
],
},
},

]

is this necessary?
why not like this?
for it's more simple and understandable

============================================================

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.normpath(os.path.join(BASE_DIR, 'templates')),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.request', = > not "DRY"
'django.contrib.messages.context_processors.messages',
],
},
},
]