Encountering a 302 redirect issue when submitting a form in React/Django app as API call gets redirected

In my Django/React app I want a user to be signed in to be able to successfuly submit a request to an API.

I successfully login with google and I believe I am passing all of the correct data to the backend api for it to be called.

I have configured CORS, ensured my CSRF tokens match, am using @login_required on my Django view, use "credentials: 'include'" in the api call, and have exhausted both ChatGPT and my own internet searching to solve the problem.

From my Conversation.jsx file I want to call /api/ask_new_york from app/views.py. Even when logged in I get a 302 response.

Here is all of what I think is the relevant code and the output:

settings.py:



MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'allauth.account.middleware.AccountMiddleware',
]

# CORS settings
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True

# CSRF settings
CSRF_TRUSTED_ORIGINS = [
    'http://localhost:3000',
    'http://localhost:8000',
    'http://127.0.0.1:8000',
    'http://localhost'
]

# Disable CSRF cookies only if needed
CSRF_COOKIE_SECURE = False 

LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/'
LOGOUT_REDIRECT_URL = '/'

app/views.py:


@csrf_exempt
def get_csrf_token(request):
    csrf_token = get_token(request)
    return JsonResponse({'csrf_token': csrf_token})

@login_required
def ask_new_york(request):
    if request.method == 'POST':
        try:
            data = json.loads(request.body.decode('utf-8'))
            input_value = data.get('input_value'
     

users/views.py: This handles the users creation.(this is the whole file)

from django.contrib.auth import get_user_model
from django.conf import settings
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .serializers import UserSerializer
from django.db import IntegrityError

User = get_user_model()

@api_view(['POST'])
@csrf_exempt
def google_login(request):
    if request.method == 'POST':
        try:
            token = request.data.get('token')
            if not token:
                return JsonResponse({'error': 'No token provided'}, status=400)

            # Verify the token using Google's API
            idinfo = id_token.verify_oauth2_token(token, google_requests.Request(), settings.GOOGLE_CLIENT_ID)

            # If token is valid, get the user info
            email = idinfo['email']
            name = idinfo.get('name', '')

            # Check if the user exists, if not, create a new user
            user, created = User.objects.get_or_create(email=email, defaults={'username': email, 'first_name': name})

            if created:
                user.set_unusable_password()  # If user is created, ensure no password is set
                user.save()

            # Return user info
            return JsonResponse({'email': user.email, 'name': user.first_name})

        except ValueError:
            return JsonResponse({'error': 'Invalid token'}, status=400)

        except IntegrityError:
            # Handle the case where the email already exists
            return JsonResponse({'error': 'User with this email already exists'}, status=400)

    return JsonResponse({'error': 'Invalid request method'}, status=400)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def user_detail(request):
    user = request.user
    serializer = UserSerializer(user)
    return Response(serializer.data)

LoginSignUpPage.jsx: Frontend page where user actually logs in. (this is the whole file)

import React, { useContext, useEffect, useState } from 'react';
import { GoogleLogin } from '@react-oauth/google';
import { useNavigate } from 'react-router-dom';
import { jwtDecode } from "jwt-decode";
import AuthContext from './AuthContext';
import axios from 'axios';
import { getCsrfToken } from './csrfToken'; // Import the utility

const LoginSignupPage = () => {
  const { setAuthUser } = useContext(AuthContext);
  const navigate = useNavigate();
  const [csrfToken, setCsrfToken] = useState('');

  useEffect(() => {
    const token = getCsrfToken(); // Use the centralized function to get CSRF token
    setCsrfToken(token);
    console.log("CSRF token set as:", token);
  }, []);

  const handleLoginSuccess = async (response) => {
    try {
      const decoded = jwtDecode(response.credential); // Decode JWT
      const { email, name } = decoded;
  
      // Send token to backend for verification and authentication
      const res = await axios.post(
        'http://localhost:8000/api/google-login/',
        { token: response.credential },
        {
          headers: {
            'X-CSRFToken': csrfToken, // Include the CSRF token in the headers
          },
        }
      );
  
      const user = {
        email: res.data.email,
        name: res.data.name,
        created_at: res.data.created_at,
      };
  
      console.log(user); // Log user info
  
      // Save user data in context or global state
      setAuthUser(user);
  
      // Redirect to home page after successful login
      navigate('/');
    } catch (error) {
      console.error('Login Failed:', error);
    }
  };
  
  const handleLoginFailure = (error) => {
    console.log('Login Failed:', error);
  };

  return (
    <div>
      <h1>Login</h1>
      <GoogleLogin onSuccess={handleLoginSuccess} onError={handleLoginFailure} />
    </div>
  );
};

export default LoginSignupPage;

Conversation.jsx: Homepage where user submits text to the API handled in handleSubmit. There is some error handling and front end components that execute handleSubmit when clicked in here as well.

import { getCsrfToken } from './csrfToken';

useEffect(() => {
    const token = getCsrfToken(); // Use the centralized function to get CSRF token
    setCsrfToken(token);
    console.log("CSRF token set as:", token);
  }, []);


const handleSubmit = async (e) => {
try {
      let headers = {
        'X-CSRFToken': csrfToken,
        'Content-Type': 'application/json',
      };
response = await fetch("http://localhost:8000/api/ask_new_york/", {
          method: "POST", // Specify POST method
          headers: headers,
          credentials: 'include', // Ensures cookies are sent with the request
          body: JSON.stringify({ input_value: inputValue }),
        });
      }

csrfToken.js: Gets the csrftoken(this is the whole file)

export const getCookie = (name) => {
    const value = `; ${document.cookie}`;
    console.log(value)
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
  };
  
  export const getCsrfToken = () => {
    return getCookie('csrftoken'); // Adjust the name if your cookie is named differently
  };
  

In my web console I have the csrfToken.js, LoginSignUpPage.jsx and Conversation.jsx files log the csrftoken they have and they are all the same. When logged in and handleSubmit is executed in Conversation.jsx I get following errors: In Google Chrome:

Error submitting data: SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON

I believe it is receiving the html of the /login page it wants to redirect to even though the user is logged in. Maybe this is where I am wrong.

In my backend terminal:

[11/Sep/2024 21:35:31] "POST /api/ask_new_york/ HTTP/1.1" 302 0
[11/Sep/2024 21:35:31] "GET /login/?next=/api/ask_new_york/ HTTP/1.1" 200 644.

I do not know why error 302 is happening.

Extra:

[11/Sep/2024 21:35:22] "OPTIONS /api/google-login/ HTTP/1.1" 200 0
[11/Sep/2024 21:35:22] "POST /api/google-login/ HTTP/1.1" 200 47

Google login seems to work fine.

Back to Top