from importlib import import_module from typing import Sequence, Type, TYPE_CHECKING from django.conf import settings from django.contrib import auth from rest_framework.authentication import BaseAuthentication, SessionAuthentication from rest_framework.permissions import BasePermission, IsAuthenticated def get_auth_header(headers): value = headers.get("Authorization") if not value: return None auth_type, auth_value = value.split()[:2] return auth_type, auth_value class SessionAsHeaderAuthentication(BaseAuthentication): """ In case we are dealing with issues like Safari not supporting SameSite=None, And the client passes the session as Authorization header: Authorization: Session Run the standard Django auth & try obtaining user. """ def authenticate(self, request): auth_header = get_auth_header(request.headers) if auth_header is None: return None auth_type, auth_value = auth_header if auth_type != "Session": return None engine = import_module(settings.SESSION_ENGINE) SessionStore = engine.SessionStore session_key = auth_value request.session = SessionStore(session_key) user = auth.get_user(request) return user, None class CsrfExemptedSessionAuthentication(SessionAuthentication): """ DRF SessionAuthentication is enforcing CSRF, which may be problematic. That's why we want to make sure we are exempting any kind of CSRF checks for APIs. """ def enforce_csrf(self, request): return if TYPE_CHECKING: # This is going to be resolved in the stub library # https://github.com/typeddjango/djangorestframework-stubs/ from rest_framework.permissions import _PermissionClass PermissionClassesType = Sequence[_PermissionClass] else: PermissionClassesType = Sequence[Type[BasePermission]] class ApiAuthMixin: authentication_classes: Sequence[Type[BaseAuthentication]] = [ CsrfExemptedSessionAuthentication, SessionAsHeaderAuthentication, ] permission_classes: PermissionClassesType = (IsAuthenticated,)