0

I am working on a web application using Symfony for the backend and VueJS 3.2 for the frontend, and I am facing a persistent issue with CSRF token validation. Despite several attempts and checks, I keep receiving an "Invalid CSRF token" error when trying to validate the token in Symfony. I would appreciate any insights or suggestions to resolve this issue.

Environment:

  • Backend: Symfony 6.3
  • Frontend: VueJS 3.2
  • Method of sending data: FormData in a POST request

Problem:

When submitting a registration form from the VueJS frontend, including a CSRF token retrieved from Symfony, the backend validation consistently fails with an "Invalid CSRF token" error.

Backend Implementation:

In Symfony, I have set up a route to generate and send a CSRF token to the frontend:


namespace App\Controller;

use App\Entity\User;
use App\Form\UserRegistrationType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class RegistrationController extends AbstractController
{
    private CsrfTokenManagerInterface $csrfTokenManager;

    public function __construct(CsrfTokenManagerInterface $csrfTokenManager){
        $this->csrfTokenManager = $csrfTokenManager;
    }

    #[Route('/api/get-csrf-token', name: 'get-csrf-token', methods: ['GET'])]
    public function getCsrfToken(): JsonResponse
    {
        $csrfToken = $this->csrfTokenManager->getToken('csrf_token')->getValue();
        return $this->json(['csrf_token' => $csrfToken]);
    }

    #[Route('/api/register', name: 'app_register', methods: ['POST'])]
    public function register(Request $request, UserPasswordHasherInterface $passwordHasher): Response
    {
        $content = $request->request->all();
        $csrfToken = $request->get('_token', '');
//        return $this->json(['message' =>$csrfToken]);

        if (!$this->isCsrfTokenValid('csrf_token', $csrfToken)) {
            return $this->json(['errors' => 'ERROR: The CSRF TEST token is invalid. Please try to resubmit the form.'], Response::HTTP_BAD_REQUEST);
        }
        $user = new User();
        $form = $this->createForm(UserRegistrationType::class, $user);
        $form->submit($content);

        if ($form->isSubmitted() && $form->isValid()) {
            // Hashing the password
            $hashedPassword = $passwordHasher->hashPassword($user, $user->getPassword());
            $user->setPassword($hashedPassword);

            // Save the User entity in the database
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($user);
            $entityManager->flush();

            // Return a success response
            return $this->json(['message' => 'User successfully registered.'], Response::HTTP_CREATED);
        }

        // Handle form errors
        return $this->json(['errors' => (string) $form->getErrors(true, false)], Response::HTTP_BAD_REQUEST);
    }

Frontend Implementation (VueJS):

On the VueJS side, I am sending the token back to Symfony in a POST request using FormData:

<template>
  <div class="form-container">
    <form @submit.prevent="submitForm" class="form-register">
      <div class="form-group">
        <label for="email">Email</label>
        <input v-model="user.email" type="email" id="email" placeholder="Email" class="form-control" required >
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input v-model="user.password" type="password" id="password" placeholder="Password" class="form-control" required>
      </div>
      <input type="hidden" name="_token" :value="csrfToken">
      <button type="submit" class="btn-submit">Register</button>
    </form>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      user: {
        email: '',
        password: '',
      },
      errors: [],
      csrfToken: '',
    };
  },
  created() {
    axios.get('/get-csrf-token').then((response) => {
      this.csrfToken = response.data.csrf_token;
      console.log('Token Get:', this.csrfToken); 

    });
  },
  methods: {
    async submitForm() {
      try {
        let formData = new FormData();
        Object.keys(this.user).forEach(key => formData.append(key, this.user[key]));
        formData.append('_token', this.csrfToken);
        const config = {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        };
        const response = await axios.post('/register', formData, config);
        console.log(response.data);
      } catch (error) {
        if (error.response) {
          console.error('Error:', error.response.data);
        } else {
          console.error('Error:', error.message);
        }
      }
    }
  },
};
</script>


Issue:

Every time I submit the form, Symfony's CSRF token validation fails, and I receive an "Invalid CSRF token" error, despite ensuring that the token sent from VueJS is identical to the one generated by Symfony.

What I've Tried:

Verifying the consistency of the CSRF token ID used in generation and validation. Ensuring that the token is correctly included in the FormData and sent in the POST request. Checking the request and response headers for any discrepancies. Testing the functionality in a controlled environment with a simple Symfony form. I am not sure what I am missing or doing incorrectly. Any guidance on how to troubleshoot this issue or insights into what might be causing the token validation to fail would be greatly appreciated.

1
  • Hi. I think you are using manual "csrf_protection" check, but your form validates again CSRF token. try $form = $this->createForm(UserRegistrationType::class, $user, ["csrf_protection" => false]); Commented Nov 23, 2023 at 12:16

1 Answer 1

0

You are using manual "csrf_protection" check via CsrfTokenManager, but your form validates again CSRF token.

The error message "Invalid CSRF token" is triggered by $form->isValid() function.

Then, if your manual check is safe, you should change the creation of form, in this way

    $form = $this->createForm(UserRegistrationType::class, $user, [
        "csrf_protection" => false
    ]);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.