# SessionAuthentication enforcing CSRF on @action decorator when permission\_classes is overridden despite JWTAuthentication set on viewset

**URL:** <https://forum.djangoproject.com/t/sessionauthentication-enforcing-csrf-on-action-decorator-when-permission-classes-is-overridden-despite-jwtauthentication-set-on-viewset/45246>\
**Category:** Mystery Errors\
**Created:** [June 8, 2026, 3:41pm UTC](https://forum.djangoproject.com/t/sessionauthentication-enforcing-csrf-on-action-decorator-when-permission-classes-is-overridden-despite-jwtauthentication-set-on-viewset/45246 "2026-06-08T15:41:55Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Nasirsuave](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nasirsuave/32/32596_2.png) [@Nasirsuave](https://forum.djangoproject.com/u/Nasirsuave)\
**Post date:** [June 8, 2026, 3:41pm UTC](https://forum.djangoproject.com/t/sessionauthentication-enforcing-csrf-on-action-decorator-when-permission-classes-is-overridden-despite-jwtauthentication-set-on-viewset/45246/1 "2026-06-08T15:41:55Z")

</div>

**Body:**

> **Environment:**
> 
> - Django 5.x
> - Django REST Framework
> - djangorestframework-simplejwt
> - React (Vite) frontend using JWT Bearer tokens
> 
> **Problem:**
> 
> I have a `ModelViewSet` with `authentication_classes = [JWTAuthentication]` set at the class level. When I add a custom `@action` decorator and override `permission_classes`, the endpoint starts returning `403 Forbidden (CSRF cookie not set)` on unsafe methods (PATCH, POST).
> 
> **Expected behavior:** The action should inherit `authentication_classes` from the parent viewset.
> 
> **Actual behavior:** Overriding `permission_classes` on the action causes `authentication_classes` to stop inheriting from the viewset and fall back to DRF’s global defaults, which include `SessionAuthentication`. That class enforces CSRF on unsafe methods, causing the 403 even though the request has a valid Bearer token.
> 
> **Workaround:** Explicitly setting `authentication_classes=[JWTAuthentication]` on every `@action` that overrides `permission_classes` fixes it — but this feels like a bug or at least a surprising gotcha worth documenting.
> 
> **Question:** Is this intended DRF behavior? If so, where is it documented?

code:

```auto
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticate

from applications.models import JobApplication
from applications.serializers.applications import JobApplicationSerializer

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.decorators import action
from rest_framework.response import Response

class JobApplicationViewSet(ModelViewSet):
    serializer_class = JobApplicationSerializer
    permission_classes = [IsAuthenticated]
    authentication_classes = [JWTAuthentication]

    def get_queryset(self):
        return JobApplication.objects.filter(
            employee=self.request.user.employee
        )

    def perform_create(self, serializer):
        serializer.save(
            employee=self.request.user.employee
        )

    @action(
        detail=True,
        methods=["patch"],
        permission_classes=[IsAuthenticated],
        authentication_classes=[JWTAuthentication],
    )

    def add_stage(self, request, pk=None):
        try:
            application = JobApplication.objects.get(pk=pk)
        except JobApplication.DoesNotExist:
            return Response({"error": "Not found"}, status=404)

        new_stage = request.data.get("stage")
        if not new_stage:
            return Response({"error": "Stage is required"}, status=400)

        history = application.status or []
        history.append(new_stage)
        application.status = history
        application.save()
        return Response({"status": application.status})

```

browser client:

```auto
    const res = await fetch(
      `http://localhost:8000/api/job-applications/${candidateId}/add-stage/`,
      {
        method: "PATCH",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({
          stage,
        }),
      }

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [June 9, 2026, 1:59am UTC](https://forum.djangoproject.com/t/sessionauthentication-enforcing-csrf-on-action-decorator-when-permission-classes-is-overridden-despite-jwtauthentication-set-on-viewset/45246/2 "2026-06-09T01:59:32Z")

</div>

Welcome @Nasirsuave !

Side Note: When posting code here, enclose the code between lines of three  
backtick - ` characters. This means you’ll have a line of ```, then your code,  
then another line of ```. This forces the forum software to keep your code  
properly formatted. (I have taken the liberty of correcting your original post.  
Please remember to do this in the future.)  
Also, I would suggest that you not post your own original material as a “quote” by prefixing each line with `>`
