Set-Cookie is not working in Chrome - with two websites - javascript

I have a problem with Set-Cookie not working in Chrome (I didn't check other browsers). It worked in the past but it stopped working recently. I have two websites with two domain names, and I need to set the cookie in both websites. I'm calling a URL in each of the domain names to set the cookie. But it doesn't set the cookie on the other website (the website I'm not browsing now).
The users login or logout or sign up to one website, and I want them to login or logout from the other website too, automatically. Currently if they login or logout to one website, it doesn't affect the other website.
The Django view code is:
#csrf_exempt
def set_session(request):
"""
Cross-domain authentication.
"""
response = HttpResponse('')
origin = request.META.get('HTTP_ORIGIN')
if isinstance(origin, bytes):
origin = origin.decode()
netloc = urlparse(origin).netloc
if isinstance(netloc, bytes):
netloc = netloc.decode()
valid_origin = any(netloc.endswith('.' + site.domain) for site in Site.objects.all().order_by("pk"))
if (not (valid_origin)):
return response
if (request.method == 'POST'):
session_key = request.POST.get('key')
SessionStore = import_module(django_settings.SESSION_ENGINE).SessionStore
if ((session_key) and (SessionStore().exists(session_key))):
# Set session cookie
request.session = SessionStore(session_key)
request.session.modified = True
else:
# Delete session cookie
request.session.flush()
response['Access-Control-Allow-Origin'] = origin
response['Access-Control-Allow-Credentials'] = 'true'
return response
And the JavaScript code is:
window.speedy = {};
window.speedy.setSession = function (domain, key) {
$.ajax({
url: '//' + domain + '/set-session/',
method: 'post',
data: {
key: key
},
xhrFields: {
withCredentials: true
}
});
};
Then there is a JavaScript code that calls this function twice:
speedy.setSession('speedy.net', 'session_key');
speedy.setSession('speedymatch.com', 'session_key');
Where 'session_key' is replaced by the session key of the user.
Is there any solution to this problem? I think this is due to recent changes in Chrome.
Update: We have a staging server where both the websites domains are subdomains of the same registered domain name. And there, Set-Cookie works fine. But in the production websites, I think the other site's cookies are blocked by Chrome because the other site's domain is different from the domain the user is currently browsing.
I checked and the cookies from the other website also don't work with Firefox and Dolphin. It might be related to the upgrade to Django 2.1 which we upgraded recently.

The same origin policy for cookies being triggered here; from a domain you can set cookies for:
own domain
parent domain (unless the parent domain is a (g)TLD)
So as the two domains in question do not share the parent-child relationship and the only common parent of them could be the TLD (assuming same TLD), you can't do this.
From MDN doc:
Cookies use a separate definition of origins. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix. Firefox and Chrome use the Public Suffix List to determine if a domain is a public suffix. Internet Explorer uses its own internal method to determine if a domain is a public suffix. The browser will make a cookie available to the given domain including any sub-domains, no matter which protocol (HTTP/HTTPS) or port is used. When you set a cookie, you can limit its availability using the Domain, Path, Secure and Http-Only flags. When you read a cookie, you cannot see from where it was set. Even if you use only secure https connections, any cookie you see may have been set using an insecure connection.

Thanks to #aaron I found out the problem. This problem started only recently, after I upgraded Django to 2.1. Django 2.1 introduced the SESSION_COOKIE_SAMESITE setting, which must be set to None for our websites to work properly with session cookies. On the other hand, CSRF_COOKIE_SAMESITE for our websites can be set to 'Strict', since we use separate CSRF cookies for each website. Therefore, I added the following lines to our base settings:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = None
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'Strict'
From those lines, only SESSION_COOKIE_SAMESITE = None is necessary to fix the problem I mentioned in this question. I relied on the default setting of Django 2.1 to the value of SESSION_COOKIE_SAMESITE, which was not working for us in this case.
Currently the login and logout works in Chrome on my desktop and in one mobile phone. But I checked another mobile phone I have, and there it doesn't work - the problem persists as it was before. I'm not sure if this is due to a personal settings in this mobile phone or in the Chrome app? But login and logout to both websites simultaneously doesn't work there. If I login to one website, I'm still logged out from the other website, and vice versa.
Currently the login and logout works in Chrome. The problem was cookies settings - check your settings at chrome://settings/cookies (desktop) or settings > site settings > cookies (mobile).
(August 2020) Update: It is now required to use the following settings for Chrome. Please see this question and answer.
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'None'

