Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Login programmatically and stay logged in

I'm trying to implement single sign on access to a website using Symfony2.

The authentication itself seems to work fine, but only for the initial page. On the next page that is loaded the user is not logged in anymore.

Relevant code:

$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$event = new InteractiveLoginEvent($request, $token);

$this->get("event_dispatcher")->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $event);
$this->get("security.context")->setToken($token);

return $this->redirect($this->generateUrl('sonata_user_profile_show'));

First page (without the redirect):

Initial page - logged in

Second page:

Second page - Not logged in anymore

like image 581
Nick Stemerdink Avatar asked Jan 14 '13 15:01

Nick Stemerdink


1 Answers

Only the following code is necessary for custom log-in.

$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get("security.context")->setToken($token);

return $this->redirect($this->generateUrl('sonata_user_profile_show'));

What this does is setting the UsernamePasswordToken in the security context. This token (and also the user) will be serialized and put in the session. On the next page the the token will be unserialized from the session and the, also unserialized, user will be refreshed.

The user-provider in the FOSUserBundle does this refreshing using the id of the unserialized user.

Also, Doctrine2 in some cases uses proxy-classes as entity-classes instead of the original entity class. This proxy-class overwrites the "getId()" function of the entity by a complex lazy-loading complex implementation.

This together could lead to the fact that, when you put the Doctrine2 proxy-object in the UserPasswordToken, the "getId()" of the serialized and then unserialized proxy-object will not return the original id. When that happens the user can not be refreshed by the user-provider, and the token will become invalid.

A fix for this is creating a custom user-provider that overwrites the "refreshUser()" by a refreshing using the username (or an other unique property).

//...
class UserProvider extends FOSUserProvider
{
    /**
     * {@inheritDoc}
     */
    public function refreshUser(SecurityUserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Expected an instance of User, but got "%s".', get_class($user)));
        }

        if (null === $reloadedUser = $this->userManager->findUserBy(array('username' => $user->getUsername()))) {
            throw new UsernameNotFoundException(sprintf('User with username "%s" could not be reloaded.', $user->getUsername()));
        }

        return $reloadedUser;
    }
}
like image 168
Arno Avatar answered Oct 18 '22 22:10

Arno