Chrome not persisting CORS cookie - javascript

The setup uses a Backbone Model, Nginx server. The users enter their username and password which is then passed via a post. The server authenticates and returns a session cookie.
When the backend and front-end are on the same server (e.g. connect via localhost) the cookie is stored. However when the connection is remote, it is not stored in Chrome; however, it is stored in Safari and FireFox.
Ajax is setup via
$.ajaxSetup({
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
The request headers are
POST /login HTTP/1.1
Host: localhost:8102
Connection: keep-alive
Content-Length: 59
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://127.0.0.1:9102
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36
Content-Type: application/json
DNT: 1
Referer: http://127.0.0.1:9102/somefolder
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
The response headers are
HTTP/1.1 200 OK
access-control-allow-origin: http://127.0.0.1:9102
access-control-allow-credentials: true
vary: origin,accept-encoding
access-control-expose-headers: WWW-Authenticate,Server-Authorization
content-type: text/html; charset=utf-8
set-cookie: na-auth-token=encrypted-string; Max-Age=86400; Expires=Thu, 27 Apr 2017 13:32:44 GMT; HttpOnly; SameSite=Strict; Path=/
cache-control: no-cache
content-encoding: gzip
Date: Wed, 26 Apr 2017 13:32:44 GMT
Connection: keep-alive
Transfer-Encoding: chunked
In FireFox and Safari the cookie is stored just fine, but in Chrome it gets the response and tosses the cookie without any notification.
Update
The cookie is actually being saved under the localhost domain, however when you navigate back to the page (e.g. via a window.location.reload) the cookie disappears.

So the answer to my question lied in a library we were using
hapi-auth-cookie
The package updated and a flag was introduced isSameSite. Changed this value to false to allow the cors cookie to persist between pages loads.

Related

Ajax/Axios cookies not being set, but still being included in following requests

For some reason cookies set by server are not being saved, can't access them from within my js code, in devtools in Application tab in cookies section it's all empty, but somehow when I make a request to the server after the initial one, those cookies that apparently didn't save are being included in the header.
I have set withCredentials to true in axios config, as well as CORS_ALLOW_CREDENTIALS to true on my backend.
Here are the headers from the request that should save cookies:
Response Header:
access-control-allow-credentials: true
access-control-allow-origin: <frontend url>
content-length: 22
content-type: text/html; charset=utf-8
date: Mon, 01 Feb 2021 12:04:09 GMT
server: nginx/1.19.2
set-cookie: csrftoken=<token>; expires=Mon, 31 Jan 2022 12:04:09 GMT; Max-Age=31449600; Path=/; SameSite=Lax
set-cookie: sessionid=<sessionid>; expires=Mon, 01 Feb 2021 13:04:09 GMT; HttpOnly; Max-Age=3600; Path=/
strict-transport-security: max-age=31536000
vary: Cookie, Origin
x-frame-options: SAMEORIGIN
Request Headers:
:authority: <backend url>
:method: POST
:path: /login/
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cache-control: no-cache
content-length: 66
content-type: application/json;charset=UTF-8
dnt: 1
origin: <frontend url>
pragma: no-cache
referer: <current frontend url query>
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
As you can see in the response I'm getting two cookies to be set, sessionid and csrftoken, those are not being saved.
What's weird those cookies are present in the following request:
Request Headers:
:authority: <backend url>
:method: GET
:path: /numbers/
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cache-control: no-cache
cookie: csrftoken=<token>; sessionid=<sessionid>
dnt: 1
origin: <frontend url>
pragma: no-cache
referer: <current frontend url query>
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
How is it possible for the cookies to not be set but still included in the request? And what can cause this behavior? I really need them to be saved.
My backend and frontend are located at different urls, my backend is running on Django with a rest-framework.
UPDATE
I've also tried to set samesite on the cookies to none, as well as changing request content-type to application/x-www-form-urlencoded. Still no luck.
After hours spent troubleshooting this problem I found out a solution, somewhat. My frontend app and backend app are hosted from different urls, but under the same domain, like that: <frontend>.<mydomain>.com and <backend>.<mydomain>.com
When frontend application was making a request to the backend, backend was sending set-cookie headers that would save the cookies under <backend>.<mydomain>.com (I was able to confirm this in here: chrome://settings/siteData), which made them inaccessible from within <frontend>.<mydomain>.com, I were able to fix that by adding a domain property to the set-cookie header that would point to .<mydomain>.com. To do that I had to create a middleware in Django that would add my predefined domain name to the cookies before being send back to the client.
I'm not really 100% happy with this because if my applications were served from different domains, I'm not sure if I could make the cookies work at all, since setting set-cookie to a different domain is not allowed by the browser.

Setting a cookie with a webpack-dev-server client and hapi API

I have a ReactJS client running webpack-dev-server on localhost:3000. It connects to a Hapi API server on localhost:8080 and I'm trying to provide a basic cookie using hapi-auth-jwt2 (I've also tried hapi-auth-cookie with equal results).
I can see the response header provides a valid set-cookie header and everything looks okay, but all my browser tests ignore it and the cookie is never set (verified by checking document.cookie and using the browser tools like Chrome's Application tab). When I connect directly to the API server with Postman, it picks up the set-cookie header correctly and stores it so I think it's just some kind of domain/port/host configuration issue.
As a simple test, I tried deploying to our ec2 environment but that didn't help. The ec2 environment is similar, with one instance serving the client and another instance serving the API. I've also tried modifying my local hosts file to redirect a domain like 127.0.0.1 example.com and providing the domain=.example.com field in the cookie, but that also didn't help.
I think I'm just missing something basic but I don't know what it is. See below for response/request headers on login.
Request Headers
POST /login HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 47
Accept: application/json, text/plain, */*
Origin: http://localhost:3000
Authorization: undefined
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
Content-Type: application/json
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Response Headers
HTTP/1.1 200 OK
authorization: <jwt token>
vary: origin,accept-encoding
access-control-allow-origin: http://localhost:3000
access-control-allow-credentials: true
access-control-expose-headers: WWW-Authenticate,Server-Authorization
content-type: application/json; charset=utf-8
set-cookie: cookie=token; Max-Age=604800; Expires=Wed, 16 May 2018 21:11:23 GMT; SameSite=Lax; Path=/
cache-control: no-cache
content-encoding: gzip
Date: Wed, 09 May 2018 21:11:23 GMT
Connection: keep-alive
Transfer-Encoding: chunked
http-proxy-middleware, which webpack-dev-server uses, has options for cookie domain/path rewrites.
You should see if those satisfy your needs. Otherwise you can also manually parse and reset cookies in the onProxyRes callback. Here is an example.

Why does jquery send an options request?

I'm running a Jenkins server behind an Apache Proxy.
I'm using Chrome as my browser
Now in a couple of places I have javascript code that looks like this.
console.log("Looking for " + log_url)
$.ajax({
url: log_url,
dataType: "xml"
})
.done(function(xml) {
console.log("FOUND " + log_url);
})
(with something more interesting going on in the "done" section of course)
Now, in one place it works fine, that's when it's in a javascript file in jenkins userContent (a folder where you can put files you want jenkins to serve up via HTML) which is loaded from HTML on a jenkins generated page.
Looking at the headers I see
Request URL:http://myserver.com/jenkins/job/WPF-TryBuild/1190/artifact/Win32_Debug_build_log.xml
Request Method:GET
Status Code:200 OK
Remote Address:82.39.249.244:80
Response Headers
view parsed
HTTP/1.1 200 OK
Date: Mon, 31 Oct 2016 12:35:58 GMT
Server: Jetty(9.2.z-SNAPSHOT)
X-Content-Type-Options: nosniff
Content-Security-Policy: sandbox allow-scripts; default-src 'self'; script-src 'self' https://ajax.googleapis.com;
X-WebKit-CSP: sandbox allow-scripts; default-src 'self'; script-src 'self' https://ajax.googleapis.com;
X-Content-Security-Policy: sandbox allow-scripts; default-src 'self'; script-src 'self' https://ajax.googleapis.com;
Last-Modified: Mon, 31 Oct 2016 10:40:11 GMT
Expires: Mon, 31 Oct 2016 10:40:11 GMT
Accept-Ranges: bytes
Content-Type: application/xml
Content-Encoding: gzip
Access-Control-Allow-Origin: null
Access-Control-Allow-Methods: GET, PUT, OPTIONS
Access-Control-Allow-Headers: X-Requested-With
Keep-Alive: timeout=5, max=93
Connection: Keep-Alive
Transfer-Encoding: chunked
Request Headers
view parsed
GET /jenkins/job/WPF-TryBuild/1190/artifact/Win32_Debug_build_log.xml HTTP/1.1
Host: myserver.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: application/xml, text/xml, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
Referer: http://myserver.com/jenkins/job/WPF-TryBuild/1190/?auto_refresh=false
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cookie: ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE=Sm9uOjE0Nzg4NjU1MTIyODg6ZjNlNmM5NDIwZDQwYWU0YzdiOTc2MWJjN2NjNzUxNDE5NDdhNmI3OGI0M2E1OGIxMzM4NjEwYmEyZjRmOGUwZA==; JSESSIONID.e269d02c=1nyxka737w02s1v7tq8opej8fu; JSESSIONID.61f82bd1=5qgyu4eegvkozmo5u1axqc6t; screenResolution=1440x900; JSESSIONID.61f82bd1=238or6iqc0se7r2lo4cyyco4; hudson_auto_refresh=false
So it's sending a GET request, headers are being served up as I have set them in the Apache proxy, and the page works, lovely.
However, if I do the same thing in a javascript file loaded by an HTML page which is in userContent, I see the following headers
Request URL:http://myserver.com/jenkins/job/WPF-TryBuild/1190/artifact/Win32_Debug_build_log.xml
Request Method:OPTIONS
Status Code:403 Forbidden
Remote Address:82.39.249.244:80
Response Headers
view parsed
HTTP/1.1 403 Forbidden
Date: Mon, 31 Oct 2016 13:18:45 GMT
Server: Jetty(9.2.z-SNAPSHOT)
X-Content-Type-Options: nosniff
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=UTF-8
X-Hudson: 1.395
X-Jenkins: 2.27
X-Jenkins-Session: f3999f25
X-Hudson-CLI-Port: 8081
X-Jenkins-CLI-Port: 8081
X-Jenkins-CLI2-Port: 8081
X-You-Are-Authenticated-As: anonymous
X-You-Are-In-Group:
X-Required-Permission: hudson.model.Hudson.Read
X-Permission-Implied-By: hudson.security.Permission.GenericRead
X-Permission-Implied-By: hudson.model.Hudson.Administer
Set-Cookie: JSESSIONID.61f82bd1=tts6a6zkw60r1rfiu36q4l7b8;Path=/jenkins;HttpOnly
Access-Control-Allow-Origin: null
Access-Control-Allow-Methods: GET, PUT, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-requested-with
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 383
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Request Headers
view parsed
OPTIONS /jenkins/job/WPF-TryBuild/1190/artifact/Win32_Debug_build_log.xml HTTP/1.1
Host: myserver.com
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
Access-Control-Request-Headers: x-requested-with
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
So it is sending an OPTIONS request, which is failing, so the code fails there and the page doesn't work (incidentally it works fine when I don't
So I have two questions.
1) Why do I see an OPTIONS request in one case, but not the other?
2) How can I make it work?
Now I know that an OPTIONS request is sent when the jquery is cross-domain, but I don't see how this would qualify as cross domain.
Everything is served up by the same server, the only difference is that in one case the parent page is generated by jenkins at the url /jenkins/job/WPF-TryBuild/[job_number]
whereas in the failing case the parent page is an html page served up by jenkins at
/jenkins/userContent/WpfReports/msbuild/MSBuildLog.html
(Incidentally, changing Access-Control-Allow-Origin from null to * makes no difference in either case)

Browser refresh corrupts bundled javascript

I added ASP.NET javascript bundling and minfication to our website, and have discovered that now if I refresh the website, the bundled javascript file is downloaded as gzip but never uncompressed by the browser, so the site of course doesn't work since the javascript is still compressed. If I refresh again, the site is fine. Refresh again, corrupt. Back and forth. This is ONLY happening in Chrome and Safari, but not in IE.
Watching in Fiddler, IE gets the javascript the first time, and subsequent refreshes return 304 not modified which is correct. Chrome / Safari refreshes continually return 200 for each refresh.
If I run my website locally hosting it in IIS Express, this problem does NOT occur. It's only in our other QA, Staging and Production environments where the website is hosted by IIS 7.5.
I did a Fiddler compare of the good Chrome request and the bad Chrome request to see what's different. They are identical except the bad request has the "If-Modified-Since" header.
The response from the server when the "If-Modified-Since" header is there has a Content-Type of "application/javascript", and this is what I think is causing the problem, the browser doesn't know it should uncompress the response body.
Here is the GOOD request and response (body omitted for brevity):
GET https://wwwq.website.com/Scripts/main.js?v=z3QOtK2bjf3mvSQyLf2KY82lAidw4JR0ePo01WHF93U1 HTTP/1.1
Host: www.website.com
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36
DNT: 1
Referer: https://www.website.com/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cookie: < cookies omitted >
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2016 13:46:30 GMT
Server: Microsoft-IIS/7.5
Cache-Control: public
Content-Type: text/javascript; charset=utf-8
Expires: Sat, 04 Feb 2017 13:46:31 GMT
Last-Modified: Fri, 05 Feb 2016 13:46:31 GMT
Vary: User-Agent,Accept-Encoding
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Via: 1.1 wwwq.website.com (Access Gateway-ag-2772908347919084-467794)
Keep-Alive: timeout=300, max=99
Connection: Keep-Alive
Content-Length: 62702
Here is the BAD request and response (body omitted for brevity):
GET https://www.website.com/Scripts/main.js?v=z3QOtK2bjf3mvSQyLf2KY82lAidw4JR0ePo01WHF93U1 HTTP/1.1
Host: www.website.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36
DNT: 1
Referer: https://www.website.com/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cookie: < cookies omitted >
If-Modified-Since: Fri, 05 Feb 2016 13:46:31 GMT
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2016 13:46:43 GMT
Server: Microsoft-IIS/7.5
Cache-Control: private
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Vary: Accept-Encoding
Via: 1.1 www.website.com (Access Gateway-ag-2772908347919084-467849)
Content-Length: 16042
Keep-Alive: timeout=300, max=94
Connection: Keep-Alive
Content-Type: application/javascript
If anyone can help me out I would be incredibly grateful I am running out of ideas.

http cache and fingerprinting usage

I am trying to understand how http cache and fingerprinting
works. I've setup my express server to cache assets forever like this:
router.use('/public',
express.static(path.join(__dirname, '..', 'public'),
{ maxAge: 864000000 }));
I am expecting this to cache assets forever, even if i change the
content of the files, thus i will need to fingerprint the filenames to
bust the cache. However;
This is Google Chrome Headers output for static asset common.js after a reload
Remote Address:192.168.56.101:3000
Request URL:http://192.168.56.101:3000/public/assets2/scripts/app/common.js
Request Method:GET
Status Code:304 Not Modified
Request Headers
GET /public/assets2/scripts/app/common.js HTTP/1.1
Host: 192.168.56.101:3000
Connection: keep-alive
Cache-Control: max-age=0
Accept: */ *
If-None-Match: W/"ogrxaeWybJBlXMTTr2leWA=="
If-Modified-Since: Fri, 11 Jul 2014 13:46:01 GMT
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Referer: http://192.168.56.101:3000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Response Headers
HTTP/1.1 304 Not Modified
X-Powered-By: Express
Accept-Ranges: bytes
Date: Fri, 11 Jul 2014 13:48:34 GMT
Cache-Control: public, max-age=864000
Last-Modified: Fri, 11 Jul 2014 13:46:01 GMT
ETag: W/"ogrxaeWybJBlXMTTr2leWA=="
Connection: keep-alive
Nice, i get a 304.
Now i change the contents of common.js and do a reload again, this is the output:
Remote Address:192.168.56.101:3000
Request URL:http://192.168.56.101:3000/public/assets2/scripts/app/common.js
Request Method:GET
Status Code:200 OK
Request Headers
GET /public/assets2/scripts/app/common.js HTTP/1.1
Host: 192.168.56.101:3000
Connection: keep-alive
Cache-Control: max-age=0
Accept: */ *
If-None-Match: W/"ogrxaeWybJBlXMTTr2leWA=="
If-Modified-Since: Fri, 11 Jul 2014 13:46:01 GMT
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Referer: http://192.168.56.101:3000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Response Headers
HTTP/1.1 200 OK
X-Powered-By: Express
Accept-Ranges: bytes
Date: Fri, 11 Jul 2014 13:50:35 GMT
Cache-Control: public, max-age=864000
Last-Modified: Fri, 11 Jul 2014 13:50:33 GMT
ETag: W/"o65+0J5C8swpsmHMxNPH+w=="
Content-Type: application/javascript
Content-Length: 1908322
Connection: keep-alive
At this point, i was expecting to get a 304 but
appearently server detected the changes and sent a 200.
So i didn't have to use fingerprinting. Where did i go wrong?
Express is permanently caching it on the server side by keeping it in memory. My guess is that the express framework maintains cache consistency by checking whether or not cached resource has been modified.
Sending a request with a if-none-match and/or a if-modified-since header is the correct behavior for a user-agent. IE is attempting optimization by skipping a network round-trip, which may lead to incorrectly loaded pages.
What you need to do is either use fingerprinting - which assigns a new generic name to each modified resource - or have more low-level control about how your server serves resources, i.e. parsing the url yourself and defining rules about how responses are formed, a 304 response in your case.
I think the issue is with the Google Chrome, apparently when i hit reload, or press enter on url bar, Chrome still sends a If-None-Match request to server, and gets a 200. I tried with Internet Explorer, and it successfully server from cache without hitting the server. I am still wondering what is wrong with Chrome though, and how do i make it serve from cache without hitting the server.

Categories

Resources