Django: использование AJAX для входа в систему, как (следует) обновить CSRF-токен формы?

Контекст:

В проекте компании, над которым я работаю, используется AJAX для входа пользователя в систему для выполнения определенных действий на веб-странице. Эту веб-страницу можно просматривать без входа в систему, но определенные действия требуют аутентификации (например, обновление настроек оборудования). Я хочу сохранить форму, в которой пользователь сейчас находится, чтобы ему не пришлось повторно вводить свои изменения (что я сейчас и делаю, скрывая форму и накладывая на нее форму "login"), поэтому обновление страницы - это не вариант или, по крайней мере, крайняя мера.

Вопрос:

После завершения входа в систему CSRF-токен первой формы стал недействительным и его необходимо обновить. Это не вопрос "как добавить CSRF-токен в AJAX-запрос", а вопрос "как его обновить".

Django выдает следующую ошибку после входа в систему: Forbidden (CSRF token from POST incorrect.)

Вот что я уже пробовал:

Отправка нового CSRF-токена для формы после входа в систему (на стороне сервера):

form = AuthenticationForm(data=request.POST or None)
if form.is_valid():
    # Login the user
    user = authenticate(
        username=form.cleaned_data.get("username"),
        password=form.cleaned_data.get("password"),
    )
    if user is not None:
        login(request, user)
        # Neither of these have worked
        # return JsonResponse(dict(success=True, csrfToken=request.POST.get("csrfmiddlewaretoken")))
        # return JsonResponse(dict(success=True, csrfToken=str(csrf(request)["csrf_token"])))

На клиенте:

// From Django's AJAX documentation
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

...

// Submit the login information and then update the page's CRSF token
// This is first and is run when the user is anonymous
$.ajax({
    url: $("#login-form").attr("action"),
    type: "POST",
    data: data,
    headers: {
        'X-CSRFToken': getCookie("csrftoken"),
    },
    processData: false,
    contentType: false,
    success: function(data) {
        // Update the new CSRF token
        csrfToken = data["csrfToken"];
        ...

// Submit the settings form with (hopefully) an updated CSRF token
// This is second and is run when the user is authenticated
// Spoiler: it doesn't work
$.ajax({
    url: $("#settings-form").attr("action"),
    type: "POST",
    data: data,
    headers: {
        'X-CSRFToken': getCookie("csrftoken"),
    },
    processData: false,
    contentType: false,

Я на 100% признаю, что у меня нет большого опыта работы с AJAX или CSRF-токенами, так что если я делаю что-то не так, то это из-за моего недостатка опыта. Я не видел никаких других сообщений - нигде - относительно проблемы, подобной этой (99% были проблемой "как мне добавить CSRF токен"), но я готов посмотреть на любые, которые, по вашему мнению, могут быть релевантными.

Заранее благодарю вас за уделенное время.

Жюль

Благодаря @thebjorn в комментариях к моему посту выше, я смог определить, что я не обновил CSRF-токен на форме.

К сожалению, по какой-то причине, которую, я уверен, знает кто-то более умный, чем я, использование CSRF-токена, который я отправляю обратно клиенту в POST-запросе от формы входа, продолжает вызывать эту проблему. В качестве обходного пути я использую API fetch и просто извлекаю новый из Django. Это, похоже, работает.

На будущее для тех, кто найдет этот пост, вот обновленный код AJAX для входа в систему, приведенный выше:

// Store the csrf token here and update this variable
// upon user login
var csrfToken = getCookie("csrftoken");

// Submit the login information and then update the page's CRSF token
// This is first and is run when the user is anonymous
$.ajax({
    url: $("#login-form").attr("action"),
    type: "POST",
    data: data,
    headers: {
        'X-CSRFToken': csrfToken,
    },
    processData: false,
    contentType: false,
    success: function(data) {
        // Update the new CSRF token
        fetch(window.location.href, {
            headers: {
                "X-CSRFTOKEN-ONLY": "True"  // Custom header
            }
        })
        .then(response=>response.json())
        .then(data_ => {
            console.log(data_);
            csrfToken = data_["csrftoken"];
            $("#settings-form [name=csrfmiddlewaretoken]").val(data_["csrftoken"]);
        ...

Снова спасибо @thebjorn и @YevgeniyKosmak за помощь.

Вернуться на верх