issue with cross-site cookies: how to set cookie from backend to frontend
Asked Answered
C

4

26

I'm currently developing my first webapp, frontend with React and backend with FastAPI.

I'm trying to test it out jointly with Chrome-- see if the frontend makes the correct API calls to backend, and display the results. I've been having problems with cookies, and I'd like help. Apologies in advance for the long post – I've been going through many resources past couple of days, and at this point I'm unsure what's relevant and what's not.

  • Frontend on localhost:8080
  • Backend on http://127.0.0.1:8000
  • Proper settings for CORS (I believe) with the following FastAPI backend code:
app = FastAPI()

origins = [
    "http://localhost:8080"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


Situation: Frontend makes a GET request to http://127.0.0.1:8000/api/abc on backend, the backend sets a cookie.

/*====================
Attempt 1:
set cookie with the following backend code:

response.set_cookie(key="abcCookieKey", value="abcCookieValue")

and make the GET request with the following frontend JS code:

fetch('http://127.0.0.1:8000/api/abc', {
            method: 'GET',
            credentials: 'include',
        })

Result with attempt 1:
on the Console tab of Chrome, I get the following warning:

A cookie associated with a cross-site resource at http://127.0.0.1/ was set without the `SameSite` attribute. It has been blocked, as Chrome now only delivers 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.

and on the network tab I get the following message when examining the set-cookie response header:

This Set-Cookie was blocked because it has the "SameSite=Lax" attribute but came from a cross-site response which was not the response to a top-level navigation.

====================*/


...so I do some research, and come up with

/*====================
Attempt 2:
set cookie with the following backend code:

response.set_cookie(key="abcCookieKey", value="abcCookieValue", samesite="none", secure=True)

and make the GET request with the same frontend JS code.

Result with attempt 2:
on the Console tab of Chrome, I get the exact same warning as from attempt 1, even though the response header has a set-cookie with Samesite=none; Secure. Additionally, the header has the following warning

This Set-Cookie was blocked because it had the "Secure" attribute but was not received over a secure connection.

====================*/


..so I try to use https and come up with:

/*====================
Attempt 3: Everything the same as attempt #2, except in my JS fetch code, I use fetch('https://127.0.0.1:8000/api/abc ...
and then I get the following warning on my backend running with uvicorn: WARNING: Invalid HTTP request received.

====================*/

Questions:

  1. am I missing something with attempt #2? surely there must be an easy way to set a cookie from backend to frontend without worrying about https?
  2. if I don't have a choice but to use https, how do I locally run a backend server that can be accessed with https? The research I did made it seem like it was a complicated/time-consuming process. (But, to be fair, my understanding of web-dev/all things network is very limited).
Chante answered 21/7, 2020 at 8:32 Comment(8)
did you solve this?History
the short answer is no, I didn't solve it. I just ended up using Safari to get around it, then checked that it works on Chrome after deployment.Chante
I resolved it, if you need I can post the answerHistory
how did you resolved it Shashan ?Julissa
Please post the answer @ShashanSooriyahetti as you can see there are many of us who would like to know.Duiker
@Julissa sorry your comment did not reach me as you have failed to mention me man. I have added the answerHistory
@Duiker I have added the answer as you guys requested feel free to ask anthingHistory
Future readers might find these related answers: here, here, as well as here and here helpful.Alcala
H
11

I will try to give the concept.

First please know that I'm using a Backend which is developed from SailsJS and a Frontend which is developed from AngularJS and Apps that connect to backend which are developed from using Angular9.

I am using http only cookie for the frontend (a CMS) to grant permission to add content to users that are logged in. HTTP only cookie will be set to them on authentication success.

Please note that there is a tricky part to http cookies and backend and frontend both serve different URLs just like in the scenario presented.

Points:

  1. So when in development you have to host your backend and frontend under same IP. ex. my backend : 192.168.8.160:1337, frontend: 192.168.8.160:81. different ports same ip.

  2. this is not the scenario when it goes to production you can have any domain :)

  3. you have to allow CORS in backend and have your frontend ip:port under accepted origins.

  4. Implementation, you have to identify your environment, in my case,


if (sails.config.environment === 'development'){
  res.setHeader('Set-Cookie',[`token=${token};  Path=/;HttpOnly; maxAge=86400000;SameSite=false;`]);
} else {
  res.setHeader('Set-Cookie',[`token=${token};  Path=/;HttpOnly; maxAge=86400000;SameSite=None;Secure=true;`]);
}


Please note in above code in development environment you need SameSite=false therefore you need to have the same ip because we cannot have SameSite=None because it would require to have Secure=true and it would then require HTTPS. It would be a headache to achieve it in development mode.

That's it folks. If you get this right you can achieve this.

History answered 16/10, 2020 at 7:32 Comment(6)
Thank you so much...This ans. saved my bunch of time.I work with Django + React(JWT Auth.).when I set Httponly cookie in backend but it is not set in frontend api request.Dhiman
I have been trying to solve the exact same issue for a whole day now. This answer did it for me. I still don't understand what is the difference between samesite false and same samesite none. I thought I they were the exact same thing. Anyway this will do for nowThornton
@KarimKamel glad it helped, you can give a upvote ;) the difference between SameSite=none means you can have this cookie for other domains as well but you need https to do that but because you cannot achieve that in development environment we use SameSite=false we can use the same IP with different ports rather than using different IPs. Because it is using same IP you don't need HTTPSHistory
If you are using create react app and wanted to use HTTPS, just add HTTPS=true to your start script in package.json: scripts: { "start": "HTTPS=true react-scripts start" }Sportive
@Sportive good one, angular also supports HTTPS with ng s --sslHistory
@Chante you should promptly mark this as the accepted answer, a truly brilliant explanationAssurgent
L
0

You can completely disable this feature by going to "chrome://flags" and disabling "Cookies without SameSite must be secure". However, this will disable it for all sites, so it will be less secure when you aren't developing too.

Lacker answered 19/8, 2020 at 14:10 Comment(1)
this didnt work for, i still am getting the "secure "errorJulissa
O
0

Remove the wildcards since wildcarding is not allowed with allow_credentials=True :

app.add_middleware(
  CORSMiddleware,
  allow_origins=['http://localhost:8080'],
  allow_credentials=True,
  allow_methods=["GET", "POST", "OPTIONS"], /* include additional methods as per the application demand */
  allow_headers=["Content-Type","Set-Cookie"], /* include additional headers as per the application demand */
)

Set samesite to none while setting the cookie (modern browsers demand it):

/* `secure=True` is optional and used for secure https connections */
response.set_cookie(key='token_name', value='token_value', httponly=True, secure=True, samesite='none')

If client side is using Safari, disable Prevent cros-site tracking in Preferences. That's It!

Olnton answered 6/12, 2021 at 3:59 Comment(0)
N
0

I was using a normal django backend and a next js frontend, and i had set a jwt cookie, I came up with this issue of the Cookie not getting set, so I changed my django cookie to this

 response.set_cookie(key='jwt_token', value=token, httponly=True, samesite='None', secure=True)

This worked for now in development but there was a warning

Third-party cookie will be blocked in future Chrome versions as part of Privacy Sandbox.
Navarra answered 3/7 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.