I have an example where I am trying to create an AJAX login using Symfony2 and FOSUserBundle. I am setting my own success_handler
and failure_handler
under form_login
in my security.yml
file.
Here is the class:
class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @see \Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener
* @param Request $request
* @param TokenInterface $token
* @return Response the response to return
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => true);
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
/**
* This is called when an interactive authentication attempt fails. This is
* called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @param Request $request
* @param AuthenticationException $exception
* @return Response the response to return
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => false, 'message' => $exception->getMessage());
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
}
This works great for handling both successful and failed AJAX login attempts. However, when enabled - I am unable to login via the standard form POST method (non-AJAX). I receive the following error:
Catchable Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given
I'd like for my onAuthenticationSuccess
and onAuthenticationFailure
overrides to only be executed for XmlHttpRequests (AJAX requests) and to simply hand the execution back to the original handler if not.
Is there a way to do this?
TL;DR I want AJAX requested login attempts to return a JSON response for success and failure but I want it to not affect standard login via form POST.
You must return a Response object in both case (Ajax or not). Add an `else' and you're good to go.
The default implementation is:
in
AbstractAuthenticationListener::onSuccess
If you want the FOS UserBundle form error support, you must use:
instead of:
In the first answer.
(of course remember about the header: use Symfony\Component\Security\Core\SecurityContext;)
This may not be what the OP asked, but I came across this question, and thought others might have the same problem that I did.
For those who are implementing an AJAX login using the method that is described in the accepted answer and who are ALSO using AngularJS to perform the AJAX request, this won't work by default. Angular's
$http
does not set the headers that Symfony is using when calling the$request->isXmlHttpRequest()
method. In order to use this method, you need to set the appropriate header in the Angular request. This is what I did to get around the problem:Before you use this method, be aware that this header does not work well with CORS. See this question
I handled this entirely with javascript:
Here is the AjaxFormDialog class. Unfortunately I have not ported it to a jQuery plugin by now... https://gist.github.com/1601803
I made a little bundle for new users to provide an AJAX login form : https://github.com/Divi/AjaxLoginBundle
You just have to replace to form_login authentication by ajax_form_login in the security.yml.
Feel free to suggest new feature in the Github issue tracker !