I am building a web application using ASP.NET Web API and SignalR. I have a pretty good understanding of HTTP but this problem is beyond me.
Currently, I am setting a cookie on all AJAX requests to this API. Subsequent AJAX requests to the API send the cookie without issue.
However, I would like this cookie to also be used in the SignalR requests that establish a connection, but according to Chrome, the cookie is not being sent in these requests. I have the cookie set as HTTPOnly. Currently, everything is running locally; all requests go to localhost:port. The domain on the cookie is being set to localhost as well.
My SignalR connections look like this:
var connection = $.connection("/updates");
/* set handlers here */
connection.start(function () {
console.log("connection started!");
});
It seems as if Chrome thinks this is a CORS request and is withholding the cookies. The requests are on the same domain however, so this does not make much sense.
Turns out I don't understand cookies well enough. Browsers seem to have trouble handling TLDs like localhost as the cookie domain. Therefore, I left the domain undefined, so that the cookie will default to the domain of the request.
However, the path parameter needed to be set to / in order to make sure the cookie is sent in all requests.
Once I made these changes, everything worked as expected, and my cookies were plainly visible in SignalR.
Related
In a javascript single page application, on first request of the user to the front-end, a cookie is set with Node JS Express.
Credentials are included to requests by adding the "credentials: include" option to Fetch API.
The first render is server-side with React server side rendering.
I can see the cookie in developer tools. It is sent on every request to the front-end, but not to the backend.
Front-end and backend are both node servers. In development they are on differents ports of localhost, (also tried setting a domain in hosts file but no difference). In staging the api server is on a subdomain of the front-server domain. Neither works.
Can they share the same cookie or should I create one cookie for each? I can't seem to set the cookie for the requests to the backend, either because different port in dev or different subdomain in staging.
Ok so I think I figured it out.
Cookies were sent on some requests but not on others. Checking the request method, turns out cookies are not sent when method type is "OPTIONS", which is preflight, and apparently this is normal.
The workaround is to avoid checking cookies if request method is OPTIONS and just send a 200 empty response in this case, so that the real request can proceed, during which cookies will be sent.
I am adding cookies on the client side using JavaScript but they never get sent to the server.
The cookies are found in chrome developer tools but the HTTP column is not ticked (I am not sure what that means).
I am running Tomcat Server on local host with HTTP only (no HTTPS) and trying to read these cookies from a servlet, but they never make it to the server, nor when I refresh the page neither when I send an ajax request.
I understand that the path needs to be set to / and I do that.
In Google chrome cookies persist as expected and I can find them when I close the tab and reopen it.
I tried incognito mode as well as normal mode but the results are the same when it comes to the server side.
What I am doing wrong???
A website, let's call it customerwebsite.com, loads a script from our domain. Let's call our domain trackingwebsite.com. This script calls a web service on the server (written in PHP) that logs some things and then sets a cookie to identify the user later.
The Set-Cookie header gets returned correctly. So far so good. However, on the next request that the script makes to the web service, the cookie is not sent back.
I'm setting the cookie with an expire time of 10 years, with the domain set to .customerwebsite.com (I've also tried this with .trackingwebsite.com to be sure)
After some googling and reading through various vague answers I think I've distilled that this set-up is not possible.
Am I correct in assuming that the web service cannot set cookies, and that the script calling the web service has to take care of getting and setting cookies and sending this to the web service if required?
I've observed that my express server sends a Set-Cookie header in response to every OPTIONS request made by my front-end Angular.js project.
The cookie has a new value on each of the OPTIONS requests, but is never actually stored by
Chrome.
When I use passport.js to authenticate a user, the response to the POST request has the same header (again with a new cookie), but this time I can see that it is stored, and sent with subsequent requests in the Cookie header.
Which express module is doing this, and why? And why does Chrome not store the cookie?
This is more curiosity than anything, as it's not causing any problems (just caused a lot of confusion when trying to track one down).
The method OPTIONS are not supposed to have a side-effect. See this HTTP 1.1 documentation
OPTIONS is a request for information to the server. Such request is not considered as real interaction between a user and server. The server likely makes the information available to all users.
The browser respects this and chooses to ignore the cookies, conforming to the specification. That said it is security risk passing cookie data to user openly. Even if it is not valid, it can reveal server-side internals, which can allow hackers to exploit it.
pretty sure this is a bug with the current session module. if you're using the new cookies session, then you won't hit this problem. feel free to file a bug: https://github.com/expressjs/session
I have a page loading up in MobileSafari which communicated with another server via CORS.
In desktop browsers (tested Chrome and Safari), I am able to log in, get a session cookie, and have that session cookie be sent back for subsequent requests so that I may be authenticated with all API calls.
However, when I login via Mobile Safari, the cookie does not get sent back on subsequent requests.
I'm using Charles Proxy to spy on what's going on, and it tells me:
POST https://myremoteserver.com/sessions.json passes up my login info
It succeeds and response is received with a valid Set-Cookie header.
GET https://myremoteserver.com/checkout.json is requested, without a Cookie request header.
Server responds as if I am not logged in.
I'm using this snippet with Zepto.js to ensure that the withCredentials: true is properly setup on the XHR object. (pardon the coffeescript)
# Add withCredentials:true to the xhr object to send the remote server our cookies.
xhrFactory = $.ajaxSettings.xhr
$.ajaxSettings.xhr = ->
xhr = xhrFactory.apply(this, arguments)
xhr.withCredentials = yes
xhr
And that snippet works great in desktop browsers, and before I added it I was not able to preserve the session cookies in those desktop browsers.
Is there some quirk in MobileSafari that prevents this from working like desktop browsers? Why does it not work in the same way?
Edit!
here is my CORS headers setup in my rails 2.3 app, fairly standard stuff I believe
def add_cors_headers
if valid_cors_domain
headers['Access-Control-Allow-Origin'] = request.headers['HTTP_ORIGIN']
headers['Access-Control-Expose-Headers'] = 'ETag'
headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD'
headers['Access-Control-Allow-Headers'] = '*,x-requested-with,Content-Type,If-Modified-Since,If-None-Match'
headers['Access-Control-Allow-Credentials'] = 'true'
headers['Access-Control-Max-Age'] = '86400'
end
end
Also today desktop Safari on Mountain Lion started not to send the cookie, behaving just like MobileSafari. I'm not entirely sure if my assessment yesterday was inaccurate, or perhaps Apple is just trolling me...
Also could this be affected by using https:// at the remote url?
I don't know if this solution will work or is acceptable to you but I had the same problem with mobile Safari and a JSONP app. It seemed that Safari was not set to accept third party cookies. I went to Settings > Safari > Accept Cookies and set 'Always' and the problem evaporated. Good luck.
Can I set cookies in a response from a jsonp request?
I believe you are experiencing what I have been seeing in my app. My issue, was caused because iOS Safari, comes with a default option "Prevent Cross-Site Tracking" enabled by default that is causing the browser to block ALL third party cookies, even cookies that are issued by your back-end server from a different domain and CORS is configured correctly.
The only solution to this problem I found was to use a proxy in production like I did in dev. I accomplished this in Azure with Azure Functions and making all request go through a proxy. At that point iOS Safari did not block my cookies everything was set as expected.
I wrote about it in my blog https://medium.com/#omikolaj1/complete-guide-to-deploying-angular-and-asp-net-33a0976d0ec1
You didn't mention whether the remote server is under a different domain or just a different subdomain. I assume is under a different domain.
As #schellsan pointed out you can't set/write cookies to a different domain even if the CORS policy allows it due the 3rd party cookies restriction on safari. It's the latest safari restriction. I guess Firefox is about to do the same.
Workarounds I'm currently evaluating:
Use a redirect on the remote server so that when the client is redirected (the remote URL is in the browser bar) you can set the cookie
Use a custom header
I was running into the same problem.
My setup was:
AngularJS (Ionic) App on Server A with domain a.com
NodeJS with Passport JS as Backend on Server B with domain b.com
The login with the cookie went well on every browser, except Mobile Safari on iOS. Also the change of the mobile cookie (Do not track) settings in iOS did not had any impact on the issue.
Solution was to set a CNAME DNS Record
backend.a.com CNAME b.com
Open an address that sets the cookie via an iFrame - this will set the cookie.