Браузер не сохраняет HttpOnly Cookie React/Django

У меня есть фронтенд netlify на app.mydomain.com, и REST API от django ninja на api.mydomain.com. Когда я отправляю логин в конечную точку, api успешно возвращается с ключом доступа (который я храню в состоянии приложения) и маркером обновления в виде безопасного куки httponly. Я вижу, что этот куки возвращается, просматривая заголовки ответа в dev tools. Однако куки не сохраняются браузером вообще. Я просмотрел множество других вопросов/ответов и считаю, что реализовал все необходимое, но это все еще не работает.

Мой вызов API логина с фронтенда выглядит так:

await fetch(
  AUTH_URL_OBTAIN,
  {
    method: RequestMethod.POST,
    headers: {"Content-Type": "application/json"},
    body: JSON.stringify({username: formData.email, password: formData.password}),
    credentials: "include",
  },
);

На бэкенде cookie устанавливается следующим образом:

response.set_cookie(
    key="refresh",
    value=refresh_token,
    expires=datetime.fromtimestamp(refresh_token_payload["exp"], timezone.utc),
    httponly=True,
    samesite="none",
    secure=True,
    path="/api/auth/web/token-refresh",
    domain=".mydomain.com",
)

У меня также установлены следующие настройки (заменяя значения переменных окружения):

CSRF_TRUSTED_ORIGINS = ["https://app.mydomain.com"]
CORS_ALLOWED_ORIGINS = ["https://app.mydomain.com"]
CORS_ORIGIN_WHITELIST = ["https://app.mydomain.com"]
CORS_ALLOW_CREDENTIALS = True

В ответе на вход в систему указан токен доступа (который работает, как и ожидалось - я могу совершать вызовы API, используя его, и имею credentials: include на всех fetch запросах), а заголовки ответа приведены ниже:

Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin: https://app.mydomain.com

Content-Length: 661

Content-Type: application/json; charset=utf-8

Cross-Origin-Opener-Policy: same-origin

Date: Thu, 06 Jun 2024 14:06:22 GMT

Referrer-Policy: same-origin

Server: daphne

Set-Cookie: refresh=ey...3uQ; Domain=.mydomain.com; expires=Sat, 06 Jul 2024 14:06:22 GMT; HttpOnly; Max-Age=2592000; Path=/api/auth/web/token-refresh; SameSite=none; Secure

Vary: origin

X-Content-Type-Options: nosniff

X-Frame-Options: DENY

Я в растерянности - любой совет будет очень ценен, спасибо!

Политики безопасности браузеров могут блокировать куки от/к ajax-запросов. Смотрите

Для кросс-оригинального API я бы предпочел вообще не использовать cookies. Представьте себе любого клиента API, кроме браузера! Cookies предназначены для отслеживания сеансов просмотра веб-сайта, а не для хранения состояния в произвольных http-клиентах.

Просто включите маркер обновления в тело ответа и сохраните его в состоянии вашего приложения.

Я реализовал множество решений такого типа, где cookie используется для включения учетных данных API-сообщений и, следовательно, выдается на стороне API-архитектуры.

По-моему, ваша проблема заключается в свойстве expires куки. Это свойство заставляет cookie быть постоянным, и я предполагаю, что оно установлено неверно. Все остальное выглядит правильно.

Думаю, вы сможете решить проблему, если удалите это свойство cookie. Это также является лучшей практикой безопасности и приводит к созданию сеансового файла cookie, который удаляется при закрытии браузера.

Кроме того, нельзя делать предположения о форматах или полезной нагрузке токенов обновления. Вместо этого маркер обновления считается истекшим, когда вы пытаетесь использовать его на emdpoint маркера сервера авторизации и получаете ответ с кодом ошибки invalid_grant. Поэтому при использовании файлов cookie, представляющих собой маркеры, всегда определяйте срок действия по базовому маркеру.

Это не имеет никакого отношения ни к Django, ни к React. Дело в том, как вы устанавливаете cookie.

  1. Куки с символом path могут быть установлены где угодно, но доступны только на этом пути и его дочерних страницах. Таким образом, вы можете увидеть cookie только при посещении страницы /api/auth/web/token-refresh
  2. .
  3. Лучше использовать Max-Age, чем Expires, так как это менее подвержено ошибкам. Это цитата из MDN page:

Expires существует дольше, чем Max-Age, однако Max-Age менее подвержен ошибкам и имеет приоритет, когда заданы оба значения. Это объясняется тем, что когда вы устанавливаете дату и время Expires, они относятся к клиенту, на котором устанавливается cookie. Если на сервере установлено другое время, это может привести к ошибкам. 3. Безопасный cookie может быть установлен только через https или localhost

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