POST http://localhost:8000/login/ 401 (Неавторизованный)

Я работаю над приложением на React Typescript и Django, но не могу заставить работать аутификацию. Я потратил на это несколько часов, но не могу найти решение. Любая помощь будет очень признательна.

App.tsx

import { useEffect, useState } from "react";
import { Home } from "./Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Login } from "./Login";
import Navbar from "./components/Navbar";
import axios from "axios";
export interface Movie {
  id: number;
  title: string;
  genre: string;
  year: number;
}
function App() {
  const [movies, setMovies] = useState<Movie[]>([]);
  const getMovies = async (): Promise<Movie[]> => {
    try{
      const { data } = await axios.get<Movie[]>('http://localhost:8000/api/movies')
      setMovies(data)
      console.log(data)
      return data
    }catch (error){
      console.error("error",error);
      return []
    }
  }
  useEffect(() => {
    getMovies();
  },[])
  return (
      <main className="bg-slate-500 p-60 min-h-screen">
    <Router>
      <Navbar />
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route path="/" element={<Home movies={movies} />} />
    </Routes>
  </Router>
      </main>
  )
  }
export default App;

Login.tsx

import axios, { AxiosResponse } from "axios";

interface User {
  email: string;
  password: string;
}
async function logIn(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  console.log("Logging in");
  try {
    const user: User = { email: "e@a.com", password: "hei" }
    const response: AxiosResponse = await axios.post('http://localhost:8000/login/', user, { withCredentials: true })
    const responseData: User = response.data
    console.log(responseData)
  } catch (error) {
    if (axios.isAxiosError(error)) {
      console.error("Axios error", error.response?.data)
    }
    else {
      console.error("error", error);
    }
  }
}
export const Login: React.FC = () => {

  return (
    <div className="flex flex-row">
      <form onSubmit={(e) => logIn(e)} className="flex flex-col gap-12 p-40 m-auto bg-[#C7D2D6] rounded-xl w-1/2">
        <div>
          <h1 className="text-4xl">FilmFlix</h1>
          <p className="text-lg">Sign In</p>
        </div>
        <div className="flex flex-col">
          <label htmlFor="email">Email:</label>
          <input id="email" className="border-2 rounded-md" type="text" placeholder="hei" />
        </div>
        <div className="flex flex-col">
          <label htmlFor="password">Password:</label>
          <input id="password" type="password" className="border-2 rounded-md" placeholder="password" />
        </div>
        <button className="bg-[#6A2DEB] p-4 w-1/2 m-auto text-white rounded-md">Sign In</button>
      </form>
      <div className="bg-slate-700 w-1/3 rounded-md">LOGO</div>
    </div>
  );
}

backends.py

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

managers.py

from django.contrib.auth.base_user import BaseUserManager
from django.utils.text import slugify

class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of usernames.
    """
    def create_user(self, email, password, username=None, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        
        from .models import MovieUser  # Import MovieUser locally to avoid circular import
        if not email:
            raise ValueError(('The Email must be set'))
        email = self.normalize_email(email)
        username = slugify(email.split('@')[0])
        user = MovieUser(email=email, username=username, **extra_fields)
        user.set_password(password)
        user.save()
        return user
    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)
        if extra_fields.get('is_staff') is not True:
            raise ValueError(('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)


models.py

from django.contrib.auth.models import AbstractUser
from django.db import models
from .managers import CustomUserManager

class MovieUser(AbstractUser):
    
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=150, unique=True)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    
    objects = CustomUserManager()
    
    def __str__(self):
        return self.email

serializers.py

from django.contrib.auth.password_validation import validate_password
from django.core import exceptions
from rest_framework import serializers
from django.contrib.auth import get_user_model

# establishes custom user model
User = get_user_model()

class MovieUserSerializer(serializers.ModelSerializer):
    # By defining the model and fields in the Meta class, the UserCreateSerializer class inherits the default behavior 
    # and functionality provided by the ModelSerializer base class. It automatically generates serializers for the specified 
    # fields and handles the data validation and creation process.
    class Meta:
        model = User
        # declare fields for creation
        fields = ('email', 'password', 'username')


    # responsible for performing additional validation and logic on the data before saving a new user instance.
    def validate(self, data):
        # logic for populating user fields
        # This line creates a new User instance using the data dictionary, which contains the validated data from the serializer. 
        # By using the ** syntax, the dictionary values are unpacked and used as keyword arguments to initialize the User instance.
        user = User(**data)
        password = data.get('password')

        # This block of code wraps the validate_password function call in a try-except block. 
        # It attempts to validate the password using the validate_password function provided by Django's password validation framework. 
        # If any validation errors occur, such as a weak password, it catches the exceptions.ValidationError and proceeds to the next step.
        try:
            validate_password(password, user)
        except exceptions.ValidationError as e:
            serializer_errors = serializers.as_serializer_error(e)
            raise exceptions.ValidationError(
                {'password': serializer_errors['non_field_errors']}
            )

        return data

    def create(self, validated_data):
        # Create a new user with the provided email and password
        user = User.objects.create_user(
            email=validated_data['email'],
            password=validated_data['password'],
        )

        return user

urls.py

from django.urls import path
from .views import LoginAPIView

urlpatterns = [
    path('', LoginAPIView.as_view(), name='login'),
]

views.py

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth import authenticate, login
from .serializers import MovieUserSerializer

class LoginAPIView(APIView):
    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        user = authenticate(request, email=email, password=password)
        if user:
            login(request, user)
            serializer = MovieUserSerializer(user)
            return Response(serializer.data)
        else:
            return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

main urls.py

"""
URL configuration for core project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('core.api.urls')),  
    path('login/', include('registration.urls'))  
]

registration - это папка, в которой находится код, связанный с пользователем

Вот что я получаю при попытке войти в систему с пользователем

Может быть, в LoginAPIView.post следует вызывать authenticate из EmailBackend, а не django.contrib.auth, как сейчас?

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