Cookie auth¶
Use cookie authentication for browser-based apps where you want the browser to manage tokens automatically.
How it works¶
- Login sets three cookies: access token (HttpOnly), refresh token (HttpOnly), and CSRF token (readable by JS)
- GET requests work automatically — the browser sends cookies, no CSRF needed
- POST/PUT/PATCH/DELETE requests require the CSRF token in the
X-CSRF-Tokenheader - Refresh and logout work via cookies — no need to manage tokens in JS
Login¶
const res = await fetch("/auth/login", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
After login, the browser stores the cookies automatically. You can ignore the JSON response body if you only use cookie transport.
Reading protected data (GET)¶
// Cookies are sent automatically — no extra headers needed
const res = await fetch("/auth/me", { credentials: "include" });
const user = await res.json();
Mutating requests (POST/PUT/PATCH/DELETE)¶
For any non-GET request to a protected route, you must include the CSRF token:
function getCsrfToken() {
return document.cookie
.split("; ")
.find(row => row.startsWith("bf_csrf_token="))
?.split("=")[1];
}
const res = await fetch("/some-protected-endpoint", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": getCsrfToken(),
},
body: JSON.stringify({ ... }),
});
Refreshing¶
const res = await fetch("/auth/refresh", {
method: "POST",
credentials: "include",
headers: { "X-CSRF-Token": getCsrfToken() },
});
// New cookies are set automatically in the response
After refreshing, re-read the CSRF cookie — it changes on each refresh.
Logout¶
await fetch("/auth/logout", {
method: "POST",
credentials: "include",
headers: { "X-CSRF-Token": getCsrfToken() },
});
// Cookies are cleared by the response
Local development¶
By default, cookies have Secure=True, which means they only work over HTTPS. For local development, disable this:
Cross-domain cookies¶
If your API and frontend are on different subdomains (e.g. api.example.com and app.example.com):