I'm using both a front-end and a back-end application on a different domain with a session-based authorization. I have setup a working CORS configuration, which works as expected on localhost
(e.g. from port :9000
to port :8080
). As soon as I deploy the applications on secure domains (both domains only allow HTTPS), the CSRF cookie is not accessible anymore within JavaScript, leading to an incorrect follow-up request of the front-end (missing the CSRF header).
The cookie is set by the back-end in the Set-Cookie
header without using the HttpOnly
flag. It is actually set somewhere in the browser, because the follow-up request contains both the session cookie and the CSRF cookie. Trying to access it by JavaScript (using e.g. document.cookie
in the console) returns an empty string. The DevTools of Chrome do not show any cookies on the front-end domain (the back-end domain is not even listed).
I'm expecting the cookie to be set and being visible on the current domain (front-end domain). I'm using the withCredentials
flag of the axios library.
Do you have any idea, why the cookie cannot be accessed from JavaScript nor from the DevTools in Chrome? Does this have anything to do with the Strict-Transport-Security
header?
1. Initial GET Response Header
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://[my-frontend-domain]
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Content-Type: application/json;charset=UTF-8
Date: Wed, 20 Sep 2017 11:57:07 GMT
Expires: 0
Pragma: no-cache
Server: Apache-Coyote/1.1
Set-Cookie: CSRF-TOKEN=[some-token]; Path=/
Vary: Origin,Accept-Encoding
X-Content-Type-Options: nosniff
X-Vcap-Request-Id: [some-token]
X-Xss-Protection: 1; mode=block
Content-Length: [some-length]
Strict-Transport-Security: max-age=15768000; includeSubDomains
2. Follow-up POST Request Header
POST /api/authentication HTTP/1.1
Host: [my-backend-host]
Connection: keep-alive
Content-Length: [some-length]
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: [my-frontend-host]
User-Agent: [Google-Chrome-User-Agent]
Content-Type: application/x-www-form-urlencoded
DNT: 1
Referer: [my-frontend-host]
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,de-CH;q=0.2,it;q=0.2
Cookie: [some-other-cookies]; CSRF-TOKEN=[same-token-as-in-the-previous-request]
This request should contain a CSRF header which would automatically be added if the cookie was accessible with JavaScript.
In short, it is not possible to access cross-origin cookies, document.cookie
can only access the current (or parent) domain cookies.
The hint for that being the root cause, was ssc-hrep3 mentioning "both domains" in his question.
It's very to easy to make that mistake when switching from a localhost deployment, using only different ports for back-end and front-end servers, to one that uses two different hosts. That will work locally, because cookies are shared across ports, and will fail when two different hosts are used. (Unlike some other CORS issues that will be also exposed locally)
See ssc-hrep3's answer for more information and a workaround.