Развертывание React front end с Django session based auth не работает через HTTPS

Итак, у меня есть работающий МЕСТНЫЙ клон Twitter под названием Hater, но я не могу развернуть front end, потому что не могу получить доступ к защищенным Cookies (https://github.com/mustafabin/hater)

Я использовал встроенный в Django аутентификатор на основе сеансов У меня все настроено промежуточное ПО

ПРОСМОТР ВХОДА

@method_decorator(csrf_protect, name="dispatch")
class LoginView(APIView):
    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        data = self.request.data

        username = data['username']
        password = data['password']

        try:
            user = auth.authenticate(username=username, password=password)

            if user is not None:
                auth.login(request, user)
                return Response({'success': 'User authenticated'})
            else:
                return Response({'error': 'Error Authenticating'})
        except:
            return Response({'error': 'Something went wrong when logging in'})

ПОДПИСАТЬСЯ

@method_decorator(csrf_protect, name="dispatch")
class SignupView(APIView):
    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        data = self.request.data

        username = data['username']
        password = data['password']
        re_password = data['re_password']
        tag = data['tag']

        try:
            if password == re_password:
                if User.objects.filter(username=username).exists():
                    return Response({"error": "Username already exists"})
                else:
                    if len(password) < 6:
                        return Response({"error": "Password must be at least 6 characters"})
                    else:
                        user = User.objects.create_user(
                            username=username, password=password)

                        user = User.objects.get(id=user.id)

                        user_profile = User_profile.objects.create(
                            user=user, name=username, tag=tag)

                        return Response({'success': "User created successfully"})
            else:
                return Response({'error': "Passwords do not match"})
        except:
            return Response({"error": "Something went wrong signing up"})

Я знаю, что некоторые из этих настроек излишни, но ваш человек отчаялся

CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_HTTPONLY = False
SESSION_COOKIE_HTTPONLY = False
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
CSRF_TRUSTED_ORIGINS = ['http://localhost:3000', 'http://localhost:8000',
                        'https://hater.netlify.app', 'https://haterip.netlify.app']
CORS_EXPOSE_HEADERS = ["Set-Cookie"]
django_heroku.settings(locals())

а это код React.js, который обрабатывает вход в систему и теринар, который отображает форму входа, когда пользователь не вошел в систему user.tag - это глобальное состояние, которое равно null, если текущий пользователь не вошел в систему

let handleLogin = (e) => {
    e.preventDefault();
    let headerInfo = {
      Accept: "application/json",
      "Content-Type": "application/json",
    };
    let loginOptions = {
      method: "POST",
      headers: headerInfo,
      credentials: "include",
      body: JSON.stringify(form),
    };
    let options = {
      method: "GET",
      headers: headerInfo,
      credentials: "include",
    };

    fetch(`https://haterbackend.herokuapp.com/user/login`, loginOptions)
      .then((res) => res.json())
      .then((data) => {
        if (data["error"]) {
          return alert(data["error"]);
        } else {
          fetch(`https://haterbackend.herokuapp.com/user/grabProfile`, options)
            .then((res) => res.json())
            .then((data) => {
              store.dispatch({ type: "set", payload: data.profile });
            })
            .then(() => navigate("/home"))
            .catch((err) => console.log(err));
        }
      })
      .catch((err) => console.log(err));
  };
  {!user.tag ? (
      <form onSubmit={handleLogin} className="landingForm">
        <CSRFToken></CSRFToken>
        <input
          onChange={handleChange}
          className="landingLoginInput"
          placeholder="Username"
          type="text"
          name="username"
        />

        <input
          onChange={handleChange}
          className="landingLoginInput"
          placeholder="Password"
          type="password"
          name="password"
          autoComplete="current-password"
        />
        <Button id="login" type="submit">
          Login
        </Button>
      </form>
    ) : (
      <div className="landing-signout">
        <Link className="landing-home-link" to="/home">
          <Button>Home 🏡</Button>
        </Link>
        <Link className="landing-signout-link" onClick={signOut} to="/">
          <Button>Sign out 🚪</Button>
        </Link>
      </div>
    )}

Компонент CSRFToken - это просто скрытое поле ввода

import React, { useState, useEffect } from "react";

export default function CSRFToken() {
  const [csrftoken, setcsrftoken] = useState("");

  const getCookie = (name) => {
    let cookieValue = null;
    if (document.cookie && document.cookie !== "") {
      let cookies = document.cookie.split(";");
      for (let i = 0; i < cookies.length; i++) {
        let cookie = cookies[i].trim();
        if (cookie.substring(0, name.length + 1) === name + "=") {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  };

  useEffect(() => {
    fetch(`https://haterbackend.herokuapp.com/user/csrf_cookie`, {
      credentials: "include",
    })
      .then((res) => {
        setcsrftoken(getCookie("csrftoken"));
      })
      .catch((err) => {
        console.log(err);
      });
  }, []);

  return (
    <input type="hidden" name="csrfmiddlewaretoken" value={csrftoken || ""} />
  );
}

Я реализовал вход в сессию с помощью react front end, как описано здесь: https://www.stackhawk.com/blog/django-csrf-protection-guide/ https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax официальный документ

приведенный выше метод работал только локально на HTTP, но не работал на HTTPS, потому что развернутый сайт не устанавливал cookie, поскольку он был защищен. снимок экрана ошибки

Но скрипты на стороне клиента не могут захватывать защищенные куки, а функция getCookie из документации Django анализирует только установленный заголовок куки, поэтому она не будет работать, если куки не определены или пусты (код скрипта на стороне клиента пытается прочитать куки, браузер возвращает пустую строку в качестве результата, источник которого приведен ниже) https://owasp.org/www-community/HttpOnly

TLDR: Проект работает локально HTTP, но при развертывании cookies не могут быть установлены через HTTPS, а клиентские скрипты не могут читать защищенные cookies, поэтому я не могу регистрироваться или входить в систему, так как для этого требуется cookie csrftoken

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

передняя часть была на netifly back end был на heroku

Лучшей альтернативой, которую я использовал, был django knox tokens, он ведет себя так же, как JWT аутентификация, но имеет больше функциональности и токены могут быть аннулированы.

TLDR; поскольку сервисы не находятся в одном домене, это невозможно при https-соединении

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