title: Symfony guard : access_control has no effect
body: <p>I'm new to symfony, and I'm trying to get a basic guard authenticator based on JWT. The work is mainly from the article here, where I have removed any user check (for now): <a href="http://kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guard" rel="nofollow noreferrer">http://kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guard</a></p>
  
  <p>I think there's something I don't get, because I can't make it work. More precisely, it works everywhere, even in the exceptions I put in place.</p>
  
  <p>Here is the Check service, basically the same as the article, without the users management, and with a bit of logging :</p>
  
  <pre><code>&lt;?php
  
  namespace AppBundle\Security;
  
  use Lexik\Bundle\JWTAuthenticationBundle\Encoder\DefaultEncoder;
  use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor;
  use Symfony\Component\HttpFoundation\JsonResponse;
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;
  use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  use Symfony\Component\Security\Core\Exception\AuthenticationException;
  use Symfony\Component\Security\Core\User\UserInterface;
  use Symfony\Component\Security\Core\User\UserProviderInterface;
  use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
  use Psr\Log\LoggerInterface;
  
  class TokenAuthenticator extends AbstractGuardAuthenticator
  {
      private $jwtEncoder;
      private $logger;
  
      public function __construct(DefaultEncoder $jwtEncoder, LoggerInterface $logger)
      {
          $this-&gt;logger = $logger;
          $this-&gt;jwtEncoder = $jwtEncoder;
      }
  
      public function start(Request $request, AuthenticationException $authException = null)
      {
          $route = $request-&gt;attributes-&gt;get('_route');
          $url = $request-&gt;getUri();
          $this-&gt;logger-&gt;info($route . ' : ' . $url);
          return new JsonResponse('Authentication required', 401);
      }
  
      public function getCredentials(Request $request)
      {
  
          if(!$request-&gt;headers-&gt;has('Authorization')) {
              return;
          }
  
          $extractor = new AuthorizationHeaderTokenExtractor(
              'Bearer',
              'Authorization'
          );
  
          $token = $extractor-&gt;extract($request);
  
          if(!$token) {
              return;
          }
  
          return $token;
      }
  
      public function getUser($credentials, UserProviderInterface $userProvider)
      {
          $data = $this-&gt;jwtEncoder-&gt;decode($credentials);
  
          if(!$data){
              return;
          }
  
          $username = $data['username'];
  
          // TODO get user from user collection
          $user = ['username' =&gt; $username];
  
          // Is user is encoded in token and exists, then it's fine
          if(!$user){
              return;
          }
  
          return $user;
      }
  
  
      public function checkCredentials($credentials, UserInterface $user)
      {
          return true;
      }
  
      public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
      {
          return new JsonResponse([
              'message' =&gt; $exception-&gt;getMessage()
          ], 401);
      }
  
      public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
      {
          return;
      }
  
      public function supportsRememberMe()
      {
          return false;
      }
  
  }
  </code></pre>
  
  <p>And the security.yml, with EVERYTHING excluded, just to check the behavior.</p>
  
  <pre><code># To get started with security, check out the documentation:
  # http://symfony.com/doc/current/security.html
  security:
  
      # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
      providers:
          in_memory:
              memory: ~
  
      firewalls:
          # disables authentication for assets and the profiler, adapt it according to your needs
          dev:
              pattern: ^/(_(profiler|wdt)|css|images|js)/
              security: false
  
  #################################
  # Secured section
  #
  
          # Custom authentication firewall for all request thats starts from /api
          api:
              pattern: ^/api
              guard:
                  authenticators:
                      - app.token_authenticator
  
  
  #################################
  # Main Configuration
  #
  
          main:
              anonymous: ~
              # activate different ways to authenticate
  
              # http_basic: ~
              # http://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
  
              # form_login: ~
              # http://symfony.com/doc/current/cookbook/security/form_login_setup.html
  
  
  
      access_control:
          #- { path: ^/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
          #- { path: ^/version, roles: IS_AUTHENTICATED_ANONYMOUSLY }
          #- { path: ^/api, roles: [ROLE_USER, ROLE_API_USER] }
          - { path: ^/api, roles: IS_AUTHENTICATED_ANONYMOUSLY }
  
          #- { path: ^/(css|js), roles: IS_AUTHENTICATED_ANONYMOUSLY }
          #- { path: ^/(_wdt|_profiler), roles: IS_AUTHENTICATED_ANONYMOUSLY }
          #- { path: ^/, roles: ROLE_USER }
  </code></pre>
  
  <p>I just put the guard in place on ^/api, and put a control_access on the same path to allow ANONYMOUS. I expect that the guard service is not called on any path with that configuration, but it's called everytime. I guess I'm missing some understanding on how it works.
  What I understand is:</p>
  
  <ul>
  <li>Access control is checked before anything else</li>
  <li>If there's a matching line, it takes it (the first one)</li>
  <li>If IS_AUTHENTICATED_ANONYMOUSLY is set, then the firewall is not checked</li>
  <li>Else, the next check is the firewall configuration, where it tells to check with the TokenAuthenticator</li>
  </ul>
  
  <p>The initial aim is to lock <em>/api</em>, except <em>/api/auth</em> and <em>/api/version</em> that can be accessed without control.</p>
  
  <p>Thanks for the help, I think after 1 day and a half on it, I'm not able to think straight about it.</p>