Custom Request Headers not being sent with a JavaScript Fetch Request - javascript

I am trying to use JavaScripts Fetch() API to send an AJAX request to my PHP OAuth server.
My issue is that I need to send a Request header Authorization with
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjM3MWFjZTRiZWE1NmViZDQ5YzQ1OTFkMmJiY2E4NGMwOTM2N2JiMjZmNjZiMmJkOTkxZmE3YmU0NTJjYWZmODBkMmZlYmJhYjFjNzMyMmM1In0.eyJhdWQiOiIxIiwianRpIjoiMzcxYWNlNGJlYTU2ZWJkNDljNDU5MWQyYmJjYTg0YzA5MzY3YmIyNmY2NmIyYmQ5OTFmYTdiZTQ1MmNhZmY4MGQyZmViYmFiMWM3MzIyYzUiLCJpYXQiOjE0ODA1NTE5NzIsIm5iZiI6MTQ4MDU1MTk3MiwiZXhwIjoxNTA3NzY3OTcyLCJzdWIiOiI0Iiwic2NvcGVzIjpbImNydWQtYm9va21hcmstY29sbGVjdGlvbnMiLCJjcnVkLWJvb2ttYXJrLXRhZ3MiLCJjcnVkLWJvb2ttYXJrcyJdfQ.YZWbwDXx4gsUtmvLP1GOY2XUnQ5MC030ymfoV6AYjgQMOqKnsmwrsYrTv5q6MVzo50_SMLipyA9t2VgpZkXj6tOdzA-v9idGnV8JVy-GZeceRlhgl7mpnAe1icI5P62mfhHQiyAdF2cfH6OKsy3ONzyzXRw1_pm-5o_qzcNbUGIATnKr5jXbYElRZZlh7-TUBQ2aSnEsu_fOR2rX5zZ_2dhpAMyE5GOK-UODhjs9PQVLXEEtnlzXyRIdjv-2YTuwJzLryHoooP4N5SncvkBlA6mk0IXVnVnJAMkomnkulofmn1k1niK6Dnzk8OANjbi_uPNbj4W2EtHA0tENNKDfAJ9maiHQZgmpWVk_rkKPrw04BogJNq682mgZhRwYjMM8tD7Rzmrb1DRI8_dM60O5AL5Nm5sxXzKd946OGmMLSQ_OnvAXsAN52KdnlQNW2RzMkFErdrXADMf1g1u7WjH-yo7G9wf-2QMVt8ejrWIbj3_7eBUHIWc4VYg_-IzFMkXl_WXKh12n1RnB62nvyz0IQ5aHbNP0_jIFZixHs0CjuNKikoWguEWfRL78eb2cTyxYMn3E2Yh31RMMaJzF1mM-we05D9WPyQUPKNMuIUQVVRI4GOvny9IJUuGbjcQVLsA-EMbXTBcf9LdRo62gQTNyeCYIshgw3MhX9OazDGK7Xks'
This is the code I have right now....
var app = {
init: function() {
this.apitest();
},
apitest: function() {
var request = new Request('http://bookmarkapi.dev/api/user', {
method: 'GET',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjM3MWFjZTRiZWE1NmViZDQ5YzQ1OTFkMmJiY2E4NGMwOTM2N2JiMjZmNjZiMmJkOTkxZmE3YmU0NTJjYWZmODBkMmZlYmJhYjFjNzMyMmM1In0.eyJhdWQiOiIxIiwianRpIjoiMzcxYWNlNGJlYTU2ZWJkNDljNDU5MWQyYmJjYTg0YzA5MzY3YmIyNmY2NmIyYmQ5OTFmYTdiZTQ1MmNhZmY4MGQyZmViYmFiMWM3MzIyYzUiLCJpYXQiOjE0ODA1NTE5NzIsIm5iZiI6MTQ4MDU1MTk3MiwiZXhwIjoxNTA3NzY3OTcyLCJzdWIiOiI0Iiwic2NvcGVzIjpbImNydWQtYm9va21hcmstY29sbGVjdGlvbnMiLCJjcnVkLWJvb2ttYXJrLXRhZ3MiLCJjcnVkLWJvb2ttYXJrcyJdfQ.YZWbwDXx4gsUtmvLP1GOY2XUnQ5MC030ymfoV6AYjgQMOqKnsmwrsYrTv5q6MVzo50_SMLipyA9t2VgpZkXj6tOdzA-v9idGnV8JVy-GZeceRlhgl7mpnAe1icI5P62mfhHQiyAdF2cfH6OKsy3ONzyzXRw1_pm-5o_qzcNbUGIATnKr5jXbYElRZZlh7-TUBQ2aSnEsu_fOR2rX5zZ_2dhpAMyE5GOK-UODhjs9PQVLXEEtnlzXyRIdjv-2YTuwJzLryHoooP4N5SncvkBlA6mk0IXVnVnJAMkomnkulofmn1k1niK6Dnzk8OANjbi_uPNbj4W2EtHA0tENNKDfAJ9maiHQZgmpWVk_rkKPrw04BogJNq682mgZhRwYjMM8tD7Rzmrb1DRI8_dM60O5AL5Nm5sxXzKd946OGmMLSQ_OnvAXsAN52KdnlQNW2RzMkFErdrXADMf1g1u7WjH-yo7G9wf-2QMVt8ejrWIbj3_7eBUHIWc4VYg_-IzFMkXl_WXKh12n1RnB62nvyz0IQ5aHbNP0_jIFZixHs0CjuNKikoWguEWfRL78eb2cTyxYMn3E2Yh31RMMaJzF1mM-we05D9WPyQUPKNMuIUQVVRI4GOvny9IJUuGbjcQVLsA-EMbXTBcf9LdRo62gQTNyeCYIshgw3MhX9OazDGK7Xks'
})
});
return fetch(request).then(app.checkStatus).then(app.parseJSON);
},
checkStatus: function(response) {
if (response.status >= 200 && response.status < 300) {
return response
} else {
var error = new Error(response.statusText)
error.response = response
throw error
}
},
parseJSON: function(response) {
return response.json()
},
};
The result of this in Google Chrome Dev Tools Network tab under Headers is this:
General:
Request URL:http://bookmarkapi.dev/api/user
Request Method:GET
Status Code:401 Unauthorized
Remote Address:127.0.0.1:80
Response Headers:
HTTP/1.1 401 Unauthorized
Date: Thu, 01 Dec 2016 00:41:43 GMT
Server: Apache/2.4.10 (Win32) OpenSSL/1.0.1i PHP/5.6.23
X-Powered-By: PHP/5.6.23
Cache-Control: no-cache
Content-Length: 28
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: application/json
Request Headers:
GET /api/user HTTP/1.1
Host: bookmarkapi.dev
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Referer: http://localhost/labs/webdevapp/tmp/tools/lab/manage_bookmark_list_tags.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
These 2 images below show the same exact data being sent to the URL using the app Postman and everything works great as the correct headers get sent so my server responds correctly.
This image shows my server returning the authenticated resources and the headers:
Access-Control-Allow-Headers →Authorization
Access-Control-Allow-Methods →GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin →*
These headers are not shown when making a request with JS Fetch() in Chrome or Firefox!
Also when I use a plugin to send custom headers in my browsers, it works correctly so the issue seems to be with the Fetch() function not send my custom headers.
Any ideas how I can get JS Fetch() to send my custom headers with my request?
From all the articles I have seen on using Fetch, it seems I have done it correctly however it does not send the headers in Google Chrome or Firefox so apparently it is not working! I have even tried adding a Fetch Pollyfil just to be on the safe side with no luck!

