I am using Symfony 2.3 with FOSUserBundle and want to make a login form with AJAX. I found a view questions on SO (like this one) and even some other helpful sites (Adding an AJAX Login Form to a Symfony Project) that explained, how to use FOSUserBundle with ajax.
Everything works fine when I submit the form regularly, but using the ajax call I keep getting the message "Invalid CSRF token.".
Here are my configurations:
The Custom AuthenticationHandler:
namespace Moodio\Bundle\UserBundle\Handler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
private $router;
private $session;
private $translator;
private $csrf_provider;
/**
* Constructor
*/
public function __construct( RouterInterface $router, Session $session, $translator, $csrf_provider )
{
$this->router = $router;
$this->session = $session;
$this->translator = $translator;
$this->csrf_provider = $csrf_provider;
}
/**
* onAuthenticationSuccess
*/
public function onAuthenticationSuccess( Request $request, TokenInterface $token )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$array = array( 'success' => true); // data to return via JSON
$response = new Response( json_encode( $array ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
} else {// if form login
return parent::onAuthenticationSuccess($request, $token);
}
}
/**
* onAuthenticationFailure
*
*/
public function onAuthenticationFailure( Request $request, AuthenticationException $exception )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$result = array(
'success' => false,
'message' => $this->translator->trans($exception->getMessage(), array(), 'FOSUserBundle')
); // data to return via JSON
$response = new Response( json_encode( $result ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
} else {// if form login
// set authentication exception to session
$request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse( $this->router->generate( 'fos_user_security_login' ) );
}
}
}
src/Moodio/Bundle/MainBundle/services.yml:
services:
moodio_main.security.authentication_handler:
class: Moodio\Bundle\UserBundle\Handler\AuthenticationHandler
public: false
arguments:
- @router
- @session
- @translator
- @form.csrf_provider
security.yml firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
success_handler: moodio_main.security.authentication_handler
failure_handler: moodio_main.security.authentication_handler
logout: true
anonymous: true
my javascript
$('#_submit').click(function(e){
e.preventDefault();
var frm = $('form');
$.ajax({
type : frm.attr('method'),
url : frm.attr('action'),
data : frm.serialize(),
success : function(data, status, object) {
if(data.error) $('.error').html(data.message);
},
error: function(data, status, object){
console.log(data.message);
}
});
});
I tried some answers like in this answer, but it did not work for me. I actually don't understand why it should make a difference, because FOSUserBundle already creates the csrf-token and includes it in the twig template with {{ csrf_token }}.
At the moment I am using the standard FOSUserBundle templates (and only added the view lines of js I needed for the ajax).
One thing I also tried just to see if the csrf validation works, is to generate the csrf token in the onAuthenticationFailure function and check if it is valid just in the next line. In this case isCsrfTokenValid() returned true.
When I deactivate the csrf_provider in the firewall (in security.yml) I always get the message "Bad credentials" even though the credentials are right.