django-allauth provides comprehensive account management capabilities that go far beyond simple login/logout. It handles user registration, multiple email addresses, password changes, and complex email verification workflows.
The allauth.account app is the core module responsible for managing regular (non-social) user accounts.
from django import formsfrom allauth.account.forms import SignupFormclass CustomSignupForm(forms.Form): """ Custom signup form that collects additional user data. Must implement a signup() method that is called after the user is created. """ first_name = forms.CharField( max_length=30, label='First Name', required=True ) last_name = forms.CharField( max_length=30, label='Last Name', required=True ) date_of_birth = forms.DateField( widget=forms.SelectDateWidget(years=range(1920, 2010)), required=False ) accept_terms = forms.BooleanField( required=True, label='I accept the Terms of Service' ) def signup(self, request, user): """ Called after user account is created. Save custom fields to user profile or related model. """ user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() # Create or update user profile user.profile.date_of_birth = self.cleaned_data.get('date_of_birth') user.profile.save()
This adds a hidden field to the signup form. Legitimate users won’t see it, but bots may fill it out. If the field contains data, the signup is silently rejected while appearing successful to the bot.
Honeypots work against simple bots but won’t stop sophisticated attacks. Use in combination with other anti-spam measures.
When ACCOUNT_UNIQUE_EMAIL = True, only one user can have a verified email address. This prevents account confusion and is recommended for most applications.
# settings.pyACCOUNT_RATE_LIMITS = { "change_password": "5/m/user", # 5 attempts per minute per user}# Optional: Log out all sessions after password changeACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = False # Default
View
Template
Adapter Hook
from allauth.account.views import PasswordChangeViewurlpatterns = [ path('accounts/password/change/', PasswordChangeView.as_view(), name='account_change_password'),]
Password reset flow with enumeration prevention:Configuration:
# settings.py# Send emails even for unknown accounts (prevents enumeration)ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = True# Automatically log in after password resetACCOUNT_LOGIN_ON_PASSWORD_RESET = False# Token generator for password reset linksACCOUNT_PASSWORD_RESET_TOKEN_GENERATOR = \ "allauth.account.forms.EmailAwarePasswordResetTokenGenerator"# Rate limitsACCOUNT_RATE_LIMITS = { "reset_password": "20/m/ip,5/m/key", # Per IP and per email "reset_password_from_key": "20/m/ip", # Submitting the reset form}
from allauth.account.adapter import DefaultAccountAdapterfrom django.shortcuts import reversefrom django.conf import settingsclass MyAccountAdapter(DefaultAccountAdapter): def is_open_for_signup(self, request): """ Control whether new signups are allowed. """ # Close signups during maintenance if settings.MAINTENANCE_MODE: return False # Require invitation code return request.session.get('invitation_code') is not None def get_login_redirect_url(self, request): """ Custom redirect after login. """ # Redirect to onboarding for new users if request.user.profile.is_new: return reverse('onboarding') # Otherwise use default return super().get_login_redirect_url(request) def save_user(self, request, user, form, commit=True): """ Called when saving a new user account. """ user = super().save_user(request, user, form, commit=False) # Set custom fields user.language = request.LANGUAGE_CODE user.timezone = request.session.get('timezone', 'UTC') if commit: user.save() return user def confirm_email(self, request, email_address): """ Called when an email address is verified. """ # Call parent implementation super().confirm_email(request, email_address) # Grant access to beta features if email_address.email.endswith('@beta-testers.com'): email_address.user.profile.beta_access = True email_address.user.profile.save()
from allauth.account.signals import user_signed_upfrom django.dispatch import receiver@receiver(user_signed_up)def on_user_signed_up(request, user, **kwargs): """ Create user profile, send welcome email, track analytics. """ # Create related models UserProfile.objects.create(user=user) # Send welcome email send_mail( 'Welcome!', 'Thanks for joining our platform.', '[email protected]', [user.email], ) # Track conversion analytics.track(user.id, 'User Signed Up')
user_logged_in
Sent when a user successfully logs in.
from allauth.account.signals import user_logged_in@receiver(user_logged_in)def on_user_logged_in(request, user, **kwargs): # Update last login IP user.profile.last_login_ip = get_client_ip(request) user.profile.save()
user_logged_out
Sent when a user logs out.
from allauth.account.signals import user_logged_out@receiver(user_logged_out)def on_user_logged_out(request, user, **kwargs): # Clear cached data cache.delete(f'user_data_{user.id}')
email_confirmed
Sent when an email address is verified.
from allauth.account.signals import email_confirmed@receiver(email_confirmed)def on_email_confirmed(request, email_address, **kwargs): user = email_address.user # Activate trial if not user.subscription: user.subscription = Subscription.create_trial(user)
email_added
Sent when a new email address is added to an account.
from allauth.account.signals import email_added@receiver(email_added)def on_email_added(request, user, email_address, **kwargs): # Log for security audit AuditLog.objects.create( user=user, action='email_added', details={'email': email_address.email} )