CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework-stubs

PEP 484 type stubs for Django REST Framework enabling static type checking with comprehensive type definitions for all major DRF components

Pending
Overview
Eval results
Files

authentication-permissions.mddocs/

Authentication & Permissions

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.

Authentication Classes

BaseAuthentication

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
        """
        ...

SessionAuthentication

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: ...

BasicAuthentication

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 header
  • userid: str - Username from Basic auth header
  • password: str - Password from Basic auth header
  • request: Request | None - Current request object

TokenAuthentication

class 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 class
  • key: str - Token key from Authorization header

RemoteUserAuthentication

class 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 username

Authentication Utilities

Helper Functions

def get_authorization_header(request: Request) -> bytes:
    """
    Extract the authorization header from the request.
    
    Args:
        request: The DRF Request object
        
    Returns:
        bytes: Authorization header value
    """
    ...

CSRF Protection

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: ...

Permission Classes

BasePermission

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
        """
        ...

Built-in Permission Classes

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."""
    pass

Django Model Permissions

class 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."""
    pass

Parameters:

  • authenticated_users_only: bool - Require authentication for all access
  • perms_map: dict[str, list[str]] - Mapping of HTTP methods to required permissions

Permission Operators

Logical Operators

class 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: ...

Permission Composition

# Combine permissions with operators
permission_classes = [IsAuthenticated & (IsOwner | IsAdmin)]
permission_classes = [IsAuthenticated & ~IsBlocked]
permission_classes = [(IsAuthenticated & IsOwner) | IsAdmin]

Usage Examples

Custom Authentication Backend

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'

Custom Permission Classes

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)

View-Level Authentication Configuration

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]

ViewSet Authentication & Permissions

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'})

Permission Decorators

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)

Token Authentication Setup

Token Model

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 = True

Token Views

from 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]

Token Serializer

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]: ...

Security Best Practices

Secure Authentication Configuration

# 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 response

Rate Limiting with Permissions

from 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 True

This 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

docs

authentication-permissions.md

exceptions-status.md

fields-relations.md

index.md

pagination-filtering.md

requests-responses.md

routers-urls.md

serializers.md

views-viewsets.md

tile.json