17

OK, I googled this hard, but everything I find talks about Symfony forms in context of regular Symfony form processing (e.g. form_widget(), creating FormType class, etc.). I have many such forms in my Symfony project, they work great.

BUT:

I also have some pretty complex AJAX forms that I would like to build manually (using plain old HTML and JS). I do still want to utilize Symfony's Form validation capabilities and CSRF protection. However, for some reason I can't get CSRF working when using isValid() for manually created forms.

This is an example of what I am trying to accomplish:

In my view controller I set _token:

$_token = $this->get('form.csrf_provider')->generateCsrfToken('form');

In my view (manually created form) (getting _token from my view controller):

<html>
  <form method="post">
    <input type="hidden" name="form[_token]" value="{{ _token }}">
    <input type="hidden" name="form[id]" value="1">
    <input type="submit" value="Submit">
  </form>
</html>

In my action controller (when form submitted, I am TRYING to do the following):

//Create form (for validation purposes)
$form = $this->get('form.factory')
  ->createBuilder('form', array('id' => $request->get('id')))
  ->add('id', 'hidden')
  ->getForm();

//Bind form
$form->bind($request)

//Validate form
if($form->isValid()) {
  //... save data
}

//Return response...

For some reason I can't get isValid() working, I suspect that my _token is thing not properly used, but I am out of ideas why. Have anyone actually made manually forms work with Symfony components? Does anyone have any suggestions on how to make this work?

Basically, what I want to accomplish is:

  1. Manually create HTML form (with CSFR protection and without TWIG form widget functions)

  2. Use Symfony's form functionality to validate that form

Thank you.

user1863635
  • 889
  • 2
  • 10
  • 18

4 Answers4

8

I think you're mismatching the intention here (argument passed to your CSRF provider). I tried generating form as you wrote above and break-pointed the generation of token. The value was unknown.

So, try passing unknown instead of form to your generateCsrfToken call and hopefully it should work. ;)

EDIT:

I have just finished some digging and it now does make perfect sense.

Look at the class FormTypeCsrfExtension. Apparently, it's the default extension used for CSRF token protection. On the line #80 (might not be this one exactly in your case) there is method setDefaultOptions that is usually overridden in your form types. Anyhow, there is a default options called intention that has a value of unknown ==> the one we are seeing here.

My guess is that you could easily override this option in your own form type just by passing intention and setting your own value (just as you would pass csrf_protection => false when you would want to disable CSRF protection altogether).

Jovan Perovic
  • 19,846
  • 5
  • 44
  • 85
  • OK. Doing so, worked. Thanks. But now I don't understand why. I looked at the API for generateCsrfToken() for details on the intention parameter, but it's still not clear to me why passing 'unknown' for an intention parameter works. I guess what I need to know is how properly use intentions? Thanks. – user1863635 Apr 30 '13 at 14:33
  • yeah, it's is a bit unclear to me as well. I would assume some more meaningful default value :) – Jovan Perovic Apr 30 '13 at 14:35
  • I am digging through Symfony's code but cannot find any code that would make 'form' fail and 'unknown' work. Maybe someone else can explain what's going on with this..? – user1863635 Apr 30 '13 at 14:54
  • Ok, I updated my answer with some findings about this default value :) – Jovan Perovic Apr 30 '13 at 15:42
  • I just tried your suggestion regarding overriding 'intention' option when creating a form, and it worked great. Basically, when I created CSRF token I used 'delete' for its intention parameter. And then when I created a form to use its isValid() function during form processing, I overridden the 'intention' option by passing an array to the createBuilder('form', $data, array('intention' => 'delete')). Doing so solved my problem. THANK YOU! It's surprising that Symfony2 does not really have documentation about this. – user1863635 Apr 30 '13 at 20:03
5

Note: intention is no longer default to be unknown. When I checked this out in symfony 2.3, it would appear it to default to the type name:

$options['intention'] ?: ($builder->getName() ?: get_class($builder->getType()->getInnerType()))

It would be good if there was some programatic way to get the intention out that is used instead of having to rely on these defaults.

PressingOnAlways
  • 11,948
  • 6
  • 32
  • 59
4

I have seen that this is resolved but I have some form troubles too and saw an other post here :

symfony2 CSRF invalid

And their solution seems to me better than making my own token :

There is no problem using {{ form_widget(form) }} to build your custom form. All you have to do is add the _token like this: {{ form_widget(form._token) }}

Community
  • 1
  • 1
G. Trennert
  • 561
  • 3
  • 12
  • 23
  • this works when you do *have* the form in your twig, which for me is not always the case.. (although that could be against good symfony practice..?) – userfuser Sep 01 '14 at 10:57
0

My solution using Symfony 2.8:

in the action Controller (when the form is submitted):

    $theToken = $request->get('Token');

    //Token Verification   
    $isValidToken = $this->isCsrfTokenValid('your_intention', $theToken);
    if ($isValidToken === false)
    {
        // Error
    }

check this page where I found the info: http://api.symfony.com/2.8/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.html#method_isCsrfTokenValid

baldychristophe
  • 183
  • 1
  • 6