Auth, Permissions & Throttling
DRF executes three security layers in sequence during the dispatch() phase of every request: authentication → permissions → throttling, before handing the request off to a view method. All three components support both global configuration and per-view overrides, and each can be customized with your own implementation.
Authentication
Authentication is responsible for identifying “who sent this request” and assigning the result to request.user and request.auth. A failed authentication does not immediately reject the request — that is the responsibility of the permissions layer. Instead, the user is marked as an anonymous user (AnonymousUser).
Built-in Authentication Classes
| Class | Description |
|---|---|
SessionAuthentication | Django session-based, suitable for browser clients (includes CSRF checks) |
BasicAuthentication | HTTP Basic Auth with Base64-encoded username/password; for development only |
TokenAuthentication | Token-based authentication; requires the rest_framework.authtoken app |
RemoteUserAuthentication | Delegates to Django’s REMOTE_USER middleware |
Global Configuration
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
]
}Per-View Override
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class MyAPIView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]Custom Authentication Class
Inherit from BaseAuthentication and implement the authenticate() method. The method should return a (user, auth) tuple, raise AuthenticationFailed on failure, or return None to pass control to the next authenticator.
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth.models import User
class TokenHeaderAuthentication(BaseAuthentication):
"""Read a token from the custom X-Token request header and look up the user."""
def authenticate(self, request):
token = request.META.get("HTTP_X_TOKEN")
if not token:
return None # No token present; skip this authenticator
try:
user = User.objects.get(profile__token=token)
except User.DoesNotExist:
raise AuthenticationFailed("Invalid token")
return (user, token) # request.user = user, request.auth = tokenRegister the class in settings or in a view’s authentication_classes for it to take effect.
Token Authentication Quick Setup
DRF ships with a built-in token authentication scheme suited for decoupled front-end/back-end projects:
# settings.py
INSTALLED_APPS = [
...
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}python manage.py migrate # Creates the authtoken tableGenerate a token for a user:
from rest_framework.authtoken.models import Token
token, created = Token.objects.get_or_create(user=user)
print(token.key)The client sends the token in the request header:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4bdjangorestframework-simplejwt), which supports stateless operation, automatic expiry, and token refresh.Permissions
Permissions determine “whether this user is entitled to access this endpoint.” After authentication establishes the user’s identity, permission classes enforce access control.
Built-in Permission Classes
| Class | Description |
|---|---|
AllowAny | Allow access to everyone (default) |
IsAuthenticated | Only authenticated users may access |
IsAdminUser | Only admin users (is_staff=True) may access |
IsAuthenticatedOrReadOnly | Authenticated users can read and write; anonymous users are read-only (GET/HEAD/OPTIONS) |
Global Permission Configuration
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}Per-View Permission Override
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class StudentAPIView(APIView):
permission_classes = [IsAuthenticated]Custom Permission Class
Inherit from BasePermission and implement one or both of the following methods:
has_permission(request, view)— controls access to the view itself (list level).has_object_permission(request, view, obj)— controls access to a specific object (detail level; only triggered when the view callsget_object()).
from rest_framework.permissions import BasePermission, SAFE_METHODS
class IsOwnerOrReadOnly(BasePermission):
"""Only the resource owner may modify or delete; everyone else is read-only."""
def has_object_permission(self, request, view, obj):
# Safe methods (GET, HEAD, OPTIONS) are allowed for everyone
if request.method in SAFE_METHODS:
return True
# Write operations are restricted to the resource creator
return obj.owner == request.user
class IsStaffOrCreator(BasePermission):
"""Admins or creators may write; everyone else is read-only."""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return request.user.is_staff or obj.created_by == request.userMultiple permission classes are evaluated with AND logic — all classes must return True for the request to proceed:
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]Throttling
Throttling controls the rate at which an endpoint can be accessed, preventing abuse such as brute-force attacks, API scraping, or high-concurrency floods.
Built-in Throttle Classes
| Class | Description |
|---|---|
AnonRateThrottle | Anonymous users, identified by IP address; uses the anon rate |
UserRateThrottle | Authenticated users, identified by user ID; uses the user rate |
ScopedRateThrottle | Applies a named scope (throttle_scope) to configure per-view rates |
Global Throttle Configuration
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day', # Anonymous users: max 100 requests per day
'user': '1000/day', # Authenticated users: max 1000 requests per day
}
}Rate format: count/period, where period may be second (s), minute (m), hour (h), or day (d).
Per-View Throttle Configuration
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class LoginView(APIView):
throttle_classes = [UserRateThrottle]Per-View Scoped Throttling (ScopedRateThrottle)
Use ScopedRateThrottle when different endpoints need different rate limits:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.ScopedRateThrottle'],
'DEFAULT_THROTTLE_RATES': {
'login': '5/minute', # Login endpoint: max 5 per minute
'contacts': '100/hour', # Contacts endpoint: max 100 per hour
}
}class LoginView(APIView):
throttle_scope = 'login'
class ContactListView(ListAPIView):
throttle_scope = 'contacts'Custom Throttle Class
from rest_framework.throttling import SimpleRateThrottle
class IPRateThrottle(SimpleRateThrottle):
"""IP-based throttle; rate is read from the `ip` key in settings."""
scope = 'ip'
def get_cache_key(self, request, view):
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request), # Retrieve the client IP
}How the Three Layers Work Together
HTTP Request
│
▼
dispatch()
│
├─ Authentication (authentication_classes)
│ → Sets request.user / request.auth
│
├─ Permissions (permission_classes)
│ → has_permission() passes? Otherwise 403
│
├─ Throttling (throttle_classes)
│ → allow_request() passes? Otherwise 429
│
▼
View method (get / post / put / delete ...)
│
└─ Detail operations: get_object() → has_object_permission()has_object_permission() is only triggered when the view calls self.get_object(). Querying data directly through the ORM bypasses object-level permission checks entirely.