PEP 484 type stubs for Django REST Framework enabling static type checking with comprehensive type definitions for all major DRF components
—
Django REST Framework provides a comprehensive authentication and permissions system with full type safety. The type stubs enable precise type checking for security configurations, custom authentication backends, and permission logic.
class BaseAuthentication:
"""Base class for all authentication backends."""
def authenticate(self, request: Request) -> tuple[Any, Any] | None:
"""
Authenticate the request and return a two-tuple of (user, token).
Returns:
tuple[Any, Any] | None: (user, auth) or None if not authenticated
"""
...
def authenticate_header(self, request: Request) -> str | None:
"""
Return a string to be used as the value of the WWW-Authenticate
header in a 401 Unauthenticated response.
Returns:
str | None: Authentication header value or None
"""
...class SessionAuthentication(BaseAuthentication):
"""Django session-based authentication."""
def authenticate(self, request: Request) -> tuple[Any, Any] | None: ...
def authenticate_header(self, request: Request) -> str | None: ...
def enforce_csrf(self, request: Request) -> None: ...class BasicAuthentication(BaseAuthentication):
"""HTTP Basic authentication against username/password."""
www_authenticate_realm: str
def authenticate(self, request: Request) -> tuple[Any, Any] | None: ...
def authenticate_header(self, request: Request) -> str: ...
def authenticate_credentials(
self,
userid: str,
password: str,
request: Request | None = None
) -> tuple[Any, None]: ...Parameters:
www_authenticate_realm: str - Realm for WWW-Authenticate headeruserid: str - Username from Basic auth headerpassword: str - Password from Basic auth headerrequest: Request | None - Current request objectclass TokenAuthentication(BaseAuthentication):
"""Token-based authentication using DRF tokens."""
keyword: str # Default: 'Token'
model: type[Model] | None
def authenticate(self, request: Request) -> tuple[Any, Any] | None: ...
def authenticate_header(self, request: Request) -> str: ...
def authenticate_credentials(self, key: str) -> tuple[Any, Any]: ...
def get_model(self) -> type[Model]: ...Parameters:
keyword: str - Token keyword in Authorization header (default: 'Token')model: type[Model] | None - Token model classkey: str - Token key from Authorization headerclass RemoteUserAuthentication(BaseAuthentication):
"""Authentication for users authenticated by external systems."""
header: str # Default: 'REMOTE_USER'
def authenticate(self, request: Request) -> tuple[Any, Any] | None: ...
def clean_username(self, username: str) -> str: ...Parameters:
header: str - HTTP header containing remote usernamedef get_authorization_header(request: Request) -> bytes:
"""
Extract the authorization header from the request.
Args:
request: The DRF Request object
Returns:
bytes: Authorization header value
"""
...class CSRFCheck(CsrfViewMiddleware):
"""CSRF protection middleware for DRF."""
def reject(self, request: HttpRequest, reason: str) -> None: ...
def process_view(
self,
request: HttpRequest,
callback: Callable,
callback_args: tuple,
callback_kwargs: dict[str, Any]
) -> HttpResponse | None: ...class BasePermission:
"""Base class for all permission checks."""
def has_permission(self, request: Request, view: APIView) -> bool:
"""
Return True if permission is granted for the view.
Args:
request: The DRF request object
view: The view being accessed
Returns:
bool: True if permission granted
"""
...
def has_object_permission(
self,
request: Request,
view: APIView,
obj: Any
) -> bool:
"""
Return True if permission is granted for the object.
Args:
request: The DRF request object
view: The view being accessed
obj: The object being accessed
Returns:
bool: True if permission granted
"""
...class AllowAny(BasePermission):
"""Allow unrestricted access, regardless of authentication."""
pass
class IsAuthenticated(BasePermission):
"""Allow access only to authenticated users."""
pass
class IsAdminUser(BasePermission):
"""Allow access only to admin users."""
pass
class IsAuthenticatedOrReadOnly(BasePermission):
"""Allow read-only access to any user, write access to authenticated users."""
passclass DjangoModelPermissions(BasePermission):
"""Permission class that ties into Django's model permissions system."""
authenticated_users_only: bool
perms_map: dict[str, list[str]]
def get_required_permissions(
self,
method: str,
model_cls: type[Model]
) -> list[str]: ...
def get_required_object_permissions(
self,
method: str,
model_cls: type[Model]
) -> list[str]: ...
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
"""Django model permissions with anonymous read-only access."""
authenticated_users_only: bool
class DjangoObjectPermissions(DjangoModelPermissions):
"""Django object-level permissions."""
passParameters:
authenticated_users_only: bool - Require authentication for all accessperms_map: dict[str, list[str]] - Mapping of HTTP methods to required permissionsclass AND:
"""Logical AND operator for permissions."""
def __init__(self, op1: BasePermission, op2: BasePermission) -> None: ...
def has_permission(self, request: Request, view: APIView) -> bool: ...
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool: ...
class OR:
"""Logical OR operator for permissions."""
def __init__(self, op1: BasePermission, op2: BasePermission) -> None: ...
def has_permission(self, request: Request, view: APIView) -> bool: ...
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool: ...
class NOT:
"""Logical NOT operator for permissions."""
def __init__(self, op1: BasePermission) -> None: ...
def has_permission(self, request: Request, view: APIView) -> bool: ...
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool: ...# Combine permissions with operators
permission_classes = [IsAuthenticated & (IsOwner | IsAdmin)]
permission_classes = [IsAuthenticated & ~IsBlocked]
permission_classes = [(IsAuthenticated & IsOwner) | IsAdmin]from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth.models import User
class APIKeyAuthentication(BaseAuthentication):
"""Custom API key authentication."""
def authenticate(self, request: Request) -> tuple[User, str] | None:
api_key = request.META.get('HTTP_X_API_KEY')
if not api_key:
return None
try:
# Look up user by API key
profile = UserProfile.objects.select_related('user').get(api_key=api_key)
return (profile.user, api_key)
except UserProfile.DoesNotExist:
raise AuthenticationFailed('Invalid API key')
def authenticate_header(self, request: Request) -> str:
return 'X-API-Key'
class JWTAuthentication(BaseAuthentication):
"""JWT token authentication."""
def authenticate(self, request: Request) -> tuple[User, dict[str, Any]] | None:
auth_header = request.META.get('HTTP_AUTHORIZATION')
if not auth_header or not auth_header.startswith('Bearer '):
return None
token = auth_header[7:] # Remove 'Bearer ' prefix
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id=payload['user_id'])
return (user, payload)
except (jwt.DecodeError, jwt.ExpiredSignatureError, User.DoesNotExist):
raise AuthenticationFailed('Invalid token')
def authenticate_header(self, request: Request) -> str:
return 'Bearer'from rest_framework.permissions import BasePermission
class IsOwnerOrReadOnly(BasePermission):
"""Allow owners to edit, others to read only."""
def has_object_permission(
self,
request: Request,
view: APIView,
obj: Any
) -> bool:
# Read permissions for any request
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
# Write permissions only to owner
return obj.owner == request.user
class IsSuperUserOrOwner(BasePermission):
"""Allow superusers or object owners full access."""
def has_permission(self, request: Request, view: APIView) -> bool:
return request.user and request.user.is_authenticated
def has_object_permission(
self,
request: Request,
view: APIView,
obj: Any
) -> bool:
return (
request.user.is_superuser or
getattr(obj, 'user', None) == request.user or
getattr(obj, 'owner', None) == request.user
)
class IsInGroupOrReadOnly(BasePermission):
"""Allow group members to edit, others to read only."""
def __init__(self, group_name: str) -> None:
self.group_name = group_name
def has_permission(self, request: Request, view: APIView) -> bool:
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
return (
request.user.is_authenticated and
request.user.groups.filter(name=self.group_name).exists()
)
class HasModelPermission(BasePermission):
"""Check Django model permissions."""
def __init__(self, model: type[Model], action: str) -> None:
self.model = model
self.action = action
def has_permission(self, request: Request, view: APIView) -> bool:
if not request.user.is_authenticated:
return False
permission_name = f"{self.model._meta.app_label}.{self.action}_{self.model._meta.model_name}"
return request.user.has_perm(permission_name)from rest_framework.views import APIView
from rest_framework.authentication import (
SessionAuthentication,
TokenAuthentication,
BasicAuthentication
)
from rest_framework.permissions import IsAuthenticated, IsAdminUser
class MultiAuthView(APIView):
"""View with multiple authentication methods."""
authentication_classes = [
SessionAuthentication,
TokenAuthentication,
BasicAuthentication
]
permission_classes = [IsAuthenticated]
def get(self, request: Request) -> Response:
# User is guaranteed to be authenticated
return Response({
'user': request.user.username,
'auth_method': type(request.auth).__name__
})
class AdminOnlyView(APIView):
"""View restricted to admin users."""
authentication_classes = [SessionAuthentication, TokenAuthentication]
permission_classes = [IsAuthenticated, IsAdminUser]
def get(self, request: Request) -> Response:
# User is guaranteed to be authenticated admin
return Response({'message': 'Admin access granted'})
class DynamicPermissionView(APIView):
"""View with dynamic permission checking."""
def get_permissions(self) -> list[BasePermission]:
if self.request.method == 'GET':
permission_classes = [AllowAny]
elif self.request.method == 'POST':
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAuthenticated, IsAdminUser]
return [permission() for permission in permission_classes]from rest_framework import viewsets
from rest_framework.decorators import action
class BookViewSet(viewsets.ModelViewSet[Book]):
"""ViewSet with action-specific permissions."""
queryset = Book.objects.all()
serializer_class = BookSerializer
def get_permissions(self) -> list[BasePermission]:
"""Different permissions for different actions."""
if self.action == 'list':
permission_classes = [AllowAny]
elif self.action in ['create', 'update', 'partial_update']:
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
elif self.action == 'destroy':
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly, IsAdminUser]
else:
permission_classes = [IsAuthenticated]
return [permission() for permission in permission_classes]
def get_authenticators(self) -> list[BaseAuthentication]:
"""Different authentication for different actions."""
if self.action in ['list', 'retrieve']:
# Public actions allow multiple auth methods
return [
SessionAuthentication(),
TokenAuthentication(),
BasicAuthentication()
]
else:
# Sensitive actions require token or session auth only
return [
SessionAuthentication(),
TokenAuthentication()
]
@action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
def my_books(self, request: Request) -> Response:
"""Get current user's books."""
books = Book.objects.filter(owner=request.user)
serializer = self.get_serializer(books, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'], permission_classes=[IsAuthenticated, IsOwnerOrReadOnly])
def publish(self, request: Request, pk: str | None = None) -> Response:
"""Publish a book."""
book = self.get_object()
book.is_published = True
book.published_date = timezone.now()
book.save()
return Response({'status': 'published'})from rest_framework.decorators import (
api_view,
authentication_classes,
permission_classes
)
@api_view(['GET'])
@permission_classes([AllowAny])
def public_endpoint(request: Request) -> Response:
"""Publicly accessible endpoint."""
return Response({'message': 'Public data'})
@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def protected_endpoint(request: Request) -> Response:
"""Protected endpoint requiring token auth."""
return Response({
'message': f'Hello {request.user.username}',
'user_id': request.user.id
})
@api_view(['GET', 'POST'])
@authentication_classes([SessionAuthentication, TokenAuthentication])
@permission_classes([IsAuthenticated])
def user_profile(request: Request) -> Response:
"""User profile endpoint with multiple auth methods."""
if request.method == 'GET':
serializer = UserSerializer(request.user)
return Response(serializer.data)
elif request.method == 'POST':
serializer = UserSerializer(request.user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)from rest_framework.authtoken.models import Token
# Token model fields
class Token(models.Model):
key: models.CharField
user: models.OneToOneField
created: models.DateTimeField
@classmethod
def generate_key(cls) -> str: ...
class TokenProxy(Token):
"""Proxy model for Token."""
class Meta:
proxy = Truefrom rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken import views
class ObtainAuthToken(APIView):
"""View for obtaining authentication tokens."""
serializer_class: type[Serializer]
def post(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
def get_serializer_context(self) -> dict[str, Any]: ...
def get_serializer(self, *args: Any, **kwargs: Any) -> Serializer: ...
# Pre-configured view instance
obtain_auth_token: Callable[..., Response]from rest_framework.authtoken.serializers import AuthTokenSerializer
class AuthTokenSerializer(serializers.Serializer):
"""Serializer for token authentication."""
username: serializers.CharField
password: serializers.CharField
token: serializers.CharField
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]: ...# Production-ready authentication setup
class SecureAPIView(APIView):
"""Secure API view configuration."""
authentication_classes = [
TokenAuthentication, # Primary auth method
SessionAuthentication # Fallback for web UI
]
permission_classes = [IsAuthenticated]
def dispatch(self, request: Request, *args: Any, **kwargs: Any) -> Response:
# Add security headers
response = super().dispatch(request, *args, **kwargs)
response['X-Content-Type-Options'] = 'nosniff'
response['X-Frame-Options'] = 'DENY'
response['X-XSS-Protection'] = '1; mode=block'
return responsefrom rest_framework.throttling import UserRateThrottle
class RateLimitedPermission(BasePermission):
"""Permission that includes rate limiting."""
def has_permission(self, request: Request, view: APIView) -> bool:
# Check base permission
if not request.user.is_authenticated:
return False
# Apply rate limiting
throttle = UserRateThrottle()
if not throttle.allow_request(request, view):
return False
return TrueThis comprehensive authentication and permissions system provides type-safe security controls with full mypy support, enabling confident implementation of authentication backends, permission logic, and access control patterns.
Install with Tessl CLI
npx tessl i tessl/pypi-djangorestframework-stubs