Обработка ошибки 401 Unauthorized при обновлении страницы с помощью JWT и React/Django

Я разрабатываю приложение для аутентификации, используя Django для бэкенда и React для фронтенда. Я реализовал JWT-токены для аутентификации. Когда я вхожу в систему, куки HttpOnly (access_token и refresh_token) устанавливаются правильно. Однако при обновлении страницы я сталкиваюсь с ошибкой 401 (Unauthorized) в консоли.

Ошибка возникла во время GET-запроса к http://localhost:8000/api/get-me/.

Выпуск

После успешного входа в систему, если я обновляю страницу, то получаю в консоли ошибку 401 (Unauthorized) для GET-запроса к конечной точке /api/get-me/. Такое поведение является неожиданным, поскольку пользователь должен быть аутентифицирован после обновления страницы.

Вопрос

Как сделать так, чтобы пользователь оставался аутентифицированным даже после обновления страницы, и избежать ошибки 401 (Unauthorized)? Может быть, что-то упущено или неправильно в моей конфигурации JWT и обработки куки?

Релевантный код

urls.py

    path('token/', views.LoginView.as_view(), name='token_obtain_pair'),
    path('get-me/', views.GetUserDetailView.as_view(), name='get-me'),
    path('token-validate/', views.TokenValidationView.as_view(), name='token-validate'),

views.py

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticated

class TokenValidationView(APIView):
    authentication_classes = [JWTAuthentication]

    def get(self, request):
        # If the code reaches here, it means the token is valid
        return Response({"message": "Token is valid"}, status=status.HTTP_200_OK)

class GetUserDetailView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        serializer = UserDetailSerializer(request.user)
        print(serializer.data)
        return Response(serializer.data)

class LoginView(APIView):
    def post(self, request, *args, **kwargs):
        serializer = LoginUserSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            user = serializer.validated_data
            refresh = RefreshToken.for_user(user)
            access_token = str(refresh.access_token)

            response = Response({
                'user_info': {
                    'id': user.id,
                    'first_name': user.first_name,
                    'last_name': user.last_name,
                    'email': user.email,
                    'is_email_verified': user.is_email_verified
                }
            }, status=status.HTTP_200_OK)

            response.set_cookie(
                'access_token',
                access_token,
                httponly=True,
                max_age=int(refresh.access_token.lifetime.total_seconds()),
                secure=True,
            )
            response.set_cookie(
                'refresh_token',
                str(refresh),
                httponly=True,
                max_age=int(refresh.lifetime.total_seconds()),
                secure=True
            )
            return response
        return Response(serializer.errors, status=400)

serializers.py

class LoginUserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = serializers.CharField()

    def validate(self, data):
        user = authenticate(**data)
        if user and user.is_active:
            return user
        raise serializers.ValidationError("Incorrect Credentials")

App.js

const App = () => {
  const dispatch = useDispatch();
  const user = useSelector(selectUser); // Get the user from Redux store
  
  useEffect(() => {
    const validateTokenAndFetchUser = async () => {
        try {
            // Validate token
            await axiosInstance.get('/api/token-validate/');
            // If token is valid, fetch user info
            const userInfoResponse = await axiosInstance.get('/get-me/');
            console.log(userInfoResponse)
            dispatch(loginUser({ state: userInfoResponse.data }));
        } catch (error) {
            console.error("Error in token validation or fetching user data:", error);
        }
    };

    validateTokenAndFetchUser();
}, [dispatch]);

  const handleLogin = async (email, password) => {
    try {
      const userData = await login(email, password);
      console.log(userData);
      dispatch(loginUser({ email: userData['email'], token: 'tralala' }));
    } catch (error) {
      console.error("Login failed:", error);
    }
  };

  return (
      <Router>
        <Menu />
        <div>
          <Routes>
            <Route
              path="/login"
              element={user ? <Navigate to="/" /> : <Login onLogin={handleLogin} />}
            />
            <Route
              path="/"
              element={user ? <Dashboard user={user} /> : <Home />}
            />
          </Routes>
        </div>
      </Router>
  );
};

export default App;

axiosConfig.js

// axiosConfig.js
import axios from 'axios';

const axiosInstance = axios.create({
  baseURL: 'http://localhost:8000/api/', // Replace with your API's base URL
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true, // This will ensure cookies are sent with the request
});

axiosInstance.interceptors.response.use(response => {
  return response;
}, error => {
  if (error.response && error.response.status === 401) {
    console.log('Error 401')
  }
  return Promise.reject(error);
});

export default axiosInstance;
Вернуться на верх