Django-allauth
Authentication Library
Django-allauth
Overview
Django-allauth is a comprehensive authentication and authorization library for Django applications. As of 2025, version 65.4.0 is the latest release, supporting over 50 social authentication providers including Google, GitHub, Facebook, Twitter, Apple, and more. It provides integrated solutions for user registration, email verification, password management, social login, and multi-factor authentication (MFA). With headless authentication support and Django REST Framework integration, it enables building authentication systems for modern SPA applications while offering extensive customization options and enterprise-grade features.
Details
Django-allauth is designed as an all-in-one authentication solution that reduces development time by providing features that typically require multiple libraries. It offers unified APIs for local authentication and social authentication, streamlined user lifecycle management from registration to account deletion, and comprehensive email verification flows. Supporting Django 3.2+ and Python 3.8+, it features session-based and token-based authentication, extensive template customization, signal support, and admin interface integration. The 2025 version includes enhanced headless authentication capabilities for React, Vue.js, and other SPA frameworks, making it suitable for modern web architecture.
Key Features
- 50+ Social Providers: Pre-integrated support for major OAuth providers
- Comprehensive Authentication: Registration, login, password reset, email verification
- Multi-Factor Authentication: TOTP, WebAuthn, recovery codes support
- Headless Support: REST API and SPA-friendly authentication
- Django Integration: Seamless integration with Django's authentication system
- High Customizability: Extensive template and workflow customization options
Advantages and Disadvantages
Advantages
- Established as the de facto standard for Django authentication with extensive track record
- Comprehensive feature set reduces need for multiple authentication libraries
- Over 50 social authentication providers supported out of the box
- Active maintenance with regular updates and security patches
- Excellent Django ecosystem integration including admin interface and DRF
- Extensive documentation and large community support
Disadvantages
- Django-specific, not usable with other frameworks
- High learning curve due to extensive configuration options
- Large library size may be overkill for simple authentication needs
- Complex customization requirements may require deep understanding of internals
- Potential performance impact in high-traffic applications
- Limited support for cutting-edge authentication standards
Reference Pages
Usage Examples
Installation and Project Setup
# Install Django-allauth
pip install django-allauth
# For social authentication providers
pip install requests-oauthlib
# For development
pip install python-dotenv
# For PostgreSQL (recommended)
pip install psycopg2-binary
# settings.py - Django Configuration
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# Django-allauth Configuration
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
# allauth
'allauth',
'allauth.account',
'allauth.socialaccount',
# social providers
'allauth.socialaccount.providers.google',
'allauth.socialaccount.providers.github',
'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.twitter',
'allauth.socialaccount.providers.discord',
# headless authentication (2025 feature)
'allauth.headless',
# applications
'accounts',
'api',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'allauth.account.middleware.AccountMiddleware', # Required for allauth
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# Authentication backends
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]
SITE_ID = 1
# allauth settings
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
# Login/Logout URLs
LOGIN_REDIRECT_URL = '/dashboard/'
LOGOUT_REDIRECT_URL = '/'
# Email backend (for development)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Production email settings
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_HOST = 'smtp.gmail.com'
# EMAIL_PORT = 587
# EMAIL_USE_TLS = True
# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')
# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')
# Social authentication settings
SOCIALACCOUNT_LOGIN_ON_GET = True
SOCIALACCOUNT_AUTO_SIGNUP = True
SOCIALACCOUNT_EMAIL_VERIFICATION = 'none'
# Headless authentication (2025 feature)
HEADLESS_FRONTEND_URLS = {
"account_confirm_email": "http://localhost:3000/auth/verify-email/{key}",
"account_reset_password": "http://localhost:3000/auth/password/reset",
"account_reset_password_from_key": "http://localhost:3000/auth/password/reset/key/{key}",
}
# Database Configuration
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DATABASE_NAME', 'django_allauth'),
'USER': os.getenv('DATABASE_USER', 'postgres'),
'PASSWORD': os.getenv('DATABASE_PASSWORD', ''),
'HOST': os.getenv('DATABASE_HOST', 'localhost'),
'PORT': os.getenv('DATABASE_PORT', '5432'),
}
}
Social Login Configuration
# settings.py - Social Provider Configuration
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': [
'profile',
'email',
],
'AUTH_PARAMS': {
'access_type': 'online',
},
'OAUTH_PKCE_ENABLED': True,
'FETCH_USERINFO': True,
},
'github': {
'SCOPE': [
'user:email',
],
'VERIFIED_EMAIL': True,
},
'facebook': {
'METHOD': 'oauth2',
'SDK_URL': '//connect.facebook.net/{locale}/sdk.js',
'SCOPE': ['email', 'public_profile'],
'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
'INIT_PARAMS': {'cookie': True},
'FIELDS': [
'id',
'first_name',
'last_name',
'middle_name',
'name',
'name_format',
'picture',
'short_name'
],
'EXCHANGE_TOKEN': True,
'LOCALE_FUNC': lambda request: 'en_US',
'VERIFIED_EMAIL': False,
'VERSION': 'v19.0',
},
'discord': {
'SCOPE': ['identify', 'email'],
'FETCH_USERINFO': True,
},
'twitter': {
'OAUTH_VERSION': '2',
'SCOPE': ['tweet.read', 'users.read'],
}
}
# urls.py - URL Configuration
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('auth/', include('accounts.urls')),
path('api/auth/', include('allauth.headless.urls')), # Headless API
path('', include('main.urls')),
]
# .env - Environment Variables
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# GitHub OAuth
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# Facebook OAuth
FACEBOOK_APP_ID=your-facebook-app-id
FACEBOOK_APP_SECRET=your-facebook-app-secret
# Discord OAuth
DISCORD_CLIENT_ID=your-discord-client-id
DISCORD_CLIENT_SECRET=your-discord-client-secret
# Twitter OAuth v2
TWITTER_CLIENT_ID=your-twitter-client-id
TWITTER_CLIENT_SECRET=your-twitter-client-secret
User Registration and Email Verification
# accounts/forms.py - Custom Registration Form
from allauth.account.forms import SignupForm
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
from django import forms
from django.contrib.auth.models import User
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='First Name')
last_name = forms.CharField(max_length=30, label='Last Name')
terms_agreement = forms.BooleanField(
required=True,
label='I agree to the Terms of Service and Privacy Policy'
)
def save(self, request):
user = super().save(request)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
class CustomSocialSignupForm(SocialSignupForm):
terms_agreement = forms.BooleanField(
required=True,
label='I agree to the Terms of Service and Privacy Policy'
)
def save(self, request):
user = super().save(request)
# Additional processing for social signup
return user
# accounts/models.py - User Profile Extension
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
website = models.URLField(blank=True)
phone_number = models.CharField(max_length=20, blank=True)
birth_date = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
email_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
# accounts/signals.py - Signal Handlers
from allauth.account.signals import user_signed_up, email_confirmed
from allauth.socialaccount.signals import pre_social_login
from django.dispatch import receiver
from django.contrib.auth.models import User
@receiver(user_signed_up)
def user_signed_up_handler(request, user, **kwargs):
print(f'New user signed up: {user.email}')
# Send welcome email
# Initialize user preferences
# Log user registration
@receiver(email_confirmed)
def email_confirmed_handler(request, email_address, **kwargs):
user = email_address.user
user.userprofile.email_verified = True
user.userprofile.save()
print(f'Email confirmed for user: {user.email}')
@receiver(pre_social_login)
def pre_social_login_handler(request, sociallogin, **kwargs):
# Connect social account to existing user if email matches
if sociallogin.is_existing:
return
if not sociallogin.email_addresses:
return
email = sociallogin.email_addresses[0].email
try:
existing_user = User.objects.get(email=email)
sociallogin.connect(request, existing_user)
except User.DoesNotExist:
pass
Customization and Template Configuration
# settings.py - Form Configuration
ACCOUNT_FORMS = {
'signup': 'accounts.forms.CustomSignupForm',
'social_signup': 'accounts.forms.CustomSocialSignupForm',
}
# accounts/adapters.py - Custom Adapters
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from django.conf import settings
from django.http import HttpRequest
class CustomAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request: HttpRequest):
return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True)
def save_user(self, request, user, form, commit=True):
user = super().save_user(request, user, form, False)
# Custom user processing
user.is_active = True # Auto-activate users
if commit:
user.save()
return user
def send_confirmation_mail(self, request, emailconfirmation, signup):
# Custom email sending logic
super().send_confirmation_mail(request, emailconfirmation, signup)
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def is_open_for_signup(self, request: HttpRequest, sociallogin):
return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True)
def save_user(self, request, sociallogin, form=None):
user = super().save_user(request, sociallogin, form)
# Additional processing for social users
return user
def populate_user(self, request, sociallogin, data):
user = super().populate_user(request, sociallogin, data)
# Customize user data from social provider
if 'name' in data:
name_parts = data['name'].split(' ', 1)
user.first_name = name_parts[0]
if len(name_parts) > 1:
user.last_name = name_parts[1]
return user
# Update settings.py
ACCOUNT_ADAPTER = 'accounts.adapters.CustomAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'accounts.adapters.CustomSocialAccountAdapter'
<!-- templates/account/login.html - Custom Login Template -->
{% extends "account/base.html" %}
{% load i18n %}
{% load account socialaccount %}
{% block head_title %}{% trans "Sign In" %}{% endblock %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="mb-0">{% trans "Sign In" %}</h3>
</div>
<div class="card-body">
<!-- Social Login Buttons -->
<div class="mb-4">
<a href="{% provider_login_url 'google' %}" class="btn btn-danger w-100 mb-2">
<i class="fab fa-google"></i> Sign in with Google
</a>
<a href="{% provider_login_url 'github' %}" class="btn btn-dark w-100 mb-2">
<i class="fab fa-github"></i> Sign in with GitHub
</a>
<a href="{% provider_login_url 'facebook' %}" class="btn btn-primary w-100 mb-2">
<i class="fab fa-facebook"></i> Sign in with Facebook
</a>
</div>
<hr>
<p class="text-center text-muted">Or sign in with email</p>
<!-- Email Login Form -->
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
<div class="mb-3">
<label for="id_login" class="form-label">Email</label>
<input type="email" class="form-control" name="login" placeholder="Enter your email" required>
</div>
<div class="mb-3">
<label for="id_password" class="form-label">Password</label>
<input type="password" class="form-control" name="password" placeholder="Enter your password" required>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" name="remember" id="id_remember">
<label class="form-check-label" for="id_remember">
{% trans 'Remember Me' %}
</label>
</div>
<button class="btn btn-success w-100" type="submit">{% trans "Sign In" %}</button>
</form>
<div class="text-center mt-3">
<a href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a><br>
<a href="{% url 'account_signup' %}">{% trans "Don't have an account? Sign up" %}</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
API Authentication and Django REST Framework Integration
# settings.py - DRF Configuration
INSTALLED_APPS += [
'rest_framework',
'rest_framework.authtoken',
'dj_rest_auth',
'dj_rest_auth.registration',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'allauth.headless.authentication.HeadlessAuthentication', # 2025 feature
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
}
# Headless Authentication Settings
HEADLESS_TOKEN_STRATEGY = 'allauth.headless.tokens.SessionTokenStrategy'
HEADLESS_ADAPTER = 'accounts.adapters.HeadlessAdapter'
# api/views.py - DRF Views with allauth
from rest_framework import generics, permissions, status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from allauth.headless.base.response import APIResponse
from allauth.headless.account.views import LoginView as HeadlessLoginView
from allauth.headless.socialaccount.views import RedirectToProviderView
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserProfileAPIView(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user
class CustomHeadlessLoginView(HeadlessLoginView):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
if response.status_code == 200:
# Custom logic after successful login
user = request.user
# Log login event
# Update last login timestamp
return response
@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def protected_endpoint(request):
return Response({
'message': 'This is a protected endpoint',
'user': request.user.email,
'authentication_method': request.auth.__class__.__name__
})
@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def social_disconnect(request):
"""Disconnect social account"""
provider = request.data.get('provider')
if not provider:
return Response({'error': 'Provider required'}, status=400)
try:
social_account = request.user.socialaccount_set.get(provider=provider)
social_account.delete()
return Response({'message': f'{provider} account disconnected'})
except:
return Response({'error': 'Social account not found'}, status=404)
# Frontend Integration Example (React)
"""
// React component for headless authentication
import React, { useState } from 'react';
const LoginForm = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async (e) => {
e.preventDefault();
const response = await fetch('/api/auth/login/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken(),
},
credentials: 'include',
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok) {
// Handle successful login
localStorage.setItem('sessionToken', data.access_token);
window.location.href = '/dashboard';
} else {
// Handle login error
console.error('Login failed:', data);
}
};
return (
<form onSubmit={handleLogin}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Login</button>
</form>
);
};
"""
Testing and Advanced Configuration
# tests/test_authentication.py - Comprehensive Testing
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.urls import reverse
from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount, SocialApp
from allauth.socialaccount.providers.google.provider import GoogleProvider
class AuthenticationTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
email='[email protected]',
password='testpass123'
)
def test_email_signup(self):
"""Test email-based user registration"""
response = self.client.post(reverse('account_signup'), {
'email': '[email protected]',
'password1': 'newpass123',
'password2': 'newpass123',
'first_name': 'Test',
'last_name': 'User',
'terms_agreement': True
})
self.assertEqual(response.status_code, 302)
self.assertTrue(User.objects.filter(email='[email protected]').exists())
def test_email_verification(self):
"""Test email verification process"""
user = User.objects.create_user(
username='unverified',
email='[email protected]',
password='testpass123'
)
email_address = EmailAddress.objects.create(
user=user,
email='[email protected]',
verified=False,
primary=True
)
confirmation = email_address.send_confirmation()
# Test email confirmation
response = self.client.get(
reverse('account_confirm_email', args=[confirmation.key])
)
email_address.refresh_from_db()
self.assertTrue(email_address.verified)
def test_social_login_setup(self):
"""Test social authentication setup"""
# Create social app
social_app = SocialApp.objects.create(
provider='google',
name='Google',
client_id='test-client-id',
secret='test-secret'
)
social_app.sites.add(1)
# Test provider URL generation
provider_url = reverse('socialaccount_provider_login', args=['google'])
response = self.client.get(provider_url)
self.assertEqual(response.status_code, 302)
self.assertIn('accounts.google.com', response.url)
class HeadlessAPITestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='apiuser',
email='[email protected]',
password='apipass123'
)
def test_headless_login(self):
"""Test headless authentication API"""
response = self.client.post('/api/auth/login/', {
'email': '[email protected]',
'password': 'apipass123'
}, content_type='application/json')
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertIn('access_token', data)
def test_authenticated_api_access(self):
"""Test API access with authentication"""
# Login first
login_response = self.client.post('/api/auth/login/', {
'email': '[email protected]',
'password': 'apipass123'
}, content_type='application/json')
token = login_response.json()['access_token']
# Access protected endpoint
response = self.client.get('/api/profile/',
HTTP_AUTHORIZATION=f'Token {token}'
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['email'], '[email protected]')
# settings.py - Advanced Configuration
ACCOUNT_RATE_LIMITS = {
# Rate limiting for authentication endpoints
'login_failed': '5/5m',
'signup': '10/m',
'reset_password': '5/5m',
'confirm_email': '10/5m',
}
# Multi-factor authentication
ACCOUNT_MFA_ENABLED = True
ACCOUNT_MFA_TOTP_ISSUER = 'MyApp'
ACCOUNT_MFA_RECOVERY_CODES_COUNT = 10
# Session configuration
SESSION_COOKIE_AGE = 86400 # 24 hours
SESSION_COOKIE_SECURE = True # HTTPS only
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
# CSRF protection
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'
# Custom middleware for logging
class AuthenticationLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path.startswith('/accounts/'):
print(f"Authentication request: {request.method} {request.path}")
response = self.get_response(request)
return response
# Add to MIDDLEWARE list
MIDDLEWARE.append('accounts.middleware.AuthenticationLoggingMiddleware')