I have two separate projects: UI(AngularJS) and Server(Symfony2).
I need to send cross-domain PUT request from AngularJS application to the Symfony2 app.
In the Symfony controller I passed $request
to the form->handleRequest();
and debug showed me, that form using this way is not submitted.
So, next I tried to pass $request
to the form->submit()
and got error "Invalid CSRF Token".
- How can I correctly process cross-domain data via Symfony forms?
I've read that passing
$request
to the submit() method is depricated. - How can I pass CSRF token to the form if I send it from UI via headers ? (I add csrf field to the request but it not processing at back-end)
EDITED: Currently I see that issue is related to CSRF token. No matter how I sending this token from UI, it's not processed on back-end and I always get "Invalid CSRF token" error.
I tried to add _token field directly to json object and set field name to _token via csrf_field_name
option into my formtype class.
I also tried to pass json_decode($request->getContent(), true)
to my form submit()
method, but after debugging I see, that submittedData is changing in next code :
// Symfony/Component/Form/Form.php, submit() method
if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
// here submittedData has csrf _token key/value
$event = new FormEvent($this, $submittedData);
$dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event);
$submittedData = $event->getData();
// now submittedData without _token key/value
}
EDITED2: more details. CsrfValidationListener
that using by Symfony Form component call $this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))
and this return false, the issue in next code:
// Symfony/Component/Security/Csrf/CsrfTokenManager.php
public function isTokenValid(CsrfToken $token)
{
if (!$this->storage->hasToken($token->getId())) {
return false;
}
return StringUtils::equals($this->storage->getToken($token->getId()), $token->getValue());
}
It seems csrf token is stored into session, so isTokenValid()
method return false
.
I continue to debug.
EDITED3:
as I can see, session is empty on calling $this->storage->hasToken($token->getId())
from CsrfTokenManager.php.
This is very strange, because I generate csrf token from my controller in next way:
$csrfToken = $this->get('security.csrf.token_manager')->refreshToken('Symfony');
And as I can see, refreshToken() method save csrf token into db:
// Csrf/CsrfTokenManager.php
public function refreshToken($tokenId)
{
$value = $this->generator->generateToken();
$this->storage->setToken($tokenId, $value);
return new CsrfToken($tokenId, $value);
}
// Csrf/TokenStorage/SessionTokenStorage.php
public function setToken($tokenId, $token)
{
if (!$this->session->isStarted()) {
$this->session->start();
}
$this->session->set($this->namespace.'/'.$tokenId, (string) $token);
}
But when I send data to the form, $this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))
that calls from preSubmit() method of CsrfValidationListener
return empty session.
just in case I add my security.yml settings, maybe I have missed something:
main:
pattern: ^/(?!login).+
stateless: true
simple_preauth:
authenticator: app_bundle.api_key_authenticator
provider: api_key_user_provider
anonymous: ~
logout: ~
login:
pattern: ^/login
stateless: false
simple_preauth:
authenticator: app_bundle.email_password_authenticator
provider: email_user_provider
anonymous: ~
Notice: I generate csrf-token under login firewall and try to access it from main firewall!
But I also tried to generate csrf-token in the same firewall. Nothing changed.
EDITED4:
I have configured custom session dir for tracking session creation. So, I can see, that on login I have session with all attributes, but when I doing PUT request, I notice that new session file is created and it contains something like this:
_sf2_attributes|a:0:{}_sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:"u";i:1449700968;s:1:"c";i:1449700968;s:1:"l";s:1:"0";}
Just empty session.