Skip to main content

Explain CSRF protection in a cookie-authenticated API

2026-11-11 16:36:24.781567+00:00
Explain CSRF protection in a cookie-authenticated API

A browser automatically sends applicable session cookies. An endpoint authenticated by those cookies therefore needs a way to reject a forged state-changing request from another site. Django's CSRF checks belong in that request path.

The protected endpoint checks the cookie-authenticated request with its CSRF token and origin policy.

Make a same-origin request with the token

For a page rendered by Django, include a token in the form and read its value when sending JSON:

<form id="note-form">
  {% csrf_token %}
  <label>Note <input name="text" required></label>
  <button type="submit">Save</button>
</form>
const form = document.querySelector("#note-form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const response = await fetch("/api/notes/", {
    method: "POST",
    mode: "same-origin",
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json",
      "X-CSRFToken": form.elements.csrfmiddlewaretoken.value,
    },
    body: JSON.stringify({ text: form.elements.text.value }),
  });
  if (!response.ok) throw new Error("Save was rejected");
});

This uses the token rendered into the page, which also works when the CSRF cookie is intentionally inaccessible to JavaScript. It assumes the view is under Django's CSRF protection and the application has a valid login session.

A React application can keep the same request contract. The token needs an intentional source, such as a server-rendered shell or a dedicated bootstrap response. Do not remove protection simply because the frontend and API use different development ports.

Diagnose the rejected request

Inspect whether the cookie arrived, whether the token header was set and whether the request origin matches the deployment policy. Test a request with a deliberately missing token and confirm it is rejected. Django's test client needs CSRF enforcement enabled when testing this behavior.

Cross-origin deployments add cookie, HTTPS, origin and CORS configuration decisions. Allowing an origin to read responses is not the same control as validating a state-changing cookie-authenticated request. Start from the intended deployment origins instead of adding broad exceptions until the error disappears.

Login can rotate tokens, so refresh the token source after an authentication transition. Avoid caching one user's token into a shared public page response.

Test the actual login and logout transitions in a browser as well. A manually copied token can make a local request pass while hiding a broken session bootstrap.

The administrative workflows in Nirogidhara is related context. Django's CSRF how-to and security overview explain the required request checks.