I have a problem when testing my web app locally.
I set cookies in a request response (here is how) :
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge: maxAge, });
res.status(201).json({ user: user._id });
return res;
But I encounter an error about Same domain Policy when executing the following request in the React Client:
axios.post(`${process.env.REACT_APP_API_URL}api/user/login`, {
email: email,
password: password,
},
{
withCredentials: true
})
.then((res) => {
if (res.data.errors) {
//Show errors
}
})
.catch((err) => {
console.log(err);
});
I tried to execute it with the parameter withCredentials : false
The request works but the cookie is not stored
But I have set-Cookie in the response of my request
And this is my CORS options :
const corsOptions = {
origin: process.env.CLIENT_URL,
credentials: true,
allowedHeaders: ["sessionId", "Content-Type"],
exposedHeaders: ["sessionId"],
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
preflightContinue: false
};
I remplace my cors options by :
const corsOptions = {origin: process.env.CLIENT_URL,credentials: true};
and add :
app.options('*', cors(corsOptions));
and now it's working
Related
I'm using zendesk OAuth for authorization. I'm using the MERN stack and the current implementation works like this,
User clicks login and redirected to zendesk
once the user signs I get redirected back to /callback path
Where I sent another request to get an auth token
After I get the token I redirect the user to frontend as ?token=XXXX attached to the URL
Is this the correct way? How should I proceed with the token should I keep it in session storage? It's not a good idea to expose the token?
export const authCallback = (req: Request, res: Response): void => {
const body = {
grant_type: 'authorization_code',
code: req.query.code,
client_id: process.env.ZENDESK_CLIENT_ID,
client_secret: process.env.ZENDESK_SECRET,
}
axios
.post(`https://${process.env.SUBDOMAIN}.zendesk.com/oauth/tokens`, body, {
headers: {
'Content-Type': 'application/json',
}
})
.then((response) => {
const token = response.data.access_token
return res.redirect(`${process.env.ORIGIN}?token=${token}`)
})
.catch((err) => {
return res.status(400).send({ message: err.message })
})
}
Either use express-session and store the token on the server in req.session.token:
(response) => {
req.session.token = response.data.access_token;
req.session.save(function() {
res.redirect(`${process.env.ORIGIN}`)
});
}
Or send the token in a session cookie directly:
(response) => {
res.cookie("token", response.data.access_token, {
httpOnly: true,
secure: true,
sameSite: "None"
});
res.redirect(`${process.env.ORIGIN}`)
}
I have a login route where I want to set a cookie after I verify the login credentials. The client and the server are on different ports.
const app = express();
app.use(
cors({
credentials: true,
origin: true,
})
);
app.use(cookieParser());
app.use('/login', (req, res) => {
res.cookie('secureCookie', JSON.stringify({ id: 1 }), {
secure: false,
httpOnly: true,
});
return res.json({ success: true });
});
app.use('/check', (req, res) => {
console.log(req.cookies);
return res.json({ id: 1 });
});
The issue is that I don't see the cookie in the devtools (applications tab) after the login request returns. Also, when trying to fetch the check endpoint using credentials: 'include' it doesn't send the cookie.
What I'm doing wrong?
Here are the requests:
fetch('http://localhost:4000/login');
fetch('http://localhost:4000/check', {
credentials: 'include',
});
According to Using Fetch article on mdn
Unless fetch() is called with the credentials option set to include, fetch():
won't send cookies in cross-origin requests
won't set any cookies sent back in cross-origin responses
credentials: include must be set for requests to make them save cookies
just delete return
res.json({ success: true });
My problem:
When I go to server adress (so I'm using get method) it is working as I would want it to work, the sessionID doesn't change upon HTTP requests, but when I'm using client's fetch method to get to the server adress, the sessionID always changes and that is defect, what I don't want.
Any ideas why this is happening and how could I fix it?
Code of how my sessions are set up:
const session = require('express-session');
...
app.set("trust proxy", 1);
app.use(
session({
secret: process.env.SESSION_SECRET,
saveUninitialized: true,
resave: false,
cookie: {
secure: false,
sameSite: true,
},
})
);
...
app.get("/lobby/:id", (req, res) => {
console.log(req.sessionID);
req.session.test = 1;
});
Client's request
useEffect(() => {
fetch(getServerAdress() + "/lobby/" + code, {
method: "GET",
})
.then((response) => response.json())
.then((data) => setLoading(false))
.catch(() => setLoadingText("Failed to join the lobby"));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
As Mat J. said, fetch does not send cookies for cross-origin by default, so I had to change it:
fetch(getServerAdress() + "/lobby/" + code, {
method: "GET",
credentials: "include",
}
Also I had to enable credentials and origin for CORS on my server:
const cors = require("cors");
app.use(cors({ credentials: true, origin: true }));
I am learning and applying authentication for my blog website!
I am using express-session to handle logins. Cookie on the browser & server sessions works fine.
However, I am having trouble retrieving cookies on the server-side express app. I tried the following:
With cookie-parser, req.cookies & req.signedCookies both returns [Object: null prototype].
Setting CORS
req.cookie & req.header.cookie returns undefined
I can see a "Cookie" header from my connection in the browser network tab.
My code / settings are as follows:
function auth (req, res, next) {
// Problem: Cannot read browser cookie of HTTP requests.
console.log('Based on browser', req.cookie, req.cookies, req.signedCookies);
next();
}
router.get('/', auth, async (req, res) => { // ... }
Middlewares
app.use(cors({
origin: ['http://localhost:3000'],
credentials: true
}));
app.use(cookieParser()) // Also tried with secret option.
app.use(session({
secret: 'top-secret',
resave: true,
rolling: true,
saveUninitialized: false,
store: store, // this is working
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 14,
httpOnly: true,
secure: process.env.NODE_ENV !== 'Development',
sameSite: process.env.NODE_ENV === 'Development' ? 'lax' : 'none'
}
}))
Thank you in advance :)
Edit 1: My fetch code:
If your using http only you should consider 2 things:
Step1 while request in client side:
you should send request like this:
const req = await fetch("http://localhost:7000/api/auth/login", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
body: JSON.stringify({
email: formData.get("email"),
password: formData.get("password"),
}),
});
const data = await req.json();
step 2 in express:
const allowedOrigins = ["http://localhost:8000"];
const corsOptions = {
origin: function (origin, callback) {
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
var msg =
"The CORS policy for this site does not " +
"allow access from the specified Origin.";
callback(new Error(msg), false);
}
},
optionsSuccessStatus: 200,
credentials: true,
};
app.use(cors(corsOptions));
now you can get coockies in express by using req.cookies.nameOfCookiesWhichYouSendThroughCoockieParser
I'm using axios (React) + Express-js on Node-js
In order to get the cookie from the server:
Simply set withCredentials: true in the axios request, you can use this config example:
const config = {
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
};
In order to get this cookie from the client:
You also need to set withCredentials: true in the axios request,
And you need to install cookie-parser library on the server:
npm i cookie-parser
Import this library:
const cookieParser = require("cookie-parser");
And use the cookieParser middleware:
app.use(cookieParser());
And finally, req.cookies should return the list of your cookies.
I'm trying to make my nodejs app to communicate with HAPROXY via https. The idea is that nodejs sends message to haproxy via https, haproxy routes message forward.
I used request.js library and all worked fine, but now I need to perform this task without any libraries. The scenario is following. If environment variable is 1, I should use HTTP, in other cases -HTTPS. The problem is that when I use https and haproxy, I get "Socket hangup error", but everything works fine with request.js. Here is my code.
const protocol = process.env.NODE_ENV === 1 ? require('http') : require('https');
then I configure HTTPS
this.api = url.parse(app.get('API_HAPROXY'));
this.options = {
port: this.api.port,
hostname: this.api.hostname,
path: '/api/report',
method: 'POST',
headers: {
"Content-Type": "application/json",
},
rejectUnauthorized: false,
requestCert: true,
agent: false
};
Because I don't want to use ca to validate ssh keys I use NODE_TLS_REJECT_UNAUTHORIZED=0
reportData(json) {
const req = protocol.request(this.options, (res) => {
res.on('error', (err) => {
this.logger.error(`Failed to report ${err.message}`)
})
});
req.write(JSON.stringify(json));
req.end();
req.on('error', (err) => {
this.logger.error(`Failed to report ${err.message}`);
});
}
In this case I get socket hangup error while using HTTPS
Here is my request configuration
request({
uri: `${this.api}/api/report`,
method: 'POST',
json,
}, (err, response) => {
if (err || response.statusCode !== 200) {
this.logger.error(`Failed to report : ${err ? err.message : response.statusCode}`);
} else {
this.logger.info(`Report was sent`);
}
});
The issue was fixed by adding content-length header to the options.headers.
this.api = url.parse(app.get('API_HAPROXY')); this.options = {
port: this.api.port,
hostname: this.api.hostname,
path: '/api/report',
method: 'POST',
headers: {
"Content-Type": "application/json",
"Content-Length: <calculated length of the object you want to send in bytes >
},
rejectUnauthorized: false,
requestCert: true,
agent: false
};