Django : Custom User Model & Allauth for OAuth

Sarthak Kumar
8 min readApr 2, 2019

--

Django ships with a built-in User model for authentication, however the official Django documentation highly recommends using a custom user model for new projects. The reason is if you want to make any changes to the User model down the road–for example adding a date of birth field–using a custom user model from the beginning makes this quite easy. But if you do not, updating the default User model in an existing Django project is very, very challenging.

The brief

This post will detail how to create a custom user with email authentication and how to integrate this in your admin interface, and your project as a whole. In place of custom views for signup,signin etc. we will use django-allauth and add Oauth (3rd party / social login).

  1. About custom user models
  2. Create the model and manager
  3. Use the custom user
  4. Using Allauth for account management

Note:~ After starting the project DO NOT apply the migrations. Remember: You must create the custom User model before you apply your first migration.It is best to first make migrations for your custom user. Not having the custom user model when you start your project will likely lead to conflicts!

1. About custom user models

Django documents three ways to extend or replace the default User:

1. Extend with a one-to-one field to User

  • What is a One-To-One Link?
    It is a regular Django model that’s gonna have it’s own database table and will hold a One-To-One relationship with the existing User Model through a OneToOneField.
  • When should I use a One-To-One Link?
    You should use a One-To-One Link when you need to store extra information about the existing User Model that’s not related to the authentication process. We usually call it a User Profile.

2. Substitute by subclassing from AbstractUser: this preserves the fields and authentication methods

  • What is a Custom User Model Extending AbstractUser?
    It is a new User model that inherit from AbstractUser. It requires a special care and to update some references through the settings.py. Ideally it should be done in the begining of the project, since it will dramatically impact the database schema. Extra care while implementing it.
  • When should I use a Custom User Model Extending AbstractUser?
    You should use it when you are perfectly happy with how Django handles the authentication process and you wouldn’t change anything on it. Yet, you want to add some extra information directly in the User model, without having to create an extra class

3. Substitute by subclassing from AbstractBaseUser: this will not preserve the default fields, also impacting existing methods.

  • What is a Custom User Model Extending AbstractBaseUser?
    It is an entirely new User model that inherit from AbstractBaseUser. It requires a special care and to update some references through the settings.py. Ideally it should be done in the begining of the project, since it will dramatically impact the database schema. Extra care while implementing it.
  • When should I use a Custom User Model Extending AbstractBaseUser?
    You should use a Custom User Model when your application have specific requirements in relation to the authentication process. For example, in some cases it makes more sense to use an email address as your identification token instead of a username.

The third option is most involved, but it is the one we will choose, because it offers the freedom to customize registration with email rather than username.

First order of business: a new app named users to hold our custom user model. Name it however you please.

python manage.py startapp users

And add the following to settings.py. This indicates that we want to use a yet-to-be-created model users.User for authentication, replacing the default django.contrib.auth.models.User.

INSTALLED_APPS = [
...
'users',
]
AUTH_USER_MODEL = 'users.User'

When you need to refer to the custom user from other parts of your code, you can do that in any of the following ways:

  • from users import User
  • from customuser.settings import AUTH_USER_MODEL
  • use the get_user_model() method from django.contrib.auth

The third method is preferred — certain for code that you intend to re-use — as it deals with both the default and custom user models.

2. Create the model and manager

The following model in users/models.py takes care of the custom User model.

from django.contrib.auth.models import AbstractBaseUser,    BaseUserManager, PermissionsMixin
from django.db import models
from django.utils import timezone


class UserManager(BaseUserManager):

def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
now = timezone.now()
email = self.normalize_email(email)
user = self.model(
email=email,
is_staff=is_staff,
is_active=True,
is_superuser=is_superuser,
last_login=now,
date_joined=now,
**extra_fields
)
user.set_password(password)
user.save(using=self._db)
return user

def create_user(self, email, password, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields)

def create_superuser(self, email, password, **extra_fields):
user=self._create_user(email, password, True, True, **extra_fields)
user.save(using=self._db)
return user


class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=254, null=True, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)


USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []

objects = UserManager()

def get_absolute_url(self):
return "/users/%i/" % (self.pk)