I finally resolved the issue. The browser request was actually the CORS preflight request in which my server was not returning the correct data so that is why fetch never sent my headers.
I now use this Laravel package https://github.com/barryvdh/laravel-cors to add cors support to a PHP laravel app and al is working great!

You are using mode: 'no-cors', this is prevent browser to sending OPTIONS request before GET. With OPTIONS request browser "notifies the server that when the actual request is sent, it will be sent with SOME custom headers. The server now has an opportunity to determine whether it wishes to accept a request under some circumstances.".
Mozilla HTTP OPTIONS method
if you do not implement that circumstances, your request will die at OPTIONS.

Related

Cross Domain REST POST Does Preflight Check But No Follow Up

I'm new to web programming so bear with me. I've created a simple REST API in python flask, and am hosting it with Apache 2.4. I've tested it via cURL and it works. Now I'm trying to access it via a web interface with jQuery.
The REST api is at http://api.localhost and the website that accesses it is at http://localhost.
The code I'm using to try and do a POST looks like this:
$.ajax({
type: 'POST',
url: 'http://api.localhost/auth',
data: '{"username":"user1", "password":"abcxyz"}',
success: function(data) { console.log(data); alert('data: ' + data); },
contentType: "application/json",
dataType: 'json'
});
However, it seems the success function doesn't run. Looking in dev console (f12) I can see that instead of a POST to that URL, and OPTIONS HTTP request is made. My understanding is this is a Cross-Origin Resource Sharing (CORS) preflight check, to make sure it's OK for localhost to access api.localhost.
I've added the following lines to my apache config for api.localhost:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
It seems to be working to because the OPTIONS request returns a 200 (and no other data). However, there is no follow up. My understanding is since the server is saying it's OK for anyone to POST to api.localhost, that it should go ahead and do the POST next, but it doesn't.
Here are the preflight check request headers:
Host: api.localhost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101
Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://localhost
Connection: keep-alive
Cache-Control: max-age=0
Here are the response headers for that same request (remember, status 200):
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: *
Allow: POST, OPTIONS
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 20
Content-Type: text/html; charset=utf-8
Date: Thu, 07 Dec 2017 00:17:08 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.29 (Debian)
Vary: Accept-Encoding
You can see that the server is saying any domain is fine (*) and that POST is OK. However, there is no follow up POST. What am I missing?
Thanks.
http://api.localhost/auth must also send Access-Control-Allow-Headers: content-type.
So to your Apache config, you also need to add this:
Header always set Access-Control-Allow-Headers "content-type"
That’s necessary because the contentType: "application/json" part of your frontend code adds a Content-Type: application/json header to the request, and any values for the Content-Type request header other than text/plain, application/x-www-form-urlencoded, or multipart/form-data will trigger browsers to send a CORS preflight OPTIONS request.
So if you have your http://api.localhost/auth server send back the Access-Control-Allow-Headers: content-type response header, that should cause the preflight to succeed, and so cause the browser to move on to making the POST request from your frontend code.

