Customizing Success Handling

So, your authentication is working. Yes! Now, what if you need to hook into what happens next? For example, maybe you need to redirect to a special page, or return JSON instead of a redirect in some cases. Or perhaps you want to store the last login time of your user. All that is possible and easy.

onAuthenticationSuccess()

Every authenticator has a onAuthenticationSuccess() method. This is called whenever authentication is completed, and it has one job: create a Response that should be sent back to the user. This could be a redirect back to the last page the user visited or return null and let the request continue (see API token).

If you extend certain authenticators - like AbstractFormLoginAuthenticator - then this method is filled in for you automatically. But you can feel free to override it and customize.

Sending back JSON for AJAX

Suppose your login form uses AJAX. Instead of redirecting after success, you probably want it to return some sort of JSON response. Just override onAuthenticationSuccess():

113 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 7
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
... line 10
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
... lines 12 - 17
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 20 - 86
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// AJAX! Return some JSON
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
// maybe send back the user's id
array('userId' => $token->getUser()->getId())
);
}
// for non-AJAX requests, return the normal redirect
return parent::onAuthenticationSuccess($request, $token, $providerKey);
}
... lines 100 - 111
}

That's it! If you login via AJAX, you'll receive a JSON response instead of the redirect.

Performing an Action on Login

Suppose you want to store the "last login" time for the user in the database. You could override onAuthenticationSuccess(), update the User and save.

But, there's a better way: Symfony security system dispatches a security.interactive_login event that you can hook into. Why is this better? Because this will be called whenever a user logs in, whether it is via this authenticator, another authenticator or some non-Guard system.

Tip

Everything in this section works in normal Symfony, even without Guard!

First, make sure you have a column on your user:

150 lines src/AppBundle/Entity/User.php
... lines 1 - 19
class User implements UserInterface
{
... lines 22 - 53
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $lastLoginTime;
... lines 58 - 143
public function setLastLoginTime(\DateTime $lastLoginTime)
{
$this->lastLoginTime = $lastLoginTime;
}
}

Next, create an event subscriber. This will be called whenever a user logs in. It's job is simple: update this lastLoginTime property and save the User:

34 lines src/AppBundle/EventListener/LastLoginSubscriber.php
... lines 1 - 2
namespace AppBundle\EventListener;
use AppBundle\Entity\User;
use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class LastLoginSubscriber implements EventSubscriberInterface
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
/** @var User $user */
$user = $event->getAuthenticationToken()->getUser();
$user->setLastLoginTime(new \DateTime());
$this->em->persist($user);
$this->em->flush($user);
}
public static function getSubscribedEvents()
{
return array(SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin');
}
}

Tip

Not familiar with listeners or susbcribers? Check out Interrupt Symfony with an Event Subscriber

Now, just register this as a service and tag it so that Symfony know about the subscriber:

20 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 14
app.last_login_subscriber:
class: AppBundle\EventListener\LastLoginSubscriber
arguments: ["@doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_subscriber }

That's all you need. Next time you login, the User's lastLoginTime will automatically be updated in the database.

Leave a comment!