Our User subclasses AbstractBaseUser, giving it the methods, but none of the default fields. We define our own fields, with the following being special cases:

  • USERNAME_FIELD: The name of the field that will serve as unique identifier (which will be the email field for us).
  • EMAIL_FIELD: The name of the field that will be returned when get_email_field_name() is called on a User instance.
  • REQUIRED_FIELDS: Required fields besides the password and USERNAME_FIELD when signing up.
  • is_staff: required by the admin.
  • is_superuser: used by the PermissionsMixin to grant all permissions.
  • is_active: indicates whether the user is considered “active”.

The UserManager subclasses the BaseUserManager and overrides the methods create_user and create_superuser. These custom methods are needed because the default methods expect a username to be provided. The admin app and manage.py will call these methods.

If you want to see a project example where custom user model and allauth library is used then i am making a project using them :~

3. Use the custom user

Apply the migrations and create a superuser. You should be prompted for an Email: (and not a username) and no errors should occur.

The admin interface isn’t aware of the custom User model yet,you can simply register the cutom User model using admin.site.register(User) or you can customize how data appears using:~

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from .models import User


class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('email', 'password', 'name', 'last_login')}),
('Permissions', {'fields': (
'is_active',
'is_staff',
'is_superuser',
'groups',
'user_permissions',
)}),
)
add_fieldsets = (
(
None,
{
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')
}
),
)

list_display = ('email', 'name', 'is_staff', 'last_login')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions',)


admin.site.register(User, UserAdmin)

It is worth taking the following from the Django docs into consideration concerning custom user models:

Think carefully before handling information not directly related to authentication in your custom user model.

It may be better to store app-specific user information in a model that has a relation with the user model. That allows each app to specify its own user data requirements without risking conflicts with other apps. On the other hand, queries to retrieve this related information will involve a database join, which may have an effect on performance.

4. Using Allauth for account management

We can create custom signup,signin and signout views,forms just like we do normally using our custom user model or we can use a few libraries that are designed to handle this exact scenario. We will use the well-designed library “django-allauth”, at it works well with our custom User model.

It also handles additional requirements such as:

  • email address verification
  • enter a single password on sign up
  • add social authentication (Oauth) #Most appealing to me
  • reset passwords
virtualenv install django-allauth

Configuration

Assuming you start with a default settings.py, these are the changes required by Allauth in settings.py:

AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # existing backend
'allauth.account.auth_backends.AuthenticationBackend',
)
INSTALLED_APPS = [
...
'django.contrib.sites', # make sure sites is included
'allauth',
'allauth.account',
'allauth.socialaccount',
# the social providers
'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.google',
'allauth.socialaccount.providers.twitter',
...
]
SITE_ID = 1ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'

Edit urls.py:

urlpatterns = [
...
path('accounts/', include('allauth.urls')),
...
]

And finally:

python manage.py migrate
python manage.py runserver

You will now have the basic functionality of our “custom” solution above and then some. The Allauth views and forms gracefully handle our User model with email authentication, after we provided this information in the settings.

Add Oauth providers

A further feature is the support of the OAuth authentication standard. This allows third party servers to perform the authentication. Users can also authorize your app to access profile data, such as the email address.

After starting your server, visit the admin pages (localhost:8000/admin/) and follow these steps:

  1. In the admin add a Site for your domain, matching settings.SITE_ID . Set the Domain name to http://127.0.0.1:8000 for development.
  2. For each OAuth based provider, add a Social Application.
  3. Go to the provider (Google, etc) to add your client app, and add a redirect URL in the form of: http://127.0.0.1:8000/accounts/google/login/callback/
  4. The provider will give you a Client ID and a Client Secret. Fill these in with the Social Application in the admin.

After setting up OAuth by getting Client ID and Client Secret,the login view i.e http://127.0.0.1:8000/accounts/login will appear something like this:~

The official documentation has detailed instructions, and a list of social authentication providers.

Try it out

Allauth gives a range of account functionalities. Try them out:

--

--

Sarthak Kumar

I’m a Software Engineer(Backend) who blogs sometimes and loves to learn and try new tools & technologies. My corner of the internet : https://sarthakkumar.xyz