How to set a same domain cookie with Heroku subdomains? - javascript

I have my front end running on one Heroku instance: fe.herokuapp.com
And my back end running on another instance: be.herokuapp.com
I want to set a same domain cookie when a user logs in from the front end.
I am using Koa cookies module to set the cookie like so:
cookies.set("accessToken", token, {
maxAge: 1000 * 60 * 24,
signed: true,
secure: process.env.NODE_ENV === "production",
httpOnly: true,
domain: process.env.ORIGIN_HOSTNAME || "localhost"
})
If it helps, I'm using a React front end and a Node back end (using Koa).
Via Postman, my back end returns the following set-cookie header:
accessToken=<access_token>; path=/; expires=Sun, 01 Sep 2019 16:27:24 GMT; domain=.herokuapp.com; secure; httponly
However, via my React app, I can't see any set-cookie headers.
My front end is using isomorphic-unfetch library with credentials = "include". (perhaps this needs to be same-origin since it's on the same subdomain?)
My two questions are:
Why can't I set the domain value in my cookie from the back end to be fe.herokuapp.com?
Why can I see the set-cookie header via postman but not in my front end React app?
Happy to post more code snippets if need be.

herokuapp.app is listed in Public suffix List which means cookies are blocked from bein set to the domain "herokuapp.com"
you must use custom domain technique

Stuck with this issue for some time. What I figured out:
Need add proxy attribute to app:
const app = new Koa()
app.proxy = true
Extend cookies options with sameSite attribute:
cookies.set("accessToken", token, {
maxAge: 1000 * 60 * 24,
signed: true,
secure: process.env.NODE_ENV === "production",
httpOnly: true,
domain: process.env.ORIGIN_HOSTNAME || "localhost",
sameSite: 'none' // <-- add this
})
Before that I bought a domain for my app. Frontend app point to "domain.com", and Backend app point to "api.domain.com". But now I am not sure if it was necessary.

Related

Way to get access to cookies with axios request in chrome application tab

I'm developing f/e page and b/e server.
f/e : react, localhost:3000
b/e : expresses, localhost:80
and I also hosted both server with different domain.
I have send a request using axis to the b/e server to get authorization cookie.
I received set-cookie header but i can't see it on my application tab.
I have searched a lot of posts.
They said that I should add an withCredentials option to axios config.
And also I need to add credentials option to b/e CORS setting.
So I fixed all of them.
But still I cannot get access to the cookies.
By the way, I cannot see or get access to the cookie. But if I send a request to B/E server, the cookies are sent correctly.
I have this issue for months.
Please help me out experts.
And please let me know if need more information.
here is my codes
// expressjs
app.use(cors({
origin: true,
credentials: true,
}));
...
res.cookie('SID', token, {
httpOnly: true, secure: true, sameSite: 'None'
});
res.cookie('SSID', token, {
httpOnly: false, secure: true, sameSite: 'None', path: '/'
});
...
I did it with/without defaults option and also with/without inline option
//react axios
...
axios.defaults.withCredentials = true;
...
const {status, data} = await axios.post(`${url}/api/blah`, {
something
}, {withCredentials: 'include'});
Response cookie
Chrome application cookie
I believe the right syntax for withCredentials on axios is:
//react axios
//...
axios.defaults.withCredentials = true;
//...
const {status, data} = await axios.post(`${url}/api/blah`, {
something
}, {withCredentials: true}); // instead of 'include'
I remember having an issue where axios.defaults.withCredentiasl = true solved it, but you probably don't want to keep it like that forever, so I'd suggest that you set it back to false after the call is finished. Also, the issue looks fixed on axios repo, so I'd try to solve it without changing the defauls.

How to set cookies with FastAPI for cross-origin requests

I have an application based FastAPI Which serves as the backend for a website And currently deployed on a server with an external IP. The frontend is situated at another developer, temporarily in local hosting.
At the beginning of the work we encountered a CORS problem, which was solved by using the following code I found on the Internet:
from fastapi.middleware.cors import CORSMiddleware
...
app.add_middleware(
CORSMiddleware,
allow_origins=['http://localhost:3000'],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
The addition allowed Frontend to make requests properly, but for some reason, cookies that are set to send (and work properly in the Swagger UI) are not set in Frontend.
The client side look like:
axios({
method: 'POST',
baseURL: 'http://urlbase.com:8000',
url: '/login',
params: {
mail: 'zzz#zzz.com',
password: 'xxxxxx'
},
withCredentials: true
}).then( res => console.log(res.data) )
.catch( err => console.log(err))
Setting and reading cookies in FastAPI can be done through the use of the Request class:
Setting the cookie refresh_token
from fastapi import Response
#app.get('/set')
async def setting(response: Response):
response.set_cookie(key='refresh_token', value='helloworld', httponly=True)
return True
Setting httponly=True makes sure the cookie can't be accessed by JS. This is great for sensitive data such as a refresh token. But if your data isn't that sensitive then you can just omit it.
Reading the cookie
from fastapi import Cookie
#app.get('/read')
async def reading(refresh_token: Optional[str] = Cookie(None)):
return refresh_token
You can find more information on using cookies as parameters on the FastAPI docs here.
Remove the wildcards since wildcarding is not allowed with allow_credentials=True :
app.add_middleware(
CORSMiddleware,
allow_origins=['http://localhost:3000'],
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:
# `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!
For Cross-Origin Situation Cookie Setting, Check things below 👇🏻
Pre-requirements
Your FE, BE servers need to talk each other with https protocol. (Set SSL Certificates using let's encrypt or other services)
Make sure your domain doesn't include port
Backend
Server Setup
Add FE Domain to allow_origins
Set allow_credentials True
allowed_methods should not be a wildcard("*")
allowed_headers should not be a wildcard("*")
Cookie Setup
secure = True
httponly = True
samesite = 'none'
List item
Fastapi Example
# main.py
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "HEAD", "OPTIONS"],
allow_headers=["Access-Control-Allow-Headers", 'Content-Type', 'Authorization', 'Access-Control-Allow-Origin'],
)
# cookie
response = JSONResponse(content={"message": "OK"})
expires = datetime.datetime.utcnow() + datetime.timedelta(days=30)
response.set_cookie(
key="access_token", value=token["access_token"], secure=True, httponly=True, samesite='none', expires=expires.strftime("%a, %d %b %Y %H:%M:%S GMT"), domain='.<YOUR DOMAIN>'
)
Frontend
Include header "Access-Control-Allow-Origin": ""
Set withCredentials: true
Axios Example
// post request that sets cookie
const response = await axios.post(
"https://<Target Backend API>",
{
param1: "123",
param2: "456",
},
{
headers: {
"Access-Control-Allow-Origin": "https://<FE DOMAIN>",
},
withCredentials: true,
},
);
Reverse Proxy Server (If you have)
Allow "OPTIONS" method (this is need when browser check server options in preflight request)
Check if any middlewares blocks your preflight requests. (e.g. Nginx Basic HTTP Authentications can block your request)
IMPORTANT
if your FE use subdomain like dev.exmaple.com and your BE also use subdomain like api.example.com, you should set cookie domain to .example.com so the subdomain services can access root domain cookie!!
In FastAPI you can set cookies via response.set_cookie,
from fastapi import FastAPI, Response
app = FastAPI()
#app.post("/cookie-and-object/")
def create_cookie(response: Response):
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return {"message": "Come to the dark side, we have cookies"}
It should be noted though that these are NOT SECURE SESSIONS you should use something like itsdangerous to create encrypted sessions.
In response to the requests not seeming to not be sent; You should make sure the option for which urls the cookies are valid for are being set.
By default they are typically / which means everything however your system might be setting them to a specific case with CORS setup.

Prevent url encode in response set cookie - nodejs

In my node application, I am trying to change my cookie value with the below code. But when I set some cookie values I see it modified in my response header of browser.
Node code :
let nonEncodedString ='s%3A9Q8kumq4BgrHtJPM90ebhhl6OqChsxdp.x0uf93Hk5I03KWeF%2FFT3TM64riv3QAs'
res.cookie('connect.sid', nonEncodedString , { maxAge, httpOnly: true, overwrite: true });
But the header I get is
set-cookie: connect.sid=s%253A9Q8kumq4BgrHtJPM90ebhhl6OqChsxdp.x0uf93Hk5I03KWeF%252FFT3TM64riv3QAs; Max-Age=157680000; Path=/; Expires=Thu, 31 Jul 2025 11:28:35 GMT; HttpOnly
essentially
s%3A9Q8kumq4BgrHtJPM90ebhhl6OqChsxdp.x0uf93Hk5I03KWeF%2FFT3TM64riv3QAs
is changed to
s%253A9Q8kumq4BgrHtJPM90ebhhl6OqChsxdp.x0uf93Hk5I03KWeF%252FFT3TM64riv3QAs. ie. '25' is being added.
I think it's happening because it is getting URL encoded.
I don't want that to happen since its changing the value I am sending and I don't have control to parse it before the browser sets it in the cookie.
you should set an encode function:
res.cookie('connect.sid', nonEncodedString,
{ maxAge,
httpOnly: true,
overwrite: true,
encode: v => v
})
the default encode function is encodeURLComponent.

Express not setting Cookie

I have 2 servers. One hosting a next.js application on localhost:5555 and another hosting an express server for the api on localhost:4444.
The authentication api returns a cookie however this is not being set in the browser running on localhost:5555.
res.cookie('cokkieName', 'bob', {
domain: '127.0.0.1:5555',
maxAge: 900000,
httpOnly: true,
});
res.status(200).json({
session: jwtSigned,
role: 'default',
});
My cors setup is:
const options: cors.CorsOptions = {
allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'X-Access-Token', 'Authorization'],
credentials: true,
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
origin: 'http://localhost:5555',
preflightContinue: false,
};
I'd prefer to set the cookie with the api rather than on the via next.js.
I've tried alternating the cors settings but have had no success. The client call uses axios and has withCredentials set.
As mentioned in the other answer, you cannot set cookie on a different domain, as it can be a security risk.
But from your question...
Since the API server is running the authentication logic, the cookie should be set on the API server domain, NOT on client domain
So in your API server, you can change code to
res.cookie('cokkieName', 'bob', {
domain: '127.0.0.1:4444',
maxAge: 900000,
httpOnly: true,
});
or just remove the domain as it defaults to server domain.
res.cookie('cokkieName', 'bob', {
maxAge: 900000,
httpOnly: true,
});
As you have a client running on localhost:5555 and API server running on localhost:4444, it's a cross domain call and you need to pass withCredentials option to axios to pass the cookie. You might not be able to see the cookie in browser, as it can be a security risk, but can echo the cookie value on server console.
axios.get("url",{
withCredentials: true
}).then(res => {
console.log(res);
});
Note that credentials option work only if Access-Control-Allow-Origin header is not a wildcard like *
I have a created a server to mimic the code you posted. The server basically echoes the request cookie back.
It can be accessed through this stackblitz. You can toggle the withCredentials flag to see the difference in response
You can not set a cookie for other domains, Because that would be an enormous security risk.
But if you are looking for a way to share a cookie between your applications, the only way you have is to share a single domain between these applications, either separate them by url prefix or create one of them as a subdomain of other.
domain.com and api.domain.com. This way by specifying domain.com in your cookie both domains have access to it
If you concern about developing environment of yours, you can use Nginx and proxy_pass these two applications in single domain
What you are trying to do is definitely possible, the problem is that cookies are not port specific: Are HTTP cookies port specific? so you should specify a domain name but no port.
If you set a Cookie on its own domain (or if you don't specify it at all), all of the subsequent requests will carry the cookie even if initiated by a different port (only when withCredentials set to true, though). Also, note that, if set, the domain of the Cookie must match exactly the current domain, or parent domain (localhost is considered as different than 127.0.0.1)
Note: doing so shows the following warning in Chrome:
A cookie associated with a cross-site resource at http://example.com/
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.
So, prepare your code for the future web standards!
I had a similar problem with nuxt.js. To set cookies in your browser using express, I suggest using the library Cookies
In my case I set cookies in express like this:
setResponseCookie(req, res, token, strategyName) {
const isHttps = req.protocol === 'https';
let { cookie: { maxAge } } = config;
const cookieSettings = isHttps => ({
maxAge,
httpOnly: true,
secure: isHttps,
});
const cookies = new Cookies(req, res);
cookies.set('auth.strategy', strategyName, cookieSettings(isHttps))
cookies.set('auth.token', token, cookieSettings(isHttps))
}
and then in express route you can add to this like:
let token = sign(user);
this.setResponseCookie(req, res, token, 'local-login')
res.json({ token, status: 'success' });
or
let token = sign(req.user);
this.setResponseCookie(req, res, token, 'google')
res.redirect(config.client.url);
I managed to fix this issue of the server not setting the browser cookies by setting my res.cookie like the following
res.cookie("Test", "If you see this then it works", {httpOnly: false, secure: true, sameSite: "none"}).json({message: "Finished"});
I checked to see if the cookie was set by opening up the chrome dev tools, going to the application, then cookies, then the site URL.
There are a few things I would like to preference about the above code. This method only works on a URL starting with https, this means it will not fix the problem if on a URL like http://localhost:3000. I don't know if it matters if httpOnly is true or false. Also, What is necessary when setting the cookie this way is to pass in the properties secure: true, sameSite: "none". Passing these values into res.cookie was what finial fixed the issue for me. I have not seen this solution posted anywhere and decided I out to let you know.
My code that fetches the data from the API and triggers the server to set the browser cookie looks like this for anyone who is interested.
fetch(domain.com/testSetCookie', {
method: 'get',
mode: "cors",
credentials: 'include' //<-- important
})
.then((res) => {
//console.log(res);
return; //res.json();
})
.then((data) => {
console.log(data);));
})
.catch((err) => {
console.log("an error happeoned");
console.log(err);
});

jQuery cookie not being passed from HTTPS page to another HTTPS page

I am trying to send a cookie from one HTTPS page to another HTTPS page with jQuery cookies.
I set the cookie like so one page 1:
$.cookie('name', variable, { expires: 300 , secure:true});
And then on the next page, I try to get it like so:
console.log( $.cookie('name') );
No dice... is what I am trying to do illegal or immoral in some way?
If it helps, the pages are:
Page 1
Page 2 can be reached by clicking on any of the "Try it Free" buttons.
You can set cookie with domain path:
$.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
You can read that here:
JQuery Cookie values not maintained while moving from http to https
you can set and get cookie by javascript as well that works fine on https server
set cookie:
document.cookie="username=John Doe";
get cookie:
var x = document.cookie;
delete cookie:
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
you can get help from:
http://www.w3schools.com/js/js_cookies.asp

Categories