CSRF incompatibility with custom CSRF_HEADER_NAME (still reproducible after #303 fix)
Related issue
Summary
django-admin-sortable2 still fails CSRF validation in Django projects that use:
CSRF_USE_SESSIONS = True, and
- a custom CSRF header name via
CSRF_HEADER_NAME (for example HTTP_X_AUTH_CSRFTOKEN).
The 2.0.2 fix for #303 addressed CSRF token retrieval from the hidden form input (works), but the JS request header key is still hardcoded to X-CSRFToken, which breaks when Django expects a custom header name.
Environment
- Django: 5.2.x
- django-admin-sortable2: 2.3.1
- CSRF settings (example):
CSRF_USE_SESSIONS = True
CSRF_HEADER_NAME = "HTTP_X_AUTH_CSRFTOKEN"
- project frontend convention header:
X-Auth-CsrfToken
Reproduction
- Enable
SortableAdminMixin on a Django admin model.
- Configure Django with:
CSRF_USE_SESSIONS = True
- non-default
CSRF_HEADER_NAME (example above)
- Open admin changelist and drag-reorder rows.
- Observe POST to:
/admin/<app>/<model>/adminsortable2_update/
- Response:
403 Forbidden (CSRF token missing/incorrect)
Why this still happens after #303
Current JS correctly extracts token from hidden input, but appends it under a fixed header name.
File:
adminsortable2/static/adminsortable2/js/adminsortable2.js
Code path:
ListSortable.headers getter
Current logic (simplified):
const inputElement = this.tableBody.closest("form")?.querySelector('input[name="csrfmiddlewaretoken"]');
if (inputElement) {
headers.append("X-CSRFToken", inputElement.value);
}
This ignores Django’s configurable CSRF_HEADER_NAME.
Equivalent behavior is present in adminsortable2.min.js.
Evidence
In a project with:
CSRF_USE_SESSIONS = True
CSRF_HEADER_NAME = "HTTP_X_AUTH_CSRFTOKEN"
Django expects request header X-Auth-CsrfToken (mapped internally to HTTP_X_AUTH_CSRFTOKEN), but sortable2 sends X-CSRFToken, so CSRF middleware rejects the request.
Expected behavior
django-admin-sortable2 should respect Django-configured CSRF header names, not only default X-CSRFToken.
Reliable fix proposal
Support configurable CSRF header key in sortable2 AJAX requests.
Pass client-side CSRF header name from backend/template config into JS (alongside update_url) and use:
headers.append(config.csrf_header || "X-CSRFToken", token);
This keeps backward compatibility and supports custom Django settings.
Why this should be supported
Django intentionally allows custom CSRF header names. Third-party admin JS should interoperate with Django’s CSRF settings to avoid requiring project-specific middleware hacks or disabling established CSRF conventions.
CSRF incompatibility with custom
CSRF_HEADER_NAME(still reproducible after #303 fix)Related issue
Summary
django-admin-sortable2still fails CSRF validation in Django projects that use:CSRF_USE_SESSIONS = True, andCSRF_HEADER_NAME(for exampleHTTP_X_AUTH_CSRFTOKEN).The 2.0.2 fix for #303 addressed CSRF token retrieval from the hidden form input (works), but the JS request header key is still hardcoded to
X-CSRFToken, which breaks when Django expects a custom header name.Environment
CSRF_USE_SESSIONS = TrueCSRF_HEADER_NAME = "HTTP_X_AUTH_CSRFTOKEN"X-Auth-CsrfTokenReproduction
SortableAdminMixinon a Django admin model.CSRF_USE_SESSIONS = TrueCSRF_HEADER_NAME(example above)/admin/<app>/<model>/adminsortable2_update/403 Forbidden(CSRF token missing/incorrect)Why this still happens after #303
Current JS correctly extracts token from hidden input, but appends it under a fixed header name.
File:
adminsortable2/static/adminsortable2/js/adminsortable2.jsCode path:
ListSortable.headersgetterCurrent logic (simplified):
This ignores Django’s configurable CSRF_HEADER_NAME.
Equivalent behavior is present in adminsortable2.min.js.
Evidence
In a project with:
Django expects request header X-Auth-CsrfToken (mapped internally to HTTP_X_AUTH_CSRFTOKEN), but sortable2 sends X-CSRFToken, so CSRF middleware rejects the request.
Expected behavior
django-admin-sortable2should respect Django-configured CSRF header names, not only defaultX-CSRFToken.Reliable fix proposal
Support configurable CSRF header key in sortable2 AJAX requests.
Pass client-side CSRF header name from backend/template config into JS (alongside
update_url) and use:This keeps backward compatibility and supports custom Django settings.
Why this should be supported
Django intentionally allows custom CSRF header names. Third-party admin JS should interoperate with Django’s CSRF settings to avoid requiring project-specific middleware hacks or disabling established CSRF conventions.