401 error - JWT Token not found using fetch

2019-08-01 19:23发布

问题:

I am new to react, and mostly to javascript. I am using react for a web app hosted on localhost:3000 with node.js and symfony 2.8 for my back end, hosted on localhost:80 with apache, that exposes an api secured with lexik and fos user. Before securing the api, everything works fine. After securing the api, I get a 401 error when using fetch as follow:

let myToken = localStorage.getItem('auth_token') //token properly retrieved
let myHeaders = new Headers();
myHeaders.append("Authorization", myToken)
return fetch(
    pathToMyResource,
    {
        method: "post",
        headers: myHeaders
    }
).then(do stuff with the answer)

This gives me the following error in the headers of the answer to the preflight request:

Request URL:http://127.0.0.1/edsa-food_app_symfony_2.8/api/site/1/get
Request Method:OPTIONS
Status Code:401 Unauthorized
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade

And the response:

{"code":401,"message":"JWT Token not found"}

I also checked, just because I don't really understand how this is supposed to work, with

credentials: 'include'

And

mode: 'no-cors'

But wasn't more successful.

When I build and send this same request using php from localhost:8000, it works perfectly. I use the following code:

$url = pathToMyResource
$options = array(
    'http' => array(
        'header'  => "Authorization: Bearer $token"
        'method'  => 'POST',
    )
);

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
var_dump($result);

If I understand well it cannot be the apache error described in Lexik JWT Token not found: the default .htaccess at the root of any symfony 2.8 project includes:

RewriteEngine On
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]

I checked and mod_rewrite is enabled.

Below are the headers as reported in Chrome:

**General**
Request URL:http://127.0.0.1/edsa-food_app_symfony_2.8/api/site/1/get
Request Method:OPTIONS
Status Code:401 Unauthorized
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade

**Response Headers**
view source
Access-Control-Allow-Origin:*
Cache-Control:no-cache
Connection:Keep-Alive
Content-Length:44
Content-Type:application/json
Date:Tue, 08 Aug 2017 01:05:20 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.18 (Win32) PHP/5.6.19
WWW-Authenticate:Bearer
X-Powered-By:PHP/5.6.19

**Request Headers**
view source
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-GB,en;q=0.8,en-US;q=0.6,fr;q=0.4,zh-CN;q=0.2,zh;q=0.2,it;q=0.2
Access-Control-Request-Headers:authorization
Access-Control-Request-Method:POST
Connection:keep-alive
Host:127.0.0.1
Origin:http://localhost:3000
Referer:http://localhost:3000/service-worker.js
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

And for the POST request itself:

**General**
Request URL:http://127.0.0.1/edsa-food_app_symfony_2.8/api/site/1/get
Referrer Policy:no-referrer-when-downgrade

**Request Headers**
Provisional headers are shown
authorization:Bearer eyJhbGciOiJSUzI1NiJ9.eyJyb2xlcyI6WyJST0xFX1VTRVIiXSwidXNlcm5hbWUiOiJhbnRvaW5lIiwiaWF0IjoxNTAyMTM4ODUwLCJleHAiOjE1MDIyMjUyNTB9.wpWZZLf5wWjrU0-eAQUR0XiDTvf1jRtiJuGIHoYm7Yo4lGhOn-_bYuJIdv71ZUiuYfaaMxOW4xzXN3JjB9KfrWmXD4jqI6CnHFYZISGlYvAGJayD_z8CMIEdvrMXrbb6_nEc0CaB68BOf7wqJyoNatFKlepwmCHevsRtTIbhc_GviQf_U_Fw30ShtogIJBLqmVD4ex-j0_9QbblAIqNhc8c0thEFYtN7FVepLehCzBNCTNL8l-mxYEFTrUYLKwSt4lRahgTsv4Ozhxl300xz7BbdQEr3ph2i4ssVcvokpEO2C07QicWSwXFx1Vx-2a6XbkoeorTz_P7WstBzinMdv0etlIz2VYN_oUmHaxDu9jlsu90nZlL2Ea7Ak7dSJaNYzmB11yga_OSiWMpzWTjaqP3MLJuS1O5keHMbliERgnBJM_rsMZ-mkVSM8j4t31L1QJCfP0RW-Vfj3biYR1uYNfXwbbdqmIpn6b39qOCY9l4F99dK6R-PKq5ZeBHEfy-OpN39NFmaMQQX5gYCQ3TzVdeou6-hjpqRnNl8dc0HYzAl3fbU102JMefZNvCsIdcI6WDCiyWZO9Viy-z9REAVF4Pr9bLFpc-Q6Lqdj32lt1-yy6i75IOavrPqRilhRh2z_V7rP_DqahrLhFSDPPVg_gcqb8n31_6q3wtyzx16aJ4
Origin:http://localhost:3000
Referer:http://localhost:3000/restaurant
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

I am lost.

回答1:

You need to configure your backend so that it responds to the preflight with a 200 or 204.

You may be able to handle that at the Apache level by adding your your Apache config this:

RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

But if that doesn’t fix is then you gonna need to dig further and figure out what part of your backend system is causing authentication to be required for OPTIONS requests.

The reason is because what’s happening here is this:

  1. Your code tells your browser it wants to send a request with the Authorization header.
  2. Your browser says, OK, requests with the Authorization header require me to do a CORS preflight OPTIONS to make sure the server allows requests with that header.
  3. Your browser sends the OPTIONS request to the server without the Authorization header—because the whole purpose of the OPTIONS check is to see if it’s OK to send that.
  4. The server sees the OPTIONS request but instead of responding to it in a way that indicates it allows Authorization in requests, it rejects it with a 401 since it lacks that header.
  5. Your browser expects a 200 or 204 response for the CORS preflight but instead gets that 401 response. So your browser stops right there and never tries the POST request from your code.