Authenticate users via access token in Symfony 3
<h1>The problem</h1>

<hr>

<p>I want users to be authenticated via an access token that is supplied as a GET parameter to the first request.</p>

<p>I have never implemented such a thing in Symfony, so I followed the steps outlined in <a href="http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html" rel="nofollow">How to Create a custom Authentication Provider</a>, but it 'doesn't work'. The <code>authenticate</code> method of the <code>AuthenticationProviderInterface</code> is not triggered.</p>

<h1>What I have tried</h1>

<hr>

<p>Because it is a lot of configuration mostly, I don't even know how to debug this. This is what I have concluded so far: Only the <code>AccessTokenProvider</code> gets constructed, nothing else.</p>

<h1>The code</h1>

<hr>

<p>These are the relevant parts of the system:</p>

<p><strong>security.yml</strong></p>

<pre><code>security:

    # Snip default (empty) in_memory provider

    firewalls:
        # Snip dev and main (symfony default)

        accesstoken_secured:
            pattern:       ^/admin/
            accesstoken:   true
</code></pre>

<p><strong>services.yml</strong></p>

<pre><code>services:
    accesstoken.security.authentication.provider:
        class: AppBundle\Security\Authentication\Provider\AccessTokenProvider
        arguments:
            - '' # User Provider
            - '%kernel.cache_dir%/security/nonces'
        public: false

    accesstoken.security.authentication.listener:
        class: AppBundle\Security\Firewall\AccessTokenListener
        arguments: ['@security.token_storage', '@security.authentication.manager']
        public: false
</code></pre>

<p><strong>AccessTokenFactory</strong></p>

<pre><code>class AccessTokenFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.accesstoken.'.$id;
        $container
            -&gt;setDefinition($providerId, new DefinitionDecorator('accesstoken.security.authentication.provider'))
            -&gt;replaceArgument(0, new Reference($userProvider))
        ;

        $listenerId = 'security.authentication.listener.accesstoken.'.$id;
        $container-&gt;setDefinition($listenerId, new DefinitionDecorator('accesstoken.security.authentication.listener'));

        return array($providerId, $listenerId, $defaultEntryPoint);
    }

    public function getPosition()
    {
        return 'pre_auth';
    }

    public function getKey()
    {
        return 'accesstoken';
    }

    public function addConfiguration(NodeDefinition $node)
    {
    }
}
</code></pre>

<p><strong>AccessTokenProvider</strong></p>

<pre><code>class AccessTokenProvider implements AuthenticationProviderInterface
{
    private $userProvider;

    public function __construct(UserProviderInterface $userProvider)
    {
        $this-&gt;userProvider = $userProvider;
    }

    public function authenticate(TokenInterface $token)
    {
        $user = $this-&gt;userProvider-&gt;loadUserByAccessToken($token-&gt;getAttribute('token'));

        if ($this-&gt;isTokenValid($token)) {
            $authenticatedToken = new AccessToken(['role_user']);
            $authenticatedToken-&gt;setUser($user);

            return $authenticatedToken;
        }

        throw new AuthenticationException('The WSSE authentication failed.');
    }

    protected function isTokenValid(AccessToken $token)
    {
        //TODO: Implement
        return (bool)$token-&gt;token;
    }

    public function supports(TokenInterface $token)
    {
        return $token instanceof AccessToken;
    }
}
</code></pre>

<p><strong>AccessTokenListener</strong></p>

<pre><code>class AccessTokenListener
{
    protected $tokenStorage;
    protected $authenticationManager;

    /**
     * AccessTokenListener constructor.
     * @param TokenStorageInterface $tokenStorage
     * @param AuthenticationManagerInterface $authenticationManager
     */
    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
    {
        $this-&gt;tokenStorage = $tokenStorage;
        $this-&gt;authenticationManager = $authenticationManager;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $accesstoken = $request-&gt;get('accesstoken');

        $token = new AccessToken();
        $token-&gt;token = $accesstoken;

        try {
            $authToken = $this-&gt;authenticationManager-&gt;authenticate($token);
            $this-&gt;tokenStorage-&gt;setToken($authToken);

            return;
        } catch (AuthenticationException $failed) {
            // ... you might log something here
        }

        // By default deny authorization
        $response = new Response();
        $response-&gt;setStatusCode(Response::HTTP_FORBIDDEN);
        $event-&gt;setResponse($response);
    }
}
</code></pre>

<p><strong>AccessToken</strong></p>

<pre><code>class AccessToken extends AbstractToken
{
    public $token;

    /**
     * AccessToken constructor.
     * @param array $roles
     */
    public function __construct(array $roles = array())
    {
        parent::__construct($roles);

        // If the user has roles, consider it authenticated
        $this-&gt;setAuthenticated(count($roles) &gt; 0);
    }

    /**
     * Returns the user credentials.
     *
     * @return mixed The user credentials
     */
    public function getCredentials()
    {
        return '';
    }
}
</code></pre>