I am using backbone to get data from an API. This all works fine when there is no authorization required and when I add the authorization to the API, I get the expected 401 - unauthorised response.
[from the console log:
GET http://localhost:999/api/tasks 401 (Unauthorized)
]
I've then add in this code to add the bearer authorization to the header for every call:
var backboneSync = Backbone.sync;
Backbone.sync = function (method, model, options) {
/*
* The jQuery `ajax` method includes a 'headers' option
* which lets you set any headers you like
*/
var theUser = JSON.parse(localStorage.getItem("happuser"));
if (theUser !== null)
{
var new_options = _.extend({
beforeSend: function(xhr) {
var token = 'Bearer' + theUser.authtoken;
console.log('token', token);
if (token) xhr.setRequestHeader('Authorization', token);
}
}, options)
}
/*
* Call the stored original Backbone.sync method with
* extra headers argument added
*/
backboneSync(method, model, new_options);
};
Once I include this code, the API sends the request with a method of OPTIONS instead of GET and I obviously get a 405 invalid method response.
Here is the console log output
OPTIONS http://localhost:999/api/tasks 405 (Method Not Allowed) jquery-1.7.2.min.js:4
OPTIONS http://localhost:999/api/tasks Invalid HTTP status code 405
Any idea why the send method would be changing?
ADDITIONAL DISCOVERY:
It appears when I do a model.save it does the same thing., even if I don't actually change the model.
FURTHER DETAILS: This is the Request/Response for the call without authorisation...
Request URL:http://localhost:999/api/tasks
Request Method:GET
Status Code:200 OK
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Host:localhost:999
Origin:http://localhost
Proxy-Connection:keep-alive
Referer:http://localhost/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://localhost
Cache-Control:no-cache
Content-Length:3265
Content-Type:application/json; charset=utf-8
Date:Wed, 13 Nov 2013 14:51:32 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
As soon as I add the sync override code in the response changes to this:
Request URL:http://localhost:999/api/tasks
Request Method:OPTIONS
Status Code:405 Method Not Allowed
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Access-Control-Request-Headers:accept, authorization
Access-Control-Request-Method:GET
Host:localhost:999
Origin:http://localhost
Proxy-Connection:keep-alive
Referer:http://localhost/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://localhost
Allow:GET,POST
Cache-Control:no-cache
Content-Length:76
Content-Type:application/json; charset=utf-8
Date:Wed, 13 Nov 2013 14:56:52 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
It would appear you are issuing a "not so simple request ™":
you're making a CORS request
and you're setting a custom header
In that case, your browser divides your request in two : a preflight request (the OPTIONS verb you see) and the actual request once the permissions have been retrieved.
To quote the article linked:
The preflight request is made as an HTTP OPTIONS request (so be sure
your server is able to respond to this method). It also contains a few
additional headers:
Access-Control-Request-Method - The HTTP method of the actual request.
This request header is always included, even if the HTTP method is a
simple HTTP method as defined earlier (GET, POST, HEAD).
Access-Control-Request-Headers - A comma-delimited list of non-simple
headers that are included in the request.
The preflight request is a way of asking permissions for the actual
request, before making the actual request. The server should inspect
the two headers above to verify that both the HTTP method and the
requested headers are valid and accepted.
Related
I can't figure out how this AJAX request knows which cookie to use. It uses the right one, but how does it know which one is the right one, or where is it specified (implicitly)?
$.ajax({
url: 'https://remote-host.de/api/v2/session',
crossDomain: true,
xhrFields: {
withCredentials: true
},
statusCode: {
401: function() {
// do stuff
},
200: function() {
// do stuff
}
}
});
The request looks like this when I inspect it with Chrome and it has the right cookie set (the one of the remote host I am trying to request the login state from):
Accept:*\/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Cookie:_foo-bar_session=WjRMdExSQ1F6UlczbER0Ui9sQU9NNllIRWo1NmpCSXo2REh6akZmM1czODZ0M29adGh4aWg3ZmdrYWdxSU5KRVptUi8ybDladmJMHJhZWKZ3A5NlJCOTdWeFpCRGJQdHVvMnlxb0VQeWlCMGRtNDkxNDF3QVdhcnVRenlsQXExa3RNEtwZ1RNMW9oaE5TV1hLbHdnPT0tLXhtYUo3YytHY2wxWTFxanlXVTJjdlE9PQ%3D%3D--b22797a9b004d0759a43f4d94686edf909610a06
Host:remote-host.de
Origin:http://localhost:3001
Referer:http://localhost:3001/de
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
The response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Methods:GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin:http://localhost:3001
Access-Control-Expose-Headers:ETag
Access-Control-Max-Age:86400
Cache-Control:no-cache
Connection:keep-alive
Content-Type:application/json; charset=utf-8
Date:Wed, 22 Mar 2017 10:18:15 GMT
Server:nginx
Set-Cookie:_foo-bar_session=SGhyWGtWTFVoc1AzUWlldmIxTjFGVXVCQU9YVkduSDFISWtLamwwT01heW5HN25saVNyYWJ1b2ZDZDI4RzNGT1BzYWZOOHNVK21DN0kxNmJRS1VDSTdwb1VVS2NtcTZ3Y1dRYUJSaTYxckpOdDZFZ2RpRlQzTHZPdDdTTjljenZzQ1hTUjlCN0RoZUlkcWlpNm5KK2VRPT0tLTkwUlNuM0Z6TDZ2TWJjZVVSUExpb0E9PQ%3D%3D--568e4688b6ff5e17faa32a3bab1a7cf01807a581; path=/; HttpOnly
Transfer-Encoding:chunked
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:b43ce1c4-2c80-4sd5-8333-0g93ae4df940f
X-Runtime:0.013627
X-XSS-Protection:1; mode=block
But how does it know?
Unless I’m misunderstanding the question, the browser just sends back whatever cookies it has that match the domain the request is being made to, and that match any path in the cookie.
How cookies work? has more details, with links to articles explaining how browsers handle cookies.
I'm developing an Azure MobileService / CordovaApp setup. The server runs locally and has the following setting to enable CORS:
<add name="Access-Control-Allow-Origin" value="*" />
The api can be called via browser using addresses like
http://localhost:59477/api/item/explore/A/B
The client script that's trying to depict this call is the following:
var client = new WindowsAzure.MobileServiceClient('http://localhost:59477/', 'http://localhost:59477/', '');
GetItems.addEventListener("click",
function ()
{
client.invokeApi("item/explore/A/B",
{
method: "get"
})
.done(
function (results)
{
alert(results.result.count);
},
function (error)
{
var xhr = error.request;
alert('Error - status code: ' + xhr.status + '; body: ' + xhr.responseText);
alert(error.message);
}
);
}
);
What I get is status 405 - Method not allowed, which I don't understand for two reasons:
The invoked Service Action is decorated with the [HttpGet] Attribute
The response headers seem to say GET is fine:
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Length: 76
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RjpcQ29kZVxGb290UHJpbnRzXGZvb3RwcmludHMuU2VydmVyXEZvb3RwcmludHNTZXJ2aWNlXGFwaVxmb290cHJpbnRcZXhwbG9yZVw1MS4yNzcwMjJcNy4wNDA3ODM=?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Date: Sat, 15 Aug 2015 18:08:30 GMT
Any idea what I can do to get this running?
Edit
The Raw request shows that, as already expected by Jeremy Foster, the client sends a request of the type OPTIONS:
OPTIONS http://localhost:59477/api/fp/get?id=1 HTTP/1.1
Host: localhost:59477
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:4400
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36
Access-Control-Request-Headers: accept, x-zumo-features, x-zumo-installation-id, x-zumo-version
Accept: */*
Referer: http://localhost:4400/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
I managed to support this request applying the [HttpOptions] attribute to my action method. Now I get a more or less valid response:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RjpcQ29kZVxGb290UHJpbnRzXGZvb3RwcmludHMuU2VydmVyXEZvb3RwcmludHNTZXJ2aWNlXGFwaVxmcFxnZXRcMQ==?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Date: Sun, 23 Aug 2015 19:34:21 GMT
Content-Length: 234
...
At least that's what fiddler tells me. The AzureMobileServiceClient throws yet another issue, which roughly translates as:
Cross-Origin request blocked, missing token 'x-zumo-installation-id' in CORS header row 'Access-Control-Allow-Headers' on CORS-Preflight-Channel
Turn on Fiddler and try your api call from the browser and then again using the Mobile Services client and compare your raw HTTP requests to see what's different.
Try to run through the CORS Support section of this blog post: http://azure.microsoft.com/blog/2014/07/28/azure-mobile-services-net-updates/.
A side note: The x-zumo-installation-id error you were getting was because the client requested access for a list of headers (see the Access-Control-Request-Headers in your request) and the server did not return that same list in the Access-Control-Allow-Headers header. Using the MS_CrossDomainOrigins setting as mentioned in the above blog post takes care of this by setting the allowed headers to * (all) by default.
I have a script running on a web page that needs to use the JQuery $.ajax method (currently using jquery 1.7.2) to submit several GET requests to a service endpoint on a different domain. I have the ajax call working in IE (9, 10, 11), but it fails with a 401 Unauthorized response in Firefox and Chrome. Part of the additional error message in Chrome is "Full authentication is required to access this resource".
My ajax call is setup like this (dataType is "json" for these requests that fail, and async is true):
$.ajax({
url: url,
type: "GET",
async: isAsync,
dataType: dataType,
username: user,
password: pswd,
success: function (response, status) {
// success code here
},
failure: function (response, status) {
// failure code here
},
complete: function (xhr, status) {
// on complete code here
}
});
I am passing in the username and password required to access the service and this works in IE. I was understanding that the JQuery ajax function would handle the authentication correctly, so if a response comes back indicating that authorization is required, it would use the credentials that were provided to make that request correctly. Am I missing something here? Do I need to manually add the Authorization header for this to work?
UPDATE:
Here is the request, response, and cookie info reported by Chrome and IE via the F12 debugging tools (some info replaced with [...removed...])
Chrome (42.0.2311.90 m)
Response Headers
access-control-allow-credentials:true
access-control-allow-origin:[...removed...]
access-control-expose-headers:
cache-control:private,max-age=0,must-revalidate connection:keep-alive
content-encoding:gzip content-length:296
content-type:text/html;charset=ISO-8859-1 date:Tue, 21 Apr 2015
20:55:12 GMT expires:Tue, 21 Apr 2015 20:55:12 GMT p3p:CP="NON DSP COR
CURa PSAa PSDa OUR NOR BUS PUR COM NAV STA"
set-cookie:JSESSIONID=qd-app-1348vf1vrksvc76oshcwirvjp.qd-app-13;Path=/;Secure;HttpOnly
set-cookie:NSC_vt1.sbmmzefw.dpn!-!IUUQT=ffffffff09091c3945525d5f4f58455e445a4a42378b;path=/;secure;httponly
status:401 Unauthorized vary:Accept-Encoding version:HTTP/1.1
www-authenticate:Basic realm="Rally ALM"
Request Headers
:host:rally1.rallydev.com :method:GET :path:[...removed...]
:scheme:https :version:HTTP/1.1 accept:application/json,
text/javascript, /; q=0.01 accept-encoding:gzip, deflate, sdch
accept-language:en-US,en;q=0.8 origin:[...removed...]
referer:[...removed...] user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90
Safari/537.36
Response Cookies
JSESSIONID qd-app-1348vf1vrksvc76oshcwirvjp.qd-app-13
NSC_vt1.sbmmzefw.dpn!-!IUUQT
ffffffff09091c3945525d5f4f58455e445a4a42378b
IE 11
Request Headers
Request GET [...removed...] Referer [...removed...] Accept
application/json, text/javascript, /; q=0.01 Accept-Language en-US
Accept-Encoding gzip, deflate User-Agent Mozilla/5.0 (Windows NT
6.1; WOW64; Trident/7.0; rv:11.0) like Gecko Host [...removed...] Connection Keep-Alive Cache-Control no-cache Cookie
JSESSIONID=qd-app-08xmftgye78tde1b0wzcl2kit4m.qd-app-08;
NSC_vt1.sbmmzefw.dpn!-!IUUQT=ffffffff09091c3145525d5f4f58455e445a4a42378b;
RALLY-Detail-treeCollapsed=false;
ZSESSIONID=RpKo5acfRqmjPhW0vIU1rgurWmDhlka0lrGCY9MIWhU;
SUBBUCKETID=713
Response Headers
Response HTTP/1.1 200 OK RallyRequestID
qd-app-08xmftgye78tde1b0wzcl2kit4m.qd-app-0810353108 Expires Thu, 01
Jan 1970 00:00:00 GMT Content-Type text/javascript; charset=utf-8
ETag "0101c2c8d3463ee3c1a4f950d4142b7d3" P3P CP="NON DSP COR CURa
PSAa PSDa OUR NOR BUS PUR COM NAV STA" Cache-Control
private,max-age=0,must-revalidate Date Tue, 21 Apr 2015 20:58:17 GMT
Connection keep-alive Set-Cookie
ZSESSIONID=RpKo5acfRqmjPhW0vIU1rgurWmDhlka0lrGCY9MIWhU;Path=/;Domain=[...removed...];Secure;HttpOnly
Set-Cookie
SUBBUCKETID=713;Path=/;Domain=[...removed...];Secure;HttpOnly
Content-Length 319
Cookies
Sent JSESSIONID qd-app-08xmftgye78tde1b0wzcl2kit4m.qd-app-08
Sent NSC_vt1.sbmmzefw.dpn!-!IUUQT
ffffffff09091c3145525d5f4f58455e445a4a42378b Sent
RALLY-Detail-treeCollapsed false Sent ZSESSIONID
RpKo5acfRqmjPhW0vIU1rgurWmDhlka0lrGCY9MIWhU Sent
SUBBUCKETID 713 Received ZSESSIONID
RpKo5acfRqmjPhW0vIU1rgurWmDhlka0lrGCY9MIWhU At end of session
[...removed...] / Yes Yes Received SUBBUCKETID 713 At end of
session [...removed...] / Yes Yes
I came across a jquery forum post that had some additional information regarding this issue. Based on what I found there, I added this to the $.ajax call:
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', makeBaseAuth(user, pswd));
}
where makeBaseAuth() uses the btoa() function like this:
makeBaseAuth: function(user, pswd){
var token = user + ':' + pswd;
var hash = "";
if (btoa) {
hash = btoa(token);
}
return "Basic " + hash;
}
That appears to be working in Chrome now, I'm not getting a login prompt or a 401 response, the request is going through and I get the expected response. I also removed the option xhrFields: { withCredentials: true } as that didn't appear to be necessary. For some reason this isn't working in Firefox yet, and in the Firefox debugger I can't actually get at the javascript to do any decent debugging to see what the problem is, the way this script works is its loaded into a web page as an anonymous script and I don't have any control over that. I have a way to get at the script in IE and Chrome, but not Firefox for some reason. I'll consider this a win just getting it to work in Chrome, thanks to everyone for prodding me in the right direction!
Ember-data doesn't support nested API urls in models. For that we need to write our own custom adapter. I have added the nested_url_adapter .
The issues that I am having right now::
When doing a POST request to the api "http://api.server/resource/resourceId/childResource",
the request payload on the request headers is not present.
2̶.̶ ̶I̶ ̶a̶m̶ ̶u̶s̶i̶n̶g̶ ̶e̶m̶b̶e̶r̶-̶s̶i̶m̶p̶l̶e̶-̶a̶u̶t̶h̶ ̶l̶i̶b̶ ̶f̶o̶r̶ ̶t̶h̶e̶ ̶a̶u̶t̶h̶e̶n̶t̶i̶c̶a̶t̶i̶o̶n̶ ̶a̶n̶d̶ ̶a̶u̶t̶h̶o̶r̶i̶z̶a̶t̶i̶o̶n̶,̶ ̶w̶h̶i̶l̶e̶ ̶d̶o̶i̶n̶g̶ ̶t̶h̶e̶ ̶P̶O̶S̶T̶ ̶r̶e̶q̶u̶e̶s̶t̶,̶ ̶i̶n̶ ̶t̶h̶e̶ ̶r̶e̶q̶u̶e̶s̶t̶ ̶h̶e̶a̶d̶e̶r̶ ̶t̶h̶e̶r̶e̶ ̶i̶s̶ ̶n̶o̶ ̶A̶u̶t̶h̶o̶r̶i̶z̶a̶t̶i̶o̶n̶ ̶h̶e̶a̶d̶e̶r̶.̶
I have implemented the same nested_url adapter with little modification to the type.typeKey as per my api server.
Here are the gist of the files that I have in my application:
Gist
XHR request tabs::
Remote Address: 127.0.1.1: 80
Request URL: http: //api.server/events/9/tickets
Request Method: OPTIONS
Status Code: 200 OK
Request Headersview parsed
OPTIONS / events / 9 / tickets HTTP / 1.1
Host: api.server
Connection: keep - alive
Access - Control - Request - Method: POST
Origin: http: //0.0.0.0:4300
User - Agent: Mozilla / 5.0(X11; Linux x86_64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 36.0.1941.0 Safari / 537.36
Access - Control - Request - Headers: accept,
client_id
Accept: *
/*
Referer: http://0.0.0.0:4300/event/9/manage/tickets
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Response Headersview parsed
HTTP/1.1 200 OK
Date: Thu, 24 Apr 2014 11:51:07 GMT
Server: Apache/2.4.9 (Ubuntu)
X-Powered-By: PHP/5.5.11-2+deb.sury.org~precise+2
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, client_id, client_secret
Access-Control-Max-Age: 0
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html*/
POST request ::
Remote Address: 127.0.1.1: 80
Request URL: http: //api.server/events/9/tickets
Request Method: POST
Status Code: 201 Created
Request Headersview source
Accept: application / json,
text / javascript,
*
/*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
client_id:[object Object]
Connection:keep-alive
Content-Length:0
Host:api.server
Origin:http://0.0.0.0:4300
Referer:http://0.0.0.0:4300/event/9/manage/tickets
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36
Response Headersview source
Access-Control-Allow-Origin:*
Access-Control-Expose-Headers:
Connection:Keep-Alive
Content-Length:242
Content-Type:application/hal+json
Date:Thu, 24 Apr 2014 11:51:07 GMT
Keep-Alive:timeout=5, max=99
Location:http://api.server/events/9/tickets/24
Server:Apache/2.4.9 (Ubuntu)
X-Powered-By:PHP/5.5.11-2+deb.sury.org~precise+2*/
As per marcoow's comment the 2nd issue has been solved. Still the 1st issue is there.
When you're using Ember.SimpleAuth with cross-origin requests you have to explicitly whitelist these origins to have the Authorization header added by Ember.SimpleAuth (that's a security feature to make sure your token doesn't get published all across the internet) - see here: http://ember-simple-auth.simplabs.com/ember-simple-auth-api-docs.html#Ember-SimpleAuth-setup
I have a Rails service returning data for my AngularJS frontend application. The service is configured to allow CORS requests by returning the adequate headers.
When I make a GET request to receive data, the CORS headers are sent, as well as the session cookie that I have previously received on login, you can see for yourself:
Request URL:http://10.211.194.121:3000/valoradores
Request Method:GET
Status Code:200 OK
Request Headers
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:_gestisol_session=BAh7B0kiDHVzZXJfaWQGOgZFRmkASSIPc2Vzc2lvbl9pZAY7AEZJIiVmYTg3YTIxMjcxZWMxNjZiMjBmYWZiODM1ODQzMjZkYQY7AFQ%3D--df348feea08d39cbc9c817e49770e17e8f10b375
Host:10.211.194.121:3000
Origin:http://10.211.194.121:8999
Pragma:no-cache
Referer:http://10.211.194.121:8999/
User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
X-Requested-With:XMLHttpRequest
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:X-Requested-With,X-Prototype-Version,Content-Type,Cache-Control,Pragma,Origin
Access-Control-Allow-Methods:GET,POST,OPTIONS
Access-Control-Allow-Origin:http://10.211.194.121:8999
Access-Control-Max-Age:1728000
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:5389
Content-Type:application/json; charset=utf-8
Date:Mon, 04 Nov 2013 14:30:51 GMT
Etag:"2470d69bf6db243fbb337a5fb3543bb8"
Server:WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30)
X-Request-Id:15027b3d323ad0adef7e06103e5aa3a7
X-Runtime:0.017379
X-Ua-Compatible:IE=Edge
Everything is right and I get my data back.
But when I make a POST request, neither the CORS headers nor the session cookie are sent along the request, and the POST is cancelled at the server as it has no session identifier. These are the headers of the request:
Request URL:http://10.211.194.121:3000/valoraciones
Request Headers
Accept:application/json, text/plain, */*
Cache-Control:no-cache
Content-Type:application/json;charset=UTF-8
Origin:http://10.211.194.121:8999
Pragma:no-cache
Referer:http://10.211.194.121:8999/
User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
X-Requested-With:XMLHttpRequest
Request Payload
{valoracione:{revisiones_id:1, valoradores_id:1}}
valoracione: {revisiones_id:1, valoradores_id:1}
And the service answers with a 403 because the request does not contain the session cookie.
I don't know why the POST request fails, as the $resource is configured just like the other one and I have defined the default for $httpProvider to send the credentials (and it works right as the GET request succeeds):
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}])
This is the failing resource when I call $save() on an instance:
'use strict';
angular.module('gestisolApp')
.service('ValoracionesService', ['$resource', 'API_BASE_URL', function ValoracionesService($resource, API_BASE_URL) {
this.valoraciones = $resource(API_BASE_URL + '/valoraciones');
}]);
And this is the service that succeeds with the query() call:
'use strict';
angular.module('gestisolApp')
.service('ValoradoresService', ['$resource', 'API_BASE_URL', function ValoradoresService($resource, API_BASE_URL) {
this.valoradores = $resource(API_BASE_URL + '/valoradores');
}]);
They are much like the same.
Does anybody know why the POST is sent without the session cookie?
Edit
Just to complete the information, preflight is handled by the following method, and is handled OK as the request before the failing POST is an OPTIONS that succeeds with a 200 response code:
def cors_preflight_check
headers['Access-Control-Allow-Origin'] = 'http://10.211.194.121:8999'
headers['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With,X-Prototype-Version,Content-Type,Cache-Control,Pragma,Origin'
headers['Access-Control-Allow-Credentials'] = 'true'
headers['Access-Control-Max-Age'] = '1728000'
render :nothing => true, :status => 200, :content_type => 'text/html'
end
This is the CORS OPTIONS request/response exchange previous to the failing POST:
Request URL:http://10.211.194.121:3000/valoraciones
Request Method:OPTIONS
Status Code:200 OK
Request Headers
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, x-requested-with, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:10.211.194.121:3000
Origin:http://10.211.194.121:8999
Referer:http://10.211.194.121:8999/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:X-Requested-With,X-Prototype-Version,Content-Type,Cache-Control,Pragma,Origin
Access-Control-Allow-Methods:GET,POST,OPTIONS
Access-Control-Allow-Origin:http://10.211.194.121:8999
Access-Control-Max-Age:1728000
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:1
Content-Type:text/html; charset=utf-8
Date:Mon, 04 Nov 2013 15:57:38 GMT
Etag:"7215ee9c7d9dc229d2921a40e899ec5f"
Server:WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30)
X-Request-Id:6aa5bb4359d54ab5bfd169e530720fa9
X-Runtime:0.003851
X-Ua-Compatible:IE=Edge
Edit 2: I have changed the title to reflect clearly my problem
I had a similar problem and adding the following before angular $http CORS request solved the problem.
$http.defaults.withCredentials = true;
Refer https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS#Requests_with_credentials for more details.
When CORS is involved, then your browser will send an OPTIONS request before the POST request.
I don't know the specifics with Rails, but I guess you have to configure Rails to actually answer the OPTIONS request with the adequate CORS headers.
The following code is just for comparison - it shows how you would address the issue in Java:
public void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Access-Control-Allow-Origin", "http://10.211.194.121:8999");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST, GET");
resp.setHeader("Access-Control-Allow-Headers", "X-Requested-With,X-Prototype-Version,Content-Type,Cache-Control,Pragma,Origin");
resp.setHeader("Access-Control-Max-Age", "600");
resp.setHeader("Access-Control-Expose-Headers","Access-Control-Allow-Origin");
super.doOptions(req, resp);
}
But it might get you on the right track how to configure it in Rails.
Ok, finally I figured out what was happening.
By the answer posted on this question, I removed the HttpOnly parameter from the cookie and got it working on Firefox. Later for Chrome was just a matter of applying the rest of recommendations from the answer to make it work, like setting a domain for the cookie.