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.
I finally could solve my problem:
I found the Divi-AjaxLoginBundle which works like a charm. I am not sure, what the problem was, because I had actually the same AuthenticationHandlers like this bundle, but there are also some files in the DependencyInjection folder which maybe did the job.
Thanks to all who tried to help me.
If you are using the very latest version of FOSUserBundle, the csrf provider has changed to security.csrf.token_manager. Your form could be generating the token with form.csrf_provider and FOSUB could be using another. Either:
Either way, clear your cookies and restart your dev server to make sure there is no legacy information in there.