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:

login api works access and refresh cookies are saved in Bruno user api returns user info only using cookies. no json body is passed.

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

Brave devtools only showing csrftoken when I inspect the "user" API request under the Network tab

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?

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