Django-allauth

authentication libraryDjango-allauthDjangoPythonsocial authenticationOAuthemail verificationmulti-factor authenticationcomprehensive authentication

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')