Django : Custom User Model & Allauth for OAuth
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).
- About custom user models
- Create the model and manager
- Use the custom user
- 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 aOneToOneField
. - 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 fromAbstractUser
. It requires a special care and to update some references through thesettings.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 fromAbstractBaseUser
. It requires a special care and to update some references through thesettings.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 fromdjango.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 theemail
field for us).EMAIL_FIELD
: The name of the field that will be returned whenget_email_field_name()
is called on a User instance.REQUIRED_FIELDS
: Required fields besides thepassword
andUSERNAME_FIELD
when signing up.is_staff
: required by the admin.is_superuser
: used by thePermissionsMixin
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:
- In the admin add a
Site
for your domain, matchingsettings.SITE_ID
. Set the Domain name tohttp://127.0.0.1:8000
for development. - For each OAuth based provider, add a
Social Application
. - 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/
- 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:
- http://127.0.0.1:8000/accounts/signup/
- http://127.0.0.1:8000/accounts/logout/
- http://127.0.0.1:8000/accounts/login/
login with email or social account - http://127.0.0.1:8000/accounts/email/
manage email addresses - http://127.0.0.1:8000/accounts/password/change/
- http://127.0.0.1:8000/accounts/social/connections/
add social accounts to your user account