Related

set cookie on parent domain but from sub-domain website

When I visit a sub-domain website ex: https://sub2.example.com, from a browser console I can set a cookie for parent domain.
document.cookie = "nameCookie=HelloWorld; domain=.example.com;"
as per Cookie RFC this works! and this cookie should be available to all sub-domains.
ex:
https://example.com
https://sub2.example.com
https://xxx.example.com
But my problem, this concept is not working on some websites.
for ex:
Go to https://square.github.io/
open browser console
document.cookie = "nameCookie=HelloWorld; domain=.github.io;"
console.log(document.cookie)
check that nameCookie is not available.
Why it is not working here? any Http header/rule setup on those websites?
Because github.io is on the list of effective top-level domains (eTLDs) (raw list here), so each github.io subdomain is treated like a subdomain of a top-level domain (that is, _______.github.io is treated just like _______.com or _______.co.uk).

Cookie set by Flask app sent but not stored

I have a backend Flask app running on localhost:3000 and a React front-end app running on localhost:5000. In my backend app I am using Flask's 'Response.set_cookie' to set a cookie:
resp = make_response({}, 200)
resp.set_cookie('my_cookie_name', 'my_val', max_age=604800, domain='127.0.0.1', samesite='Lax', secure=None, httponly=None)
I am also allowing cross-origin for all responses in my flask app as follows:
# Child class of Flask to override some features
class TailoredFlask(Flask):
# Override make_response
def make_response(self, rv):
# Call default version from partent
resp = super().make_response(rv)
# Add CORS header to every response
resp.headers["Access-Control-Allow-Origin"] = "*"
resp.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS,HEAD"
resp.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Authorization"
return resp
My client accesses my flask cookie endpoint with a call to fetch.
In the Chrome dev tools I can see that the cookie is sent with the HTTP response from my backend. It is visible when on the Network->Cookies tab when I select the request to my backend. However, if I go to the Application tab in the dev tools, my cookie is not there.
It seems like chrome is silently discarding my cookie. I have seen several simiar issues here on SO but none of them seem to explain what is going on or provide a solution to my issue.
I'm also confused about the cookie options. There is a 'domain' option which I've read is to allow cross domain operation for the cookie. However, everything is running on localhost so I feel that I shouldn't need this unless the port is causing issues. However, I have also read that the port should not be included in the cookie 'domain' field.
If anyone can help to explain this to me I would greatly appreciate it because I'm just going round in circles with this stuff.
One more thing to note: I am pointing the browser at 'localhost', but the API call to my backend and the cookie domain both use '127.0.0.1', since I've read elsewhere that the 'domain' field must have at least two dots in it. (I don't have a choice in the browser URL since I am using AWS cognito login UI to redirect to my app after login. Cognito allows http for 'localhost', but only allows https for '127.0.0.1' so I have to use 'localhost' for development.) Could the missmatch between the browser url and cookie domain be causing this issue? Or is there something else that I'm missing?
Ok, so I think I now understand what's going on here, although I don't think there's a fix for my specific problem. As described in this thread browsers (including Chrome) will not allow a domian of 'localhost' within a cookie (I just wish there was a message in the console or something to indicate why the cookie is not being saved, rather than a silent fail!)
There are various suggestions for workarounds, such as using '.app.localhost' to access the application. Unfortunately this is not an option for me as I am redirecting to my front-end app from AWS Cognito, and the only domain that is supported with HTTP (rather than HTTPS) is 'localhost'. Variants such as '.app.localhost' or '127.0.0.1' are not allowed.

How to use CloudFront signed cookies in the browser?

