Search code examples
restsymfonyfosuserbundlefosrestbundle

API Rest "The CSRF token is invalid. Please try to resubmit the form."


I'm looking to combine FOS Rest Bundle AND FOS User Bundle to my API application to register new users. The problem is:

  • Can't found best practices for user registration on API Rest
  • Unable to register a user using fos user registration type, got 400 Bad Request with "The CSRF token is invalid. Please try to resubmit the form."

How can I resolve those issues?

My POST Body

{
    "firstname": "xxx",
    "lastname": "xxx",
    "email":"xxxxx@xxxx.com",
    "username":"xxx",
    "plainPassword":{
        "first":"xxx",
        "second":"xxx"
    }
}

Response:

{
    "errors": [
        "The CSRF token is invalid. Please try to resubmit the form."
    ],
    "children": {
        "email": {},
        "username": {},
        "plainPassword": {
            "children": {
                "first": {},
                "second": {}
            }
        },
        "firstname": {},
        "lastname": {}
    }
}

UserType Form

class UserType extends AbstractType
    {
        /**
         * @inheritDoc
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('firstname')
                ->add('lastname');
        }

        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'crsf_protection' => false,
                'csrf_token_id' => null,
                'data_class' => 'TM\UserBundle\Document\User'
            ]);
        }

        public function getParent()
        {
            return 'FOS\UserBundle\Form\Type\RegistrationFormType';

            // Or for Symfony < 2.8
            // return 'fos_user_registration';
        }
    }

The Controller

class UserController extends FOSRestController
{
    /**
     * @Rest\Post("/register")
     * @param Request $request
     * @return null|Response
     */
    public function postUserAction(Request $request)
    {
        /** @var $formFactory FormFactory */
        $formFactory = $this->get('form.factory');
        /** @var $userManager UserManagerInterface */
        $userManager = $this->get('fos_user.user_manager');
        /** @var $dispatcher EventDispatcherInterface */
        $dispatcher = $this->get('event_dispatcher');

        $user = $userManager->createUser();
        $user->setEnabled(true);

        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);

        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }

        $form = $formFactory->create(UserType::class, $user);

        $form->submit($request->request->all());

        if ($form->isValid()) {
            $event = new FormEvent($form, $request);
            $dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);

            $userManager->updateUser($user);

            if (null === $response = $event->getResponse()) {
                $url = $this->generateUrl('fos_user_registration_confirmed');
                $response = new RedirectResponse($url);
            }

            $dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

            $view = $this->view(array('token' => $this->get("lexik_jwt_authentication.jwt_manager")->create($user)), Response::HTTP_CREATED);

            return $this->handleView($view);
        }

        $view = $this->view($form, Response::HTTP_BAD_REQUEST);
        return $this->handleView($view);
    }
}

Solution

  • For an API you'll likely need to disable CSRF across the board, particularly if your API is processing data through forms directly (which I would recommend you do regardless).

    You can find a clever solution for this here: https://stackoverflow.com/a/9888593/4620798

    In your particular case, I think you may need to remove the csrf_token_id as it may be submitting a null value despite you telling the form to disable it anyway?

    Separately, you'll probably end up running into PREFLIGHT issues if you're using Angular2 or any other "modern" frontend framework as the consumer. If/when you do there are solutions to that also :)