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.
Related
I am doing web app using Angular and Node.js (Express) and I have a problem with cookies - they are set into my backend domain instead of my frontend.
When I make a POST request to /auth endpoint, server will return HttpOnly cookies - one is JWT and the second is refresh token. When I inspect network tab in chrome, I can see that server sent these cookies back, but when I inspect Application > Storage > Cookies, nothing is here.
I find out, that cookies are set on my backend domain. (app-backend.com instead of app.com) They are just simply associated with my backend domain.
Wierd thing is, that my app is working just fine on my computer, but when I switch to my phone, cookies are not sent from there (I am using iPhone with Safari or Chrome). Also, when I ran my app on localhost dev server, everything worked aswell.
I tried to set domain in cookie config to my frontend domain, it is not working at all.
Also, Chrome warns me with this message, I don't know if it has anything to do with my problem
A cookie associated with a cross-site resource at "my-domain" was set without the SameSite attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
Here is my code on github:
Frontend: https://github.com/TenPetr/dashboard
Backend: https://github.com/TenPetr/dashboard_backend
Thanks for your advices.
You are setting the SameSite=None; Secure attributes in your production.json which is correct. However, depending on your version of iOS / Safari you may be hitting an incompatibility issue where the cookies are incorrectly treated as SameSite=Strict.
In your development set-up you are both: not setting SameSite=None; Secure, and might be using URLs that count as the same site anyway, e.g. serving on localhost can lead to some weird cookie behaviour.
I would try testing your production configuration without the SameSite=None attribute. If this then starts to work on Safari, then you are hitting that bug. You can mitigate this by either setting two versions of the cookie, or adding useragent sniffing. There are more details on https://web.dev/samesite-cookie-recipes
Alternatively, you may be hitting Safari cookie policy issues if you are attempting to set cookies from the back-end server when it's not a site the user actually visits.
I am storing cookies for my web app using the 'Set-Cookie' header response from my python backend.
Here is my ajax call on the client-end to the function:
In developer tools in Chrome and Safari, when I look for the cookies, the cookies don't show up.
On Chrome, the Set-Cookie doesn't even show up in the response header to the network call.
In Safari, the Set-Cookie response header shows up and shows under request/response cookies,
but when I check cookies for the application, nothing shows up.
Furthermore, the cookie data shown in Safari is incorrect: it shows an incorrect expiration date and httpOnly/secure which should both be true.
The cookies seem to not exist, but when I log the server, I see clearly that the cookies exist and they appear
(also safari shows them going back and forth in the request/response headers)which means that the cookies are being properly stored and sent back to the server after every call in the header. I tried earlier to set httpOnly to false and secure to false, but even then the cookies exhibited the same behavior.
These cookies are still under the radar of both developer tools. How can I see the cookies on the browser in developer tools correctly? And what could this problem be?
Have you tried opening a tab to the server https://*.amazonaws.com and checking there instead?
The cookie will be set on the server's domain, but you won't see it in your local server's cookie storage. The reason is that all web storages are bound by same origin policy and your document can only access storages from its own domain, and the server can only set cookies for it's domain.
The rationale here is that if I sent you a link to a rogue document, it can't exfiltrate your SO cookies even if they were accessible from JS, neither sending a request to a rogue server can overwrite cookies on SO.
Try to disable chrome://flags/#network-service and it should work properly.
Lets say I have an application on foo.com domain.
I am doing user authorization to api.boo.com using the Fetch method with credentials flag as include and CORS set as well.
After successful authorization I am getting a session cookie. I need that cookie to have an access to another API's methods on api.boo.com domain.
This flow works fine on almost every browser. The problem is on iPhone's Safari. Safari has default option (Settings -> Safari -> Block Cookies) setted as "Allow from Websistes I Visit" with this configuration Safari doesn't send the session cookie when I am calling another API's methods so it looks like I am unauthorized.
The simplest solution is to change the cookie policy to "Always Allow". After that a session cookie is being sent. However, I can't ask every user of my application to do that.
Do you have any ideas how to make this flow working properly on iPhone's Safari?
Our intranet website has to communicate with a client .NET app. We're using a HttpListener (on http://localhost:[port]) on the client app and an iframe that refers to this url in the page. Its working like a charm when the page is HTTP.
Problem:
When the site is HTTPS a 'Mixed content' Javascript error is displayed in newer browsers and the request doesnt arrive at the client.
I believe this error would also occur when using an Ajax request instead of an iframe.
I also tried to bind a self-signed certificate to the listener and listening on https://localhost:[port] (which works for IE), but since the Firefox has its own certificate store its really tough to install it there automatically (IE uses Windows certificate store which is easy to install there).
So, does anyone know any possibility to make a request to http://localhost:[port] when the site itself is HTTPS that works for both FF and IE?
Thanks!
Change the iframe to:
<script>
var request = new XMLHTTPRequest();
request.open("GET", "http://localhost:[port]/?action=doStuff");
request.send();
</script>
You will also need to make some minor modifications to your app.
It needs to implement an OPTIONS method and it needs to return a cross-origin-resource-policy. This sounds a lot harder than it is, it just needs to return a reply with the Access-Control-Allow-Origin header set to *.
The response of the GET request must also have this header.
If you know all the domains that try to communicate with your app on localhost you can change the * to a whitelist or even just a single value.
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.