python / expert
Snippet
Django Authentication Backend with Custom User Model
Django's authentication system is pluggable via authentication backends that can support multiple authentication methods simultaneously. This snippet demonstrates a custom authentication backend that authenticates against username, email, or phone number, implements account lockout after failed attempts, and uses Django's F() expressions for atomic counter updates. Combined with a custom User model manager, this provides a complete authentication solution with security hardening.
snippet.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from django.contrib.auth.backends import ModelBackendfrom django.contrib.auth import get_user_modelfrom django.db.models import Qclass MultipleAuthBackend(ModelBackend):def authenticate(self, request, username=None, password=None, **kwargs):User = get_user_model()if not username or not password:return Nonetry:user = User.objects.get(Q(username=username) | Q(email=username) | Q(phone=username))except User.DoesNotExist:User().set_password(password)return Noneif not user.is_active:return Noneif user.locked_until and user.locked_until > now():raise PermissionError('Account temporarily locked')if user.check_password(password):self._update_login_attempts(user, success=True)return userself._update_login_attempts(user, success=False)return Nonedef _update_login_attempts(self, user, success=True):if success:user.failed_login_attempts = 0user.locked_until = Noneelse:user.failed_login_attempts = F('failed_login_attempts') + 1if user.failed_login_attempts >= getattr(settings, 'MAX_LOGIN_ATTEMPTS', 5):user.locked_until = now() + timedelta(minutes=30)user.save(update_fields=['failed_login_attempts', 'locked_until'])class CustomUserManager(BaseUserManager):def create_superuser(self, username, email, password, **extra_fields):extra_fields.setdefault('is_staff', True)extra_fields.setdefault('is_superuser', True)if extra_fields.get('is_staff') is not True:raise ValueError('Superuser must have is_staff=True')return self._create_user(username, email, password, **extra_fields)def _create_user(self, username, email, password, **extra_fields):if not username:raise ValueError('Username must be set')
django
Breakdown
1
class MultipleAuthBackend(ModelBackend):
Custom backend extending Django's ModelBackend for multi-field authentication
2
Q(username=username) | Q(email=username) | Q(phone=username)
Q objects combine OR conditions for authenticating with any identifier
3
if not user.is_active: return None
Prevents authentication of inactive/deactivated accounts
4
user.locked_until and user.locked_until > now()
Time-based account lockout check after failed attempts
5
user.failed_login_attempts = F('failed_login_attempts') + 1
F() expression enables atomic database-side increment without race conditions
6
user.save(update_fields=[...])
Partial update restricts SQL to only modified columns for efficiency