title: JWT Token not found when I am trying to access secured endpoints with apl platform (admin component)
body: <p>I am trying to build my API with API Platform (Symfony 4) and everything seems to be fine but when I am using admin component to access <strong>secured endpoints</strong> (after I have been logged in successfully) server always returns 401 "JWT Token not found". I've found that <strong>Admin component</strong> is not sending the <strong>'Authorization' header with token</strong> so server can't check token to authorize a request. </p>
  
  <p>Since the Admin login works fine and token is always stored in localStorage (after successful login) I've tried to send a request with <strong>cURL</strong> and <strong>Postman</strong> with the stored token and server always returns valid response. </p>
  
  <p>Any idea?</p>
  
  <h1>Update 1</h1>
  
  <p>I am using code from API Platform documentation (<a href="https://api-platform.com/docs/admin/authentication-support/#authentication-support" rel="nofollow noreferrer">https://api-platform.com/docs/admin/authentication-support/#authentication-support</a>).</p>
  
  <p><strong>App.js</strong></p>
  
  <pre><code>import React from 'react';
  import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
  import {fetchHydra as baseFetchHydra, HydraAdmin, hydraClient} from '@api-platform/admin';
  import authProvider from './authProvider';
  import {Redirect, Route} from 'react-router-dom';
  
  const entrypoint = process.env.REACT_APP_API_ENTRYPOINT;
  const fetchHeaders = {'Authorization': `Bearer ${localStorage.getItem('token')}`};
  const fetchHydra = (url, options = {}) =&gt; baseFetchHydra(url, {
    ...options,
    headers: new Headers(fetchHeaders),
  });
  const dataProvider = api =&gt; hydraClient(api, fetchHydra);
  const apiDocumentationParser = entrypoint =&gt;
    parseHydraDocumentation(entrypoint, {
      headers: new Headers(fetchHeaders),
    }).then(
      ({api}) =&gt; ({api}),
      result =&gt; {
        const {api, status} = result;
  
        if (status === 401) {
          return Promise.resolve({
            api,
            status,
            customRoutes: [
              &lt;Route path="/" render={() =&gt; &lt;Redirect to="/login"/&gt;}/&gt;,
            ],
          });
        }
  
        return Promise.reject(result);
      }
    );
  
  export default () =&gt; (
    &lt;HydraAdmin
      apiDocumentationParser={apiDocumentationParser}
      authProvider={authProvider}
      entrypoint={entrypoint}
      dataProvider={dataProvider}
    /&gt;
  );
  </code></pre>
  
  <p><strong>authProvider.js</strong></p>
  
  <pre><code>import {AUTH_CHECK, AUTH_ERROR, AUTH_LOGIN, AUTH_LOGOUT} from 'react-admin';
  
  const authenticationTokenUri = `${process.env.REACT_APP_API_ENTRYPOINT}/authentication_token`;
  
  export default (type, params) =&gt; {
    switch (type) {
      case AUTH_LOGIN:
        const {username, password} = params;
        const request = new Request(authenticationTokenUri, {
          method: 'POST',
          body: JSON.stringify({email: username, password}),
          headers: new Headers({'Content-Type': 'application/json'}),
        });
  
        return fetch(request)
          .then(response =&gt; {
            if (response.status &lt; 200 || response.status &gt;= 300) throw new Error(response.statusText);
  
            return response.json();
          })
          .then(({token}) =&gt; {
            localStorage.setItem('token', token); 
            window.location.replace('/');
          });
  
      case AUTH_LOGOUT:
        localStorage.removeItem('token');
        break;
  
      case AUTH_ERROR:
        if (401 === params.status || 403 === params.status) {
          localStorage.removeItem('token');
  
          return Promise.reject();
        }
        break;
  
      case AUTH_CHECK:
        return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
  
      default:
        return Promise.resolve();
    }
  }
  </code></pre>
  
  <p><strong>index.js</strong></p>
  
  <pre><code>import React from 'react';
  import ReactDOM from 'react-dom';
  import App from './App';
  import * as serviceWorker from './serviceWorker';
  
  ReactDOM.render(&lt;App /&gt;, document.getElementById('root'));
  
  serviceWorker.unregister();
  </code></pre>
  
  <h1>Update 2</h1>
  
  <p>I have found that the authorization header with token is sent with some requests, but all requests to API endpoints are sent without authorization header that results in 401.</p>
  
  <p><a href="https://i.stack.imgur.com/BSCiS.png" rel="nofollow noreferrer">dev tools network img</a></p>
  
  <p><a href="https://i.stack.imgur.com/zHdVU.png" rel="nofollow noreferrer">correct request headers img</a></p>
  
  <p><a href="https://i.stack.imgur.com/U1O2R.png" rel="nofollow noreferrer">a endpoint request headers - miss authorization header img</a></p>
  
  <p><a href="https://i.stack.imgur.com/yiVLO.png" rel="nofollow noreferrer">dev tools console log img</a></p>
  
  <h1>Update 3</h1>
  
  <p><strong>security.yaml</strong></p>
  
  <pre><code>security:
      encoders:
          App\Entity\User:
              algorithm: bcrypt
      providers:
          app_user_provider:
              entity:
                  class: App\Entity\User
                  property: email
      firewalls:
          dev:
              pattern: ^/(_(profiler|wdt)|css|images|js)/
              security: false
          main:
              anonymous: true
              stateless: true
              provider: app_user_provider
              json_login:
                  check_path: /api/authentication_token
                  username_path: email
                  password_path: password
                  success_handler: lexik_jwt_authentication.handler.authentication_success
                  failure_handler: lexik_jwt_authentication.handler.authentication_failure
              guard:
                  authenticators:
                      - lexik_jwt_authentication.jwt_token_authenticator
  
      access_control:
           # api platform has prefix /api
           - { path: ^/api/authentication_token$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
           - { path: ^/api/, roles: IS_AUTHENTICATED_FULLY } 
  </code></pre>
  
  <p><strong>lexik_jwt_authentication.yaml</strong></p>
  
  <pre><code>lexik_jwt_authentication:
      secret_key: '%env(resolve:JWT_SECRET_KEY)%'
      public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
      pass_phrase: '%env(JWT_PASSPHRASE)%' 
  </code></pre>
  
  <p><strong>Greeting entity</strong>
      
  
  <pre><code>namespace App\Entity;
  
  use ApiPlatform\Core\Annotation\ApiResource;
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Validator\Constraints as Assert;
  
  /**
   * This is a dummy entity. Remove it!
   *
   * @ApiResource
   * @ORM\Entity
   */
  class Greeting
  {
      /**
       * @var int The entity Id
       *
       * @ORM\Id
       * @ORM\GeneratedValue
       * @ORM\Column(type="integer")
       */
      private $id;
  
      /**
       * @var string A nice person
       *
       * @ORM\Column
       * @Assert\NotBlank
       */
      public $name = '';
  
      public function getId(): int
      {
          return $this-&gt;id;
      }
  }
  </code></pre>
  
  <h1>Update 4</h1>
  
  <p>As you can see a request to a secured endpoint with Postman works.
  <a href="https://i.stack.imgur.com/BzzLl.png" rel="nofollow noreferrer">Postman printscreen</a></p>