So I have a private CDN (can't access data through S3, only by signed URL & cookies) using CloudFront that holds some files for a web application.
One of the pages on my site displays an image whose source lives on the cdn.
<img src="cdn.example.com/images/file.jpg">
Now when I generate a signed URL on the server I can just pass it to the client and set it as source...Easy
But when I try to do the same thing using signed cookies I run into 2 problems:
After generating the signed cookies server-side, I set them using res.cookie but I'm not sure how to find them in response on the client side.
In the event of actually getting the cookie on the client side (in angular/javascript), how to I "set" it so that when the browser tries to grab the image in the above mentioned html it allows access?
I'm using node, express, and cookie-parser.
I'm fairly new to this stuff so any help is appreciated
Have you checked that the domain for the cookie is the same or a subdomain of the CloudFront CDN?
The cookie will be included in the request to the CloudFront distribution only if it is coming from a related domain. For example, if you are testing locally using "localhost" that won't work as the cookie domain is "localhost" but the signed cookie domain is "cdn.example.com".
To ensure that the domain is correct:
Set up a CNAME for the CloudFront Distribution e.g.
http://cdn.example.com
Make sure the signed cookie has the correct
domain e.g. domain:'*.example.com' will match against the domain.
There's a good article here:
https://www.spacevatican.org/2015/5/1/using-cloudfront-signed-cookies/
There's also a good node module for creating signed cookies:
https://github.com/jasonsims/aws-cloudfront-sign
Using this module you would do something like:
const options = {
keypairId: YOUR_ACCESS_ID,
privateKeyPath: PATH_TO_PEM_FILE
};
const signedCookies = cf.getSignedCookies("http://cdn.example.com/*", options);
for (const cookieId in signedCookies) {
res.cookie(cookieId, signedCookies[cookieId], {domain: ".example.com"});
}
Note the way I set the domain. If not explicitly set it will default to the one that made the request, which is probably not what you want.
Last thing, to test this locally, I set up port-forwarding on my home network and created a CNAME in GoDaddy to point to my home IP e.g. "test.example.com". I then used "http://test.example.com" instead of "http://localhost". This created a cookie with the subdomain "test.example.com". Finally, I created another CNAME in GoDaddy called "cdn.example.com", pointing it to the CloudFront URL. I used the wildcard domain in my signed cookie, ".example.com" and because I now have the signed cookie on the same domain "example.com" it's passed to CloudFront and boom we have liftoff!
For those trying to access a protected resource in CloudFront from a different domain you can not set the cookies cross domain so you're limited to using Signed URLs instead. However you can use javascript on the CloudFront site to use the parameters in the Signed URL to create a Signed Cookie directly in CloudFront. I put together a simple JS script that does just that.
aws-signed-cookie-from-signed-url.js
build signed url in www.mysite.com that points to CloudFront url www.cloudfronturl.com/sample/index.html
included the JS in the CloudFront resource "sample/index.html"
upon the user using the signed URL, index.html will create the SignedCookie which will allow all additional navigation within www.cloudfronturl.com/sample to work based on the cookie

Keycloak is causing IE to have an infinite loop

we are using a keycloak 1.3.1 authentication library, and I've noticed that once I initialize the keycloak with { onLoad: 'login-required' }, IE (11) gets infinite loop...
Other browsers work fine.
I'm basically doing this:
keycloak.init({ onLoad: 'login-required' }).success(function(authenticated) {
console.info(authenticated ? 'authenticated' : 'not authenticated');
some other stuff...
}).error(function() {
console.warn('failed to initialize');
});
Any idea what's causing it, and to solve this? Trying to install the newest version 1.4.0 now in hopes the weird bug gets solved.
Thanks in advance.
I had the same problem with keycloak v1.5.0.Final / Internet Explorer 11, and finally figured out what is going on.
1. Behind the scene
When using modes 'login-required' or 'check-sso' in Keycloak's init method, Keycloak Javascript Adapter sets an iframe that checks at timed intervals that user is authenticated.
This iframe is retrieved from keycloak's server (let's say http(s)://yourkeycloakhost:port):
http(s)://yourkeycloakhost:port/auth/realms/yourrealm/protocol/openid-connect/login-status-iframe.html?client_id=yourclientid&origin=http(s)://yourorigin
and its content is a javascript script which should be able to access KEYCLOAK_SESSION cookie previously set by keycloak on authentication (on the same domain ie http(s)://yourkeycloakhost:port).
2. The problem with IE
Yes! Here is the problem with Internet Explorer, which has a strict policy with iframes and cookies. Actually, the keycloak iframe does NOT have access to the yourkeycloakhost domain cookies due to its P3P policy (Microsoft Internet Explorer is the only major browser to support P3P).
This problem is well described on this stackoverflow question
3. Resolution
The solution is to make Internet Explorer trust our keycloak's domain (yourkeycloakhost) for using cookies, so that the iframe is able to read the KEYCLOAK_SESSION cookie value, and register it in its data.
To do that, your keycloak server must append HTTP response header with P3P information. You can do that with an apache or nginx proxy that will always set proper headers. I did that with apache and it's mod_headers module:
Header always set P3P "CP=ALL DSP COR CUR ADM PSA CONi OUR SAM OTR UNR LEG"
You can learn more on P3P with W3C and/or validate your P3P Policy with this P3P validator.
4. Consequence
You can have a look at keycloak's iframe code :
var cookie = getCookie('KEYCLOAK_SESSION');
if (cookie) {
data.loggedIn = true;
data.session = cookie;
}
Now the cookie on domain yourkeycloakhost is retrieved correctly by Internet Explorer, and the problem is fixed!
A workaround that worked for me, learnt from keycloak documentation, add the parameter checkLoginIframe when executing init method : .init({onLoad: 'login-required', checkLoginIframe: false})
The Keycloak developers fixed this problem, as described by #François Maturel, in version 1.9.3. See for more information issue #2828.

uploading images - do they have domain name in them - chrome not sending session id

For testing I downloaded images from the net and uploaded using valum file upload in chrome...chrome is not sending session cookie along with these request header( I dont see that in the server side/though I see it on developer tool)...does chrome know that these images are from different domain . what is happening...Is there work around for this to pass the session id (as cookie). It is also happening in IE10 which makes me belive it is some standard. and not just a chrome issue. This problem is not there with firefox/safari/opera
It is fine when uploading to localhost. only when uploading to different server with domain name there is this problem leading to creating a new session for this.
Update:
I have added xhr.withCredentials = true still no use.
Also added on the server side to the upload url...
res.setHeader 'Access-Control-Allow-Origin', '*'
res.setHeader 'Access-Control-Allow-Credentials', true
I dont know how helpful this would be, because I would have already sent the upload file and response header will not of much help.
basically the problem is I don't have access to the session variable at the server side, since the session id/sid cookie is not coming back /I am not able to save some of this upload details into the current session(because this is a new session) .
Update:
I tried creating an image in teh desktop using paint..even then chrome would not sent the cookies. Really drives me crazy...
First of all, to get the basics out of the way, this is unrelated to the origin of the image. Chrome or other browsers don't care where you get your images.
It's rather difficult to guess exactly what's going on, would have helped to see a jsfiddle or some more setup explanation, but based on what I'm guessing, you might be using different domains for the page where the upload button is hosted and the target url where you're sending your files (even using ssl for one and http for the other makes it different). Even different subdomains will not allow cookies to be passed if the cookies were not set with a base domain (yourdomain.com)
So, if sub-domains are the problem, you know what to do - set a base domain so you get your cookies to go on any sub domain.
If it's http vs. https you need to always use https (or http) because you can't switch cookies between those two.
If that's not it, or if you're using completely different domains, you can access your cookies locally via script (if they're not marked as http only) and add them to the upload request. Valum 2.0 (don't know about v1.0) lets you add parameters to the request like so:
var uploader = new qq.FileUploader({
element: document.getElementById('file-uploader'),
action: '/server-side.upload',
// additional data to send, name-value pairs
params: {
param1: 'value1',
param2: 'value2'
}
});
You can't set cookies on a domain which is not the page's domain via script so for using completely different domains your only choice is using request params.
It is possible that the uploader is using Flash under some circumstances to do the upload. There is a bug in Flash which prevents cookies being sent for these types of requests. This would explain the behaviour you are seeing. The workaround is to pass in the sessionId and transmit it in a different way eg. querystring.

Categories

Resources