Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle ephemeral tokens when using SimplePreAuthenticator & PreAuthenticatedToken

I'm using the SimpleAuthentication feature as described here : http://symfony.com/doc/2.6/cookbook/security/api_key_authentication.html

The purpose is to provide an authentication through a token string passed as a query (get) parameter or a header. This token, let's call it TemporaryAccessToken to avoid confusion with the Sf2 token, is generated by a controller, sent to the user by email (not described here) and should be available for a limited amount of time (there's a valid_until \DateTime column in the dedicated entity).

For the record, when the protected page (by the simple_user_account firewall) is accessed the first time the authentication process goes like this :

  • SimplePreAuthenticationListener is triggered
  • Basically, calls MyAuthenticator->createToken()
  • Then calls AuthenticationManager->authenticate(), which calls MyAuthenticator->authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)

Problem is : once the user has been authenticated, and because I'm using stateless: false in the firewall configuration, the authentication process is not triggered again since there's already a valid Sf2 token in session.

I see 2 logic solutions but I can't figure out how to do this properly :

  • make the session valid until the date which is set into my TemporaryAccessToken.valid_until column. Is there a way to achieve that "natively" ? (I saw that the remember_me firewall has a lifetime parameter)
  • or, be able to re-check the TemporaryAccessToken validity (see the if( $accessToken && $accessToken->isValid() ) line in my Authenticator)

app/config/security.yml

firewalls:
    simple_user_account:
        pattern:   ^/account/access
        stateless:  false
        simple_preauth:
            authenticator: app.security.simple_user_authenticator
        logout:
            path:   /logout_simple
            target: /

My authenticator class looks like :

class SimpleUserAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{
    /**
     * @var AccessTokenManager
     */
    private $accessTokenManager;

    /**
     * SimpleUserAuthenticator constructor.
     * @param AccessTokenManager $accessTokenManager
     */
    public function __construct(AccessTokenManager $accessTokenManager)
    {
        $this->accessTokenManager = $accessTokenManager;
    }

    public function createToken(Request $request, $providerKey)
    {
        $TemporaryAccessToken = $request->query->get('simple_user_token');
        if (!$TemporaryAccessToken) {
            throw new BadCredentialsException('No simple_user token found');
        }

        return new PreAuthenticatedToken(
            'anonymous',
            $TemporaryAccessToken,
            $providerKey
        );
    }


    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        if( $token->getUser() instanceof SimpleUser )
        {
            $newToken = new PreAuthenticatedToken(
                $token->getUser(),
                $token->getCredentials(),
                $providerKey,
                array('ROLE_USER')
            );
            return $newToken;
        }

        $accessToken = $this->accessTokenManager->getRepository()->findOneByToken($token->getCredentials());
        if( $accessToken && $accessToken->isValid() )
        {
            $user = $userProvider->loadUserByUsername($accessToken->getAccount()->getEmailCanonical());
            $newToken = new PreAuthenticatedToken(
                $user,
                $token->getCredentials(),
                $providerKey,
                array('ROLE_USER')
            );
            return $newToken;
        }
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $response =  new RedirectResponse(
            '/login'
        );
        return $response;
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
    }


}

Symfony2 version : 2.5.6

like image 238
foobar Avatar asked Dec 08 '15 08:12

foobar


1 Answers

Well, if you need to use session, you can set lifetime under your config.yml session settings:

# config.yml
framework:

    session:
        # handler_id set to null will use default session handler from php.ini
        handler_id: ~
        lifetime: 3600

You can change lifetime value to anything in seconds (it defaults to 3600 or 1 hour)

Also, you can try to configure garbage collector.

If this answer was not helpful, please, let me know.

like image 88
Sergio Ivanuzzo Avatar answered Sep 26 '22 14:09

Sergio Ivanuzzo