Creating a get request using fetch - only options is send

I'm trying to create a get request using Fetch. The request looks like this:
fetch('https://requestb.in/14ikb6j1', {
method: 'get',
headers: {
'Authorization': 'Token token=xxxxx'
}});
If I make this request to my own server, it fails on the options request every time. I cannot figure out why.
If I make this request to requestbin, there is an options request first. However, after receiving the options response, the actual get request is never made.
This is what requestbin received:
OPTIONS /14ikb6j1
HEADERS
Referer: http://localhost:9000/
Host: requestb.in
Total-Route-Time: 0
Via: 1.1 vegur
Accept-Encoding: gzip
Cf-Visitor: {"scheme":"https"}
Cf-Ipcountry: NL
Cf-Ray: 36edd35d1cad2b28-AMS
Cf-Connecting-Ip: 37.153.231.90
Connection: close
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Accept: */*
Connect-Time: 1
Access-Control-Request-Method: GET
Content-Length: 0
Accept-Language: nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4
X-Request-Id: 69b120c1-0f18-454b-a7fd-91f082252a06
Access-Control-Request-Headers: authorization
Origin: http://localhost:9000
This is what chrome logs:
The strangest thing is, I get a 200 - OK status as result. The console however, still logs this:
Fetch API cannot load https://requestb.in/14ikb6j1. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
:9000/#/branching:1 Uncaught (in promise) TypeError: Failed to fetch
But if I make this request using Postman it does work:
GET /14ikb6j1
HEADERS
Cf-Ipcountry: NL
Cf-Ray: 36eddb797ebf7223-AMS
Authorization: Token token=xxxx
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Total-Route-Time: 0
Via: 1.1 vegur
Connection: close
Cf-Connecting-Ip: 37.153.231.90
Connect-Time: 0
Accept-Language: nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4
Postman-Token: 57486701-6f9a-4aab-be1b-776f0d376920
Accept: */*
Host: requestb.in
X-Request-Id: 73a7a35e-0d89-41fd-b760-2aa952349871
Accept-Encoding: gzip
Cf-Visitor: {"scheme":"https"}
Cache-Control: no-cache
How can I make this get request with authentication headers work?
This is probably a CORS problem. The server you are seinding the request to doesn't allow requests from a different domain.
Check your browser console and look for errors. You might find somehting like this:
Fetch API cannot load https://requestb.in/14ikb6j1. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'https://localhost:9000' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.

