Dj-rest-auth access token not sent from react font-end to django back-end
I'm making an SPA with a React front-end and a Django back-end. For user authentication, I'm using dj_rest_auth
, which wraps allauth
to provide a REST API interface.
dj-rest-auth provides a "login" endpoint that sends back access and refresh token cookies. It also provides a "user" endpoint that returns the username, email, first name and last name.
Both work as expected in my API client (Bruno), BUT, I can't get the "user" API to work in my front-end code. My browser doesn't send cookies to the server.
Here are screenshots from Bruno. Notice how the cookies are saved, and fetching the user's info works:
Here's my login request from my React app and the set-cookie headers (aka response cookies):
// ...
fetch(BACKEND_URL + "/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: username,
password: password,
}),
})
.then(async (response) => { //...
study_stream_auth_cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzM3MjM3MTEyLCJpYXQiOjE3MzcyMzM1MTIsImp0aSI6IjNjZDc4ZjI3ZWYyNTQwMzI4MmVmNjAwODRiMjcxYzU0IiwidXNlcl9pZCI6NDB9.JK-SKSGMWuTBFm4JFk4-T8RqNEf2vwANrS4-h7P9UMY; expires=Sat, 18 Jan 2025 21:51:52 GMT; Max-Age=3600; Path=/; SameSite=Lax
study_stream_refresh_cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTc0NTAwOTUxMiwiaWF0IjoxNzM3MjMzNTEyLCJqdGkiOiJhMTljYjg1ZDI5Zjk0NTQyOWUzMjM2ODdmY2IxYzQ0YiIsInVzZXJfaWQiOjQwfQ.xvfB61xkq9NUzQSZeAJmXSYaFrYcSfrsJ9QX2u635LU; expires=Fri, 18 Apr 2025 20:51:52 GMT; Max-Age=7776000; Path=/; SameSite=Lax
sessionid=qt2yxaiitghqavr74xvvl8icjlckq6by; expires=Sat, 01 Feb 2025 20:51:52 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=None; Secure
And here's my "user" GET request. Notice that the only cookie sent here is csrftoken
which is unrelated.
const response = await fetch(BACKEND_URL + "/api/auth/user/", {
method: "GET",
credentials: 'include',
});
const data = await response.json();
console.log(data); // debug
The console logs this, which is exactly what I get in Bruno when I clear my cookies:
{detail: 'Authentication credentials were not provided.'}
I've already added these flags to Django to try and fix this with no success:
CORS_ALLOW_CREDENTIALS = True
# SESSION_COOKIE_SAMESITE = 'None'
# vv pulled from https://stackoverflow.com/a/63590219/14751074
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
JWT_AUTH_SAMESITE = None
Here's some more configs and info from Django and React:
- The React front-end is running on
http://localhost:5173/
.
// vars.ts. imported in all files using "BACKEND_URL".
export const BACKEND_URL = "http://localhost:8000"
// same address as django
// settings.py in django
"""
Django settings for backend project.
Generated by 'django-admin startproject' using Django 5.1.2.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
from pathlib import Path
import os
from datetime import timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-v(lj2mjma-76y7)@f23yan9hoeo5z+$mn#no4j7-z)o_gg5(0("
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
LOGIN_URL = "login" # Redirect here if user is not logged in
# LOGIN_REDIRECT_URL = 'notes_list' # Redirect here after successful login
LOGOUT_REDIRECT_URL = "login" # Redirect here after logout
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"notes",
"rest_framework_simplejwt",
"rest_framework",
"rest_framework.authtoken",
"dj_rest_auth",
"allauth",
"allauth.account",
"allauth.socialaccount",
"dj_rest_auth.registration",
"corsheaders",
]
# CORS_ALLOWED_ORIGINS = [
# "http://localhost:5173",
# ]
CORS_ALLOW_ALL_ORIGINS = True
# trying to make browser send my damn cookies!!!!!!
# src: https://pypi.org/project/django-cors-headers/
CORS_ALLOW_CREDENTIALS = True
# SESSION_COOKIE_SAMESITE = 'None'
# vv pulled from https://stackoverflow.com/a/63590219/14751074
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
JWT_AUTH_SAMESITE = None
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
#'django.middleware.csrf.CsrfViewMiddleware',
"allauth.account.middleware.AccountMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"corsheaders.middleware.CorsMiddleware",
]
ROOT_URLCONF = "backend.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "backend.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/"
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(hours=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=90),
}
REST_AUTH = {
"USE_JWT": True,
"JWT_AUTH_COOKIE": "study_stream_auth_cookie",
"JWT_AUTH_REFRESH_COOKIE": "study_stream_refresh_cookie",
"JWT_AUTH_HTTPONLY": False, # Makes sure refresh token is sent
}
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"dj_rest_auth.jwt_auth.JWTCookieAuthentication",
),
}
ACCOUNT_EMAIL_VERIFICATION = "none"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = True
How can I get jwt-cookie-based authentication working?