0
# Authentication and Permissions
1
2
Security framework providing authentication backends for user identification and permission classes for access control. Supports multiple authentication methods and granular permission systems.
3
4
## Capabilities
5
6
### Authentication Classes
7
8
Authentication classes identify users making requests to the API.
9
10
```python { .api }
11
class BaseAuthentication:
12
"""
13
Base class for all authentication backends.
14
"""
15
def authenticate(self, request):
16
"""
17
Authenticate the request and return user/auth tuple.
18
19
Args:
20
request: DRF Request object
21
22
Returns:
23
tuple or None: (user, auth) tuple or None if not authenticated
24
"""
25
26
def authenticate_header(self, request):
27
"""
28
Return string for WWW-Authenticate header when authentication fails.
29
30
Args:
31
request: DRF Request object
32
33
Returns:
34
str or None: Authentication header value
35
"""
36
37
class BasicAuthentication(BaseAuthentication):
38
"""
39
HTTP Basic authentication using username and password.
40
"""
41
www_authenticate_realm = 'api'
42
43
def authenticate(self, request):
44
"""Authenticate using HTTP Basic credentials."""
45
46
def authenticate_credentials(self, userid, password, request=None):
47
"""
48
Authenticate credentials against user database.
49
50
Args:
51
userid (str): Username
52
password (str): Password
53
request: HTTP request
54
55
Returns:
56
tuple: (user, None) if valid
57
58
Raises:
59
AuthenticationFailed: If credentials invalid
60
"""
61
62
class SessionAuthentication(BaseAuthentication):
63
"""
64
Django session-based authentication.
65
"""
66
def authenticate(self, request):
67
"""Authenticate using Django session."""
68
69
def enforce_csrf(self, request):
70
"""
71
Enforce CSRF validation for authenticated requests.
72
73
Raises:
74
PermissionDenied: If CSRF validation fails
75
"""
76
77
class TokenAuthentication(BaseAuthentication):
78
"""
79
Token-based authentication using database-stored tokens.
80
"""
81
keyword = 'Token' # Authorization header keyword
82
model = Token # Token model class
83
84
def authenticate(self, request):
85
"""Authenticate using token from Authorization header."""
86
87
def authenticate_credentials(self, key):
88
"""
89
Authenticate token key against database.
90
91
Args:
92
key (str): Token key
93
94
Returns:
95
tuple: (user, token) if valid
96
97
Raises:
98
AuthenticationFailed: If token invalid
99
"""
100
101
class RemoteUserAuthentication(BaseAuthentication):
102
"""
103
Authentication using REMOTE_USER header from web server.
104
"""
105
header = 'HTTP_REMOTE_USER' # Header name
106
107
def authenticate(self, request):
108
"""Authenticate using remote user header."""
109
110
def clean_username(self, username):
111
"""
112
Clean username for user lookup.
113
114
Args:
115
username (str): Raw username
116
117
Returns:
118
str: Cleaned username
119
"""
120
```
121
122
### Permission Classes
123
124
Permission classes control access to views and objects.
125
126
```python { .api }
127
class BasePermission:
128
"""
129
Base class for all permission checks with operator support.
130
"""
131
def has_permission(self, request, view):
132
"""
133
Check if request has permission to access view.
134
135
Args:
136
request: DRF Request object
137
view: View being accessed
138
139
Returns:
140
bool: True if permission granted
141
"""
142
143
def has_object_permission(self, request, view, obj):
144
"""
145
Check if request has permission to access specific object.
146
147
Args:
148
request: DRF Request object
149
view: View being accessed
150
obj: Object being accessed
151
152
Returns:
153
bool: True if permission granted
154
"""
155
156
def __and__(self, other):
157
"""Combine permissions with AND logic."""
158
return OperandHolder(AND, self, other)
159
160
def __or__(self, other):
161
"""Combine permissions with OR logic."""
162
return OperandHolder(OR, self, other)
163
164
def __invert__(self):
165
"""Negate permission."""
166
return SingleOperandHolder(NOT, self)
167
168
class AllowAny(BasePermission):
169
"""
170
Allow access to any request.
171
"""
172
def has_permission(self, request, view):
173
return True
174
175
class IsAuthenticated(BasePermission):
176
"""
177
Allow access only to authenticated users.
178
"""
179
def has_permission(self, request, view):
180
return bool(request.user and request.user.is_authenticated)
181
182
class IsAdminUser(BasePermission):
183
"""
184
Allow access only to admin users.
185
"""
186
def has_permission(self, request, view):
187
return bool(request.user and request.user.is_staff)
188
189
class IsAuthenticatedOrReadOnly(BasePermission):
190
"""
191
Allow read permissions to any request, write permissions to authenticated users.
192
"""
193
def has_permission(self, request, view):
194
return (
195
request.method in SAFE_METHODS or
196
request.user and request.user.is_authenticated
197
)
198
199
class DjangoModelPermissions(BasePermission):
200
"""
201
Use Django's built-in model permissions system.
202
"""
203
perms_map = {
204
'GET': [],
205
'OPTIONS': [],
206
'HEAD': [],
207
'POST': ['%(app_label)s.add_%(model_name)s'],
208
'PUT': ['%(app_label)s.change_%(model_name)s'],
209
'PATCH': ['%(app_label)s.change_%(model_name)s'],
210
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
211
}
212
213
def get_required_permissions(self, method, model_cls):
214
"""
215
Get required permissions for HTTP method and model.
216
217
Args:
218
method (str): HTTP method
219
model_cls: Django model class
220
221
Returns:
222
list: Required permission strings
223
"""
224
225
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
226
"""
227
Django model permissions with anonymous read access.
228
"""
229
230
class DjangoObjectPermissions(DjangoModelPermissions):
231
"""
232
Use Django's object-level permissions.
233
"""
234
def has_object_permission(self, request, view, obj):
235
"""Check object-level permissions using Django's system."""
236
```
237
238
### Permission Operators
239
240
Combine permissions using logical operators.
241
242
```python { .api }
243
class OperationHolderMixin:
244
"""Mixin providing permission combination operators."""
245
def __and__(self, other):
246
return OperandHolder(AND, self, other)
247
248
def __or__(self, other):
249
return OperandHolder(OR, self, other)
250
251
def __invert__(self):
252
return SingleOperandHolder(NOT, self)
253
254
class SingleOperandHolder(OperationHolderMixin):
255
"""Hold single operand for unary operations like NOT."""
256
def __init__(self, operator_class, op1_class):
257
self.operator_class = operator_class
258
self.op1_class = op1_class
259
260
class OperandHolder(OperationHolderMixin):
261
"""Hold two operands for binary operations like AND/OR."""
262
def __init__(self, operator_class, op1_class, op2_class):
263
self.operator_class = operator_class
264
self.op1_class = op1_class
265
self.op2_class = op2_class
266
267
class AND:
268
"""Logical AND operation for permissions."""
269
def has_permission(self, request, view):
270
return (
271
self.op1.has_permission(request, view) and
272
self.op2.has_permission(request, view)
273
)
274
275
class OR:
276
"""Logical OR operation for permissions."""
277
def has_permission(self, request, view):
278
return (
279
self.op1.has_permission(request, view) or
280
self.op2.has_permission(request, view)
281
)
282
283
class NOT:
284
"""Logical NOT operation for permissions."""
285
def has_permission(self, request, view):
286
return not self.op1.has_permission(request, view)
287
```
288
289
## Usage Examples
290
291
### Basic Authentication Setup
292
293
```python
294
# settings.py
295
REST_FRAMEWORK = {
296
'DEFAULT_AUTHENTICATION_CLASSES': [
297
'rest_framework.authentication.SessionAuthentication',
298
'rest_framework.authentication.TokenAuthentication',
299
],
300
'DEFAULT_PERMISSION_CLASSES': [
301
'rest_framework.permissions.IsAuthenticated',
302
],
303
}
304
305
# View-level configuration
306
from rest_framework.authentication import TokenAuthentication
307
from rest_framework.permissions import IsAuthenticated
308
309
class BookViewSet(ModelViewSet):
310
authentication_classes = [TokenAuthentication]
311
permission_classes = [IsAuthenticated]
312
queryset = Book.objects.all()
313
serializer_class = BookSerializer
314
```
315
316
### Token Authentication Usage
317
318
```python
319
# Create token for user
320
from rest_framework.authtoken.models import Token
321
322
user = User.objects.get(username='testuser')
323
token, created = Token.objects.get_or_create(user=user)
324
print(f"Token: {token.key}")
325
326
# Client request with token
327
import requests
328
329
headers = {'Authorization': f'Token {token.key}'}
330
response = requests.get('http://api.example.com/books/', headers=headers)
331
```
332
333
### Custom Permission Class
334
335
```python
336
from rest_framework.permissions import BasePermission
337
338
class IsOwnerOrReadOnly(BasePermission):
339
"""
340
Custom permission to only allow owners to edit objects.
341
"""
342
def has_object_permission(self, request, view, obj):
343
# Read permissions for any request
344
if request.method in ['GET', 'HEAD', 'OPTIONS']:
345
return True
346
347
# Write permissions only to owner
348
return obj.owner == request.user
349
350
# Usage in view
351
class BookViewSet(ModelViewSet):
352
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
353
queryset = Book.objects.all()
354
serializer_class = BookSerializer
355
```
356
357
### Permission Operators
358
359
```python
360
from rest_framework.permissions import IsAuthenticated, IsAdminUser
361
362
# Combine permissions with operators
363
class MyView(APIView):
364
# Must be admin OR (authenticated AND owner)
365
permission_classes = [IsAdminUser | (IsAuthenticated & IsOwnerOrReadOnly)]
366
367
# Must NOT be anonymous (same as IsAuthenticated)
368
permission_classes = [~AllowAny]
369
```
370
371
### Function-Based View Authentication
372
373
```python
374
from rest_framework.decorators import api_view, authentication_classes, permission_classes
375
from rest_framework.authentication import TokenAuthentication
376
from rest_framework.permissions import IsAuthenticated
377
378
@api_view(['GET'])
379
@authentication_classes([TokenAuthentication])
380
@permission_classes([IsAuthenticated])
381
def protected_view(request):
382
return Response({'message': f'Hello {request.user.username}!'})
383
```
384
385
### Custom Authentication Backend
386
387
```python
388
from rest_framework.authentication import BaseAuthentication
389
from rest_framework.exceptions import AuthenticationFailed
390
from django.contrib.auth import get_user_model
391
392
User = get_user_model()
393
394
class CustomTokenAuthentication(BaseAuthentication):
395
def authenticate(self, request):
396
token = request.META.get('HTTP_X_API_KEY')
397
if not token:
398
return None
399
400
try:
401
user = User.objects.get(api_key=token)
402
except User.DoesNotExist:
403
raise AuthenticationFailed('Invalid API key')
404
405
return (user, token)
406
```
407
408
## Throttling
409
410
Rate limiting classes for controlling request frequency and preventing API abuse. Throttling works alongside authentication and permissions to provide comprehensive access control.
411
412
### Throttling Classes
413
414
```python { .api }
415
class BaseThrottle:
416
"""
417
Base class for all throttling implementations.
418
"""
419
def allow_request(self, request, view):
420
"""
421
Return True if request should be allowed, False otherwise.
422
423
Args:
424
request: DRF Request object
425
view: API view being accessed
426
427
Returns:
428
bool: Whether request is allowed
429
"""
430
431
def wait(self):
432
"""
433
Return recommended number of seconds to wait before next request.
434
435
Returns:
436
int or None: Seconds to wait, or None if not available
437
"""
438
439
class SimpleRateThrottle(BaseThrottle):
440
"""
441
Cache-based rate throttling with configurable rates.
442
"""
443
cache = None # Cache backend
444
timer = None # Time function
445
rate = None # Rate string like "100/day"
446
scope = None # Throttle scope for settings lookup
447
448
def get_cache_key(self, request, view):
449
"""
450
Return unique cache key for throttling.
451
Must be overridden.
452
453
Returns:
454
str or None: Cache key or None to skip throttling
455
"""
456
457
class AnonRateThrottle(SimpleRateThrottle):
458
"""
459
Throttle anonymous requests by IP address.
460
"""
461
scope = 'anon'
462
463
class UserRateThrottle(SimpleRateThrottle):
464
"""
465
Throttle authenticated requests by user.
466
"""
467
scope = 'user'
468
469
class ScopedRateThrottle(SimpleRateThrottle):
470
"""
471
Throttle requests based on view-specific scope.
472
"""
473
scope_attr = 'throttle_scope'
474
```
475
476
### Throttling Configuration
477
478
Configure throttling in Django settings:
479
480
```python
481
# settings.py
482
REST_FRAMEWORK = {
483
'DEFAULT_THROTTLE_CLASSES': [
484
'rest_framework.throttling.AnonRateThrottle',
485
'rest_framework.throttling.UserRateThrottle'
486
],
487
'DEFAULT_THROTTLE_RATES': {
488
'anon': '100/day',
489
'user': '1000/day',
490
'burst': '60/min',
491
'sustained': '1000/day'
492
}
493
}
494
```
495
496
### View-Level Throttling
497
498
```python
499
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
500
501
class BookViewSet(ModelViewSet):
502
throttle_classes = [UserRateThrottle, AnonRateThrottle]
503
queryset = Book.objects.all()
504
serializer_class = BookSerializer
505
506
# Custom throttle scope
507
class ContactViewSet(ModelViewSet):
508
throttle_classes = [ScopedRateThrottle]
509
throttle_scope = 'contacts'
510
queryset = Contact.objects.all()
511
serializer_class = ContactSerializer
512
```
513
514
### Custom Throttle Class
515
516
```python
517
from rest_framework.throttling import UserRateThrottle
518
519
class BurstRateThrottle(UserRateThrottle):
520
scope = 'burst'
521
522
class LoginRateThrottle(AnonRateThrottle):
523
scope = 'login'
524
525
def get_cache_key(self, request, view):
526
if request.user.is_authenticated:
527
return None # Only throttle anonymous users
528
529
ident = self.get_ident(request)
530
return self.cache_format % {
531
'scope': self.scope,
532
'ident': ident
533
}
534
```
535
536
### Function-Based View Throttling
537
538
```python
539
from rest_framework.decorators import api_view, throttle_classes
540
from rest_framework.throttling import UserRateThrottle
541
542
@api_view(['POST'])
543
@throttle_classes([UserRateThrottle])
544
def send_message(request):
545
return Response({'message': 'Message sent'})
546
```
547
548
## Constants and Utilities
549
550
```python { .api }
551
# Safe HTTP methods (read-only)
552
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
553
554
# Utility functions
555
def get_authorization_header(request):
556
"""
557
Extract authorization header from request.
558
559
Args:
560
request: Django request object
561
562
Returns:
563
bytes: Authorization header value
564
"""
565
```