Fetch api issue with DELETE - changes to OPTIONS even when cors is good

Using fetch api in the client browser, I am not having issues with GET or POST, but I am having issues with fetch and DELETE. Its seems to change the DELETE request method to OPTIONS.
Most of the research shows to be a cors issues, but with me I have the cors issues covered.
I am not sure if this is a valid fetch spec api link but it shows:
A CORS-safe listed method is a method that is GET, HEAD, or POST.
I am not sure if this means I can not use DELETE on fetch with cors and this is why I am having issues?
browser code:
var request =
new Request(url, {
credentials: 'include',
mode: 'cors',
method: 'DELETE'
});
return fetch(request)
.then(this.fetchError.bind(this))
.then(this.json)
.then((response)=> {
this.set(`uploadState.${index}.value`, false);
})
.catch((e) => {
console.log(e);
});
},
chrome network tab:
Request URL:http://72.12.4.3:9000/api/v1/listings/3/47/image-3-47-1492565415145.jpeg
Request Method:OPTIONS
Status Code:401 Unauthorized
Remote Address:72.12.4.3:9000
Response Headers
view source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:true
Access-Control-Allow-Methods:POST, GET, OPTIONS, DELETE
Access-Control-Allow-Origin:http://72.12.4.3:8000
Connection:keep-alive
Content-Length:25
Content-Type:application/json; charset=utf-8
Date:Wed, 19 Apr 2017 01:36:07 GMT
ETag:W/"19-9NCRiMyz+z1Bt6fGQfcxA"
X-Powered-By:Express
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:
Access-Control-Request-Method:DELETE
Cache-Control:no-cache
Connection:keep-alive
Host:72.12.4.3:9000
If-None-Match:W/"19-9NCRiMyz+z1Bt6fGQfcxA"
Origin:http://72.12.4.3:8000
Pragma:no-cache
Referer:http://72.12.4.3:8000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
The OPTIONS request is expected and basically is the mechanism that the browser uses to check if the request DELETE in this case is allowed.
The OPTIONS request seems to get a 401 Unauthorized response from your server. This would indeed cause the request to fail. Respond with 200 OK instead.
As an aside, the header Access-Control-Allow-Headers: is wrong, but this shouldn't affect anything.

$http POST call in angularjs returns HTTP status code 405

I try to make a POST call from a javascript client to a foursquare API called addvenue.
This the API endpoint documentation link.
But the server returns 405 - Method not allowed. Here is the snippet making the call
var postdata = {'oauth_token':$scope.access_token_foursquare,
'v':'20141217','name':'randomlisting',
'll':'44.3,37.2','m':'foursquare'};
var req = {
method: 'POST',
url: 'https://api.foursquare.com/v2/venues/add',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
data: postdata
}
$http(req).then(function(response){
console.log(response);
});
Following is the Request and response packet for the above call.
Remote Address:103.245.222.185:443
Request URL:https://api.foursquare.com/v2/venues/add
Request Method:OPTIONS
Status Code:405 Method Not Allowed
**Request Headers**
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, authorization, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:api.foursquare.com
Origin:http://localhost:9000
Referer:http://localhost:9000/foursquare
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
**Response Headers**
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Connection:Keep-Alive
Content-Length:90
Content-Type:application/json; charset=utf-8
Date:Wed, 17 Dec 2014 12:15:15 GMT
Keep-Alive:timeout=10, max=50
Server:nginx
Tracer-Time:1
Via:1.1 varnish
X-Cache:MISS
X-Cache-Hits:0
X-Served-By:cache-sn87-SIN
I also studied about CORS issue. In my case the server is allowing all origins, as seen in the response headers. I am struck with this issue and could not proceed further.
Any help would be appreciated.
Thanks in advance.
Request Method:OPTIONS
The client is making a pre-flight OPTIONS request to the server.
An OPTIONS request is automatically made by the browser before making a non-simple (e.g. not a GET) cross domain (CORS) request.
The purpose of the OPTIONS request is a quick check with the server to ensure that the client is permitted to make the POST before actually making the POST. Thus the client makes 1 or 2 requests.
An OPTIONS request and if the OPTIONS request responds with success (not a 405) then make the POST.
The OPTIONS request is failing most likely because you have not stated in your server response that your server supports OPTIONS requests.
Add this header to your server response ..
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Then it should all work.
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests for more info

Annoying issue with CORS and Header Authentication with Angular

This is my trouble:
With $http I'm trying to make a request. This is the response:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8383' is therefore not allowed access.
I just enable all that I need to make CORS request. This is on my server:
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with, bbtoken, CUSTOM_AUTH_TOKEN, Authorization");
httpResponse.setHeader("Access-Control-Expose-Headers", "CUSTOM_AUTH_TOKEN");
This is the code from Angular:
$http({
method: 'GET',
url: 'http://www.myurl\:8080/mypath/myservice',
headers: {
CUSTOM_AUTH_TOKEN: SessionService.currentUser.token
}
})
.success(function(data, status, headers, config) {
successCallback(data);
})
.error(function(data, status, headers, config) {
console.error(data);
});
But I found an infamous 401 Auth Error.
If I try with cURL or RestConsole (chrome), all is ok... I cannot understand the mistake!
Thanks!
UPDATE
This is the source request header
OPTIONS /mypath/blabla HTTP/1.1
Host: myhost
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Access-Control-Request-Method: GET
Origin: http://localhost:8383
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
Access-Control-Request-Headers: custom_auth_token
Accept: */*
Referer: http://localhost:8383/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: it
UPDATE WITH RESPONSE
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Restricted Service"
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 1079
Date: Sun, 08 Jun 2014 08:08:32 GMT
You are adding a custom HTTP request header to the request, this prevents the request from being simple and makes it preflighted.
Before the browser will make the GET request, it will make an OPTIONS request to ask if the extra header is acceptable.
You need to set up your server so it will respond with suitable CORS headers to the OPTIONS request as well as the GET request.
Solved: The problem came from by Spring Security. A nonsense configuration about security, needs authentication (by custom filter) on all request. OPTION included. By for HTTP standards, the headers inside the OPTION do not contain values, but only name (of the headers)
This is the solution for my trouble:
<intercept-url pattern="/**" access="permitAll" method="OPTIONS" requires-channel="any"/>

Categories

Resources