I am currently trying to set up a Node/Express app with a React client to interact with it. I setup passport to handle authentication with JWT. When the user logs in, I validate the email/password. I then set the cookie:
res.cookie('jwt', token, { httpOnly: true, secure: false });
I see the token being passed back in the response header, but when I inspect my Chrome browser's cookie under Developer Tools > Application > Cookies, I see an empty cookie. What am I doing wrong and how do I send the jwt in the response header with subsequent requests?
server/App.js
const app = express()
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.post('/login', (req, res) => {
passport.authenticate('local', { session: false }, (error, user) => {
if (error || !user) {
res.status(400).json({ error });
}
// Construct JWT payload
const payload = {
email: user.email,
expires: Date.now() + parseInt(process.env.JWT_EXPIRATION_MS),
};
// Assign payload to req.user
req.login(payload, {session: false}, (error) => {
if (error) {
res.status(400).send({ error });
}
// Generate a signed JWT
const token = jwt.sign(JSON.stringify(payload), process.env.JWT_SECRET);
// Assign JWT to cookie
res.cookie('jwt', token, { httpOnly: true, secure: false });
res.status(200).send({ email: user.email });
});
})(req, res);
});
client/LoginModal.js
handleLogin = async () => {
const { name, email, password } = this.state
try{
const res = await axios.post('http://localhost:8080/login', {
email: email,
password: password,
})
if(res.status == 200){
console.log("Logged in")
console.log(res)
}
} catch (err) {
console.log(err)
}
}
Edit: My current workaround is to send the token as part of the payload. My react client then grabs the token from the payload and stores it in the browser's cookie. Is there a way to avoid this workaround (see below for example)?
server
res.status(200).send({ email: user.email, jwt: token });
client
if(res.status == 200){
cookies.set('jwt', res.data.jwt)
cookies.set('email', res.data.email)
}
When making the axis.post() call, you'll have to pass {withCredentials: true, credentials: 'include'} as your second argument, only this way will your browser set the cookies.
You have the cookie set with the httpOnly flag enabled. Most modern browsers restrict read access to such cookies through developer tools. You can read more about it here.
If you'd like to see the contents of the cookie in your development environment, set httpOnly to false.
This solution I found works with both local development and production ( and alos LAN access, eg. when you access the website on your LAN IP address such as http://192.168.xxx.xxx:<port>):
// Set CORS options
const cors = require(`cors`)
const whitelist = [
'http://localhost:<application port>', // not https
'https://yourprod.ip.address.com' // must be https!
'http://<your local IP>:<port>', // optional, LAN access
// ...
]
const corsOptions = {
credentials: true,
origin: (origin, callback) => {
// `!origin` allows server-to-server requests (ie, localhost requests)
if(!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error("Not allowed by CORS: "+ origin))
}
},
optionsSuccessStatus: 200
}
app.use(cors(corsOptions))
Then on the authentication endpoint:
// Set Cookie
const cookieContent = 'this is a cookie'
const cookieOptions = {
httpOnly: true, // safety, does not allow cookie to be read in the frontend javascript
maxAge: 24*3600*1, // cookie age in seconds
sameSite: 'Strict' // works for local development
}
if(process.env.NODE_ENV === 'production') {
// these options work on a https server
cookieOptions.secure = true
cookieOptions.sameSite= 'None'
}
res.cookie(
'cookie-tag',
refreshToken,
cookieOptions
)
res.json(cookieContent)
What worked for me is setting app.use(cors({ origin: true, credentials: true })) in cors package. Also setting withCredentials: true, credentials: 'include' while fetching from backend
Related
I know this question has been asked a 1000 times on Stack Overflow but none of the solutions seem to fit mine. I've also been through a lot of GitHub repo's but I just can't seem to find the solution. Also this is my first experience with developing a back-end.
The problem
I created a backend with authentication including refresh token and refresh token rotation. When I try to log in / sign up / request a request token in Insomnia I don't have any issues at all. Within MongoDB my refreshToken gets updated, I retrieve a new access and refreshtoken after calling the /refresh endpoint. However when I try to do call the refresh endpoint from the browser, I get an error "401 unauthorized" on the first step because it does not see the cookie. When trying to log the cookie I get [Object: null prototype] {}.
I know I am in development so what I've already tried:
Set withCredentials: true for the axios calls
Added credentials: true and origin: true to the cors config
Setting secure: false for res.cookie()
When I try this from my React front-end I do bump into some issues.
- Login endpoint properly sends the accessToken in the json and the refreshToken as a HTTPOnly Cookie
- Response from backend when calling the refresh endpoint
When I try to console.log the req.cookies I get [Object: null prototype] {}.
Here are some parts of my code:
Server.js
require("dotenv").config({ path: "./.env" });
const express = require("express");
const app = express();
const connectDB = require("../server/configs/db");
const errorHandlerMiddleware = require("../server/middleware/error-handler");
const mongoose = require("mongoose");
const cors = require("cors");
const path = require("path");
const PORT = process.env.PORT || 5000;
const verifyJWT = require("./middleware/verifyJWT");
const cookieParser = require("cookie-parser");
//Connect Database
connectDB();
const db = mongoose.connection;
// Middleware
app.use(cors({ credentials: true, origin: true }));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(errorHandlerMiddleware);
//Serve public folder
app.use("/", express.static(path.join(__dirname, "/public")));
//Routes
app.use("/api/users", require("./routes/authRoutes"));
app.use("/api/logout", require("./routes/logoutRoutes"));
//Protected routes
app.use(verifyJWT);
app.use("/api/habitcards", require("./routes/habitcardRoutes"));
db.once("open", () => {
console.log("Connected to database");
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
});
// Log errors on occurence
db.on("error", (err) => {
console.log("Error connecting to database", err);
});
The refresh controller
//Get the refresh token from the request
const cookies = req.cookies;
const refreshToken = cookies.jwt;
console.log(cookies);
console.log("step");
//Check if the refresh token is present
if (!refreshToken) {
return res.status(401).json({ message: "Unauthorized" });
}
//Remove the old refresh token from the client
res.clearCookie("jwt", { httpOnly: true, secure: false, sameSite: "none" });
//Check if the user exists
const user = await User.findOne({ refreshToken }).select("-password").exec();
//If the user does not exist then the refreshToken does not exist anymore
// Check to which user the refreshToken (Refresh token container user ID) and delete all refresh tokens for that user
if (!user) {
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
async (err, user) => {
//Return Forbidden when token is invalid
console.log("hoi");
if (err) return res.status(403).json({ message: "Forbidden" });
//Delete all refresh tokens for that user if token is valid (Maybe warn the user?)
const hackedUser = await User.findById(decoded.userId);
hackedUser.refreshToken = [];
await hackedUser.save();
}
);
return res.sendStatus(403);
}
//Make sure the new refreshtoken is not the same as the old one
const newRefreshTokenArray = user.refreshToken.filter(
(rt) => rt !== refreshToken
);
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
asyncHandler(async (err, decoded) => {
if (err) {
user.refreshToken = [...newRefreshTokenArray];
const result = await user.save();
}
if (err || user.email !== decoded.email)
return res.status(403).json({ msg: "Forbidden" }); // Forbidden
//Refresh token was still valid
const roles = Object.values(user.roles);
//Create a new accessToken and refreshToken and return to the user
const accessToken = jwt.sign(
{ userInfo: { userId: user.id, roles: roles } },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "30s",
}
);
const newRefreshToken = jwt.sign(
{ userId: user.id, email: user.email },
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: "1d",
}
);
//Save the new refresh token to the user
user.refreshToken = [...newRefreshTokenArray, newRefreshToken];
const result = await user.save();
res.cookie("jwt", newRefreshToken, {
httpOnly: true,
secure: false,
sameSite: "none",
maxAge: 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
})
);
};
The Refresh call from the Front-End (Not complete since I was trying to debug and just make a simple call)
const useAxios = axios.create({
baseURL: "http://localhost:5000/api/users",
withCredentials: true,
crossDomain: true,
});
const refresh = async () => {
try {
const { data } = await useAxios.get("/refresh");
console.log(data);
} catch (error) {
console.log(error);
}
};
What can I try to solve this issue?
Latest request made to the back-end:
The response that gets sent by the signin endpoint
Thanks to #jub0bs I was able to solve this problem.
I was not correctly setting the cookies because when signin in my axios post call did not have the qithCredentials parameter. I was in the misunderstanding that the withCredentials was only used for sending credentials and not for receiving.
Solution:
Add the axios parameter { withCredentials: true } to post requests.
const signin = async (formValues) => {
const { data } = await axios.post(
"http://localhost:5000/api/users/signin",
{ ...formValues },
{ withCredentials: true }
);
};
I'm using the passport.js local strategy.
I was testing it under proxy setting, localhost.
Things worked fine until I prepare to deploy.
I changed the API address to include dotenv and set CORS settings on the server-side.
When trying to login, CORS works fine, OPTIONS and the POST get 200 ok. The client receives the success data. cookie saved in client.
But when auth checking process runs right after Redux "isLoggedin" state is updated(useEffect), req.session doesn't
t have the passport object. So deserializeUser not be called. The session contains other cookie info except for Passport.
This one is only on Firefox(not Chrome): Page will be redirected if the login auth succeeded(it checks right after login redux state changed), but since it's failed, the user stays on the login page still. But if I try to login on the same page again, the cookie start to show the passport object.(in other words, it shows from 2nd attempt). But it doesn't persist because the Redux login state has been changed to true at the first login attempt already.(so Auth checking doesn't occur.)
client:
Axios.post(
`${process.env.REACT_APP_API_URI}/api/users/login`,
loginData,
{ withCredentials: true, }
).then((res) => res.data){
//save isLoggedin to true in Redux
}
// auth check logic starts right after changing isLoggedin. Axios Get to authenticateCheck
server.js
app.use(helmet());
// app.use(express.static('public'));
app.use("/uploads", express.static("uploads"));
// Passport configuration.
require("./utils/passport");
// connect to mongoDB
mongoose
.connect(db.mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => console.log("mongoDB is connected."))
.catch((err) => console.log(err));
// CORS Middleware
const corsOptions = {
origin: "http://localhost:8000",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
credentials: true,
methods: ["POST", "GET", "DELETE", "PUT", "PATCH", "OPTIONS"],
allowedHeaders:
"Origin, X-Requested-With, X-AUTHENTICATION, X-IP, Content-Type, Accept, x-access-token",
};
// app.use(cors(corsOptions));
app.options(/\.*/, cors(corsOptions), function (req, res) {
return res.sendStatus(200);
});
app.all("*", cors(corsOptions), function (req, res, next) {
next();
});
// to get json data
// support parsing of application/json type post data
app.use(express.json());
app.use((req, res, next) => {
req.requestTime = new Date().toISOString();
next();
});
//support parsing of application/x-www-form-urlencoded post data
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
// db session store
const sessionStore = new MongoStore({
mongoUrl: db.mongoURI,
collection: "sessions",
});
// tell app to use cookie
app.use(
session({
secret: process.env.SESSION_SECRET_KEY,
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
httpOnly: true,
secure: false,
sameSite:"none",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
//keys: [process.env.COOKIE_ENCRYPTION_KEY]
},
name: "pm-user",
})
);
// tell passport to make use of cookies to handle authentication
app.use(passport.initialize());
app.use(passport.session());
app.use(compression());
app.use(flash());
app.use((req, res, next) => {
console.log("req.session:", req.session);
// console.log('/////// req: ///////', req);
console.log("////// req.user: ", req.user, " //////");
next();
});
//---------------- END OF MIDDLEWARE ---------------------//
authController:
exports.authenticateCheck = (req, res, next) => {
console.log("req.isAuthenticated():", req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
} else {
return res.json({
isAuth: false,
error: true,
});
}
};
It would be a really big help if you can advise me where to look to fix it.
Thanks.
I found the solution finally.
It was because the session was renewed every time when a new request starts other than a login request.
The solution was, I had to add { withCredentials: true } to every Axios option in my frontend.
I have a express server running on localhost:5000 and client running on port 3000.
After sending post request from client to login using fetch API browser is not setting cookie. I'm getting cookie in response header as 'set-cookie' but it isn't set in the browser at client side.
Here is my fetch request code:
return (
fetch(baseUrl + "users/login", {
method: "POST",
body: JSON.stringify(User),
headers: {
"Content-Type": "application/json",
},
credentials: "same-origin",
})
Server side code:
router
.route("/login")
.options(cors.corsWithOptions, (req, res) => {
res.sendStatus(200);
})
.post(cors.corsWithOptions, (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
if (err) return next(err);
if (!user) {
res.statusCode = 401;
res.setHeader("Content-Type", "application/json");
res.json({ success: false, status: "Log In unsuccessful", err: info });
}
req.logIn(user, (err) => {
if (err) {
res.statusCode = 401;
res.setHeader("Content-Type", "application/json");
// res.header("Access-Control-Allow-Credentials", true);
res.json({
success: false,
status: "Login Unsuccessful!",
err: "Could not log in user!",
});
}
var token = authenticate.getToken({ _id: req.user._id });
res.cookie("jwt-token", token, {
signed: true,
path: "/",
httpOnly: true,
});
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.json({ success: true, status: "Login Successful!", token: token });
});
})(req, res, next);
});
How to handle Server-side session using Express-session
Firstly you will need the following packages
npm i express-session connect-mongodb-session or yarn add express-session connect-mongodb-session
Now that we have packages that we need to setup our mongoStore and express-session middleware:
//Code in server.js/index.js (Depending on your server entry point)
import expressSession from "express-session";
import MongoDBStore from "connect-mongodb-session";
import cors from "cors";
const mongoStore = MongoDBStore(expressSession);
const store = new mongoStore({
collection: "userSessions",
uri: process.env.mongoURI,
expires: 1000,
});
app.use(
expressSession({
name: "SESS_NAME",
secret: "SESS_SECRET",
store: store,
saveUninitialized: false,
resave: false,
cookie: {
sameSite: false,
secure: process.env.NODE_ENV === "production",
maxAge: 1000,
httpOnly: true,
},
})
);
Now the session middleware is ready but now you have to setup cors to accept your ReactApp so to pass down the cookie and have it set in there by server
//Still you index.js/server.js (Server entry point)
app.use(
cors({
origin: "http://localhost:3000",
methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
credentials: true,
})
);
Now our middlewares are all setup now lets look at your login route
router.post('/api/login', (req, res)=>{
//Do all your logic and now below is how you would send down the cooki
//Note that "user" is the retrieved user when you were validating in logic
// So now you want to add user info to cookie so to validate in future
const sessionUser = {
id: user._id,
username: user.username,
email: user.email,
};
//Saving the info req session and this will automatically save in your mongoDB as configured up in sever.js(Server entry point)
request.session.user = sessionUser;
//Now we send down the session cookie to client
response.send(request.session.sessionID);
})
Now our server is ready but now we have to fix how we make request in client so that this flow can work 100%:
Code below: React App/ whatever fron-tend that your using where you handling logging in
//So you will have all your form logic and validation and below
//You will have a function that will send request to server
const login = () => {
const data = new FormData();
data.append("username", username);
data.append("password", password);
axios.post("http://localhost:5000/api/user-login", data, {
withCredentials: true, // Now this is was the missing piece in the client side
});
};
Now with all this you have now server sessions cookies as httpOnly
Problem:
Trying to set the cookie on login using express-session, but think I'm missing something obvious. The response to the login POST request includes Set-Cookie. I've also set the Access-Control-Allow-Origin and Access-Control-Allow-Headers to wildcards as shown here:
https://i.stack.imgur.com/XS0Zv.png
But we see that in the browser storage (tried with Firefox and Chrome) there is nothing. As shown here
I'm currently setting my express-session as follows (refer to end of post for full code. Adding snippet for easier read):
app.use(session({
genid: () => { return uuidv4(); },
store: new MongoStore({ mongooseConnection: mongoose.connection }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: false,
sameSite: true,
}
})
);
Then after I've verified the user is getting logged in, I try to set the userId via:
req.session.userId = user.id;
Possibly Relevant Info
These sessions are successfully getting stored in Mongo as you can see here, which makes me believe that I'm at least generating the sessions correctly. Now I could be totally wrong here...
my backend is running on localhost:8000 via: app.listen(8000);
my client is running on http://localhost:3000/
trying not to use Apollo GraphQL for learning purposes
Things I've tried so far:
different combinatons of resave, saveUnitialized.
remove the cookie parameter.
stop setting userId
restarting browser and servers
Looked at relevant stack overflow posts
Please advise! Even ideas on how to debug this or what other things I can look at would be immensely helpful!
Relevant Code
app.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const {v4: uuidv4} = require('uuid');
const graphqlSchema = require('./graphql/schema/index');
const graphqlResolvers = require('./graphql/resolvers/index');
const app = express();
const path = '/graphql';
app.use(bodyParser.json());
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', '*');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
mongoose
.connect(`mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}#cluster0.ccz92.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`,
{ useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }
)
.then(() => {
app.use(session({
genid: () => { return uuidv4(); },
store: new MongoStore({ mongooseConnection: mongoose.connection }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: false,
sameSite: true,
}
})
);
app.use(path, graphqlHTTP({
schema: graphqlSchema,
rootValue: graphqlResolvers,
graphiql: true,
}));
app.listen(8000);
})
.catch(err => {
console.log(err);
});
graphql/resolvers/auth.js
const argon2 = require('argon2');
const jwt = require('jsonwebtoken');
const User = require('../../models/user');
module.exports = {
createUser: async args => {
try {
const existingUser = await User.findOne({
email: args.userInput.email
});
if (existingUser) {
throw new Error('User exists already.');
}
const hashedPassword = await argon2.hash(
args.userInput.password,
12
);
const user = new User({
email: args.userInput.email,
password: hashedPassword,
loggedIn: true
});
const result = await user.save();
const token = jwt.sign(
{ userId: result.id, email: result.email },
process.env.JWT_KEY,
{ expiresIn: '1h' }
);
return {
userId: result.id,
token: token,
tokenExpiration: 1
};
} catch (err) {
console.log("error in resolvers/auth.js");
throw err;
}
},
login: async (args, req) => {
const { userId } = req.session;
if (userId) {
console.log("found req.session");
return User.findOne({ _id: userId });
}
console.log("looking for user with ", args.userInput.email);
const user = await User.findOne({ email: args.userInput.email });
console.log("found user");
if (!user) {
throw new Error("User does not exist!");
}
user.loggedIn = true;
user.save();
const isEqual = await argon2.verify(user.password, args.userInput.password);
if (!isEqual) {
throw new Error ("Password is incorrect!");
}
console.log("setting session.userId");
req.session.userId = user.id;
return { ...user._doc, password: null};
},
logout: async (args, req) => {
if (!req.isAuth) {
throw new Error('Unauthenticated');
}
try {
const result = await User.findOneAndUpdate(
{ _id: req.userId },
{ loggedIn: false },
{ new: true },
);
return { ...result._doc, password: null };
} catch (err) {
console.log("logout error", err);
throw(err);
}
},
};
So it turned out to be a CORS issue. I didn't realize that the port would mean a different origin. In this case my client is at 3000 and my server is at 8000.
Given the CORS nature, in the client I need to include credentials (cookies, authorization headers, or TLS client certificates) when I'm fetching:
fetch(config.url.API_URL, {
method: 'POST',
body: JSON.stringify(requestBody),
headers: {
'Content-Type': 'application/json'
},
credentials: "include",
})
This will tell the user agent to always send cookies.
Then serverside I need to set Access-Control-Allow-Credentials to be true as such:
res.setHeader('Access-Control-Allow-Credentials', true);
This will allow the browser to expose the response (which has the cookie) to the frontend Javascript code.
Since we are using credentials, we will need to specify Access-Control-Allow-Headers and Access-Control-Allow-Origin
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
I am building a login system using express for node.js and react.js. In my back-end when a user logs in, it creates a cookie. When I go to Network > Login I can see this:
Set-Cookie:
user_id=s%3A1.E%2FWVGXrIgyXaM4crLOoxO%2Fur0tdjeN6ldABcYOgpOPk; Path=/; HttpOnly; Secure
But when I go to Application > Cookies > http://localhost:3000, there is nothing there. I believe that is because I am not allowing credentials to go through correctly when I do a post request from the client side. How do I go about this? Please, let me know if I can improve my question in any way.
//Login back-end
router.post('/login', (req, res, next) => {
if(validUser(req.body)) {
User
.getOneByEmail(req.body.email)
.then(user => {
if(user) {
bcrypt
.compare(req.body.password_digest, user.password_digest)
.then((result) => {
if(result) {
const isSecure = process.env.NODE_ENV != 'development';
res.cookie('user_id', user.id, {
httpOnly: true,
secure: isSecure,
signed: true
})
res.json({
message: 'Logged in'
});
} else {
next(new Error('Invalid Login'))
}
});
} else {
next(new Error('Invalid Login'))
}
});
} else {
next(new Error('Invalid Login'))
}
});
//Allow CORS index.js
app.use(
cors({
origin: "http://localhost:3000",
credentials: true
})
);
//Login client side (React.js)
loginUser(e, loginEmail, password) {
e.preventDefault();
let email = loginEmail;
let password_digest = password;
let body = JSON.stringify({ email, password_digest });
fetch("http://localhost:5656/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
credentials: "include",
body
})
.then(response => response.json())
.then(user => {
console.log(user);
});
}
You should be secure of set "credentials" in the server and in app.
Try to set on you index.js or app.js server side this:
app.use(function(req, res, next) {
res.header('Content-Type', 'application/json;charset=UTF-8')
res.header('Access-Control-Allow-Credentials', true)
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
next()
})
and in you client site add options like this:
let axiosConfig = {
withCredentials: true,
}
export async function loginUser(data) {
try {
const res = await axios.post(
`${URL}:${PORT}/${API}/signin`,
data,
axiosConfig
)
return res
} catch (error) {
console.log(error)
}
}
Edit
To set "credentials" in server we need this line:
res.header('Access-Control-Allow-Credentials', true)
This would let you handle credentials includes in headers.
You also have to tell to axios to set credentials in headers with:
withCredentials: true
Do not forget to adjust cors middleware.
Your node.js express code
const express = require("express");
const cors = require('cors')
const app = express();
app.use(cors(
{
origin: 'http://localhost:3000',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
));
app.use(function(req, res, next) {
res.header('Content-Type', 'application/json;charset=UTF-8')
res.header('Access-Control-Allow-Credentials', true)
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
next()
})
app.get("/auth", function(req, res){
res.cookie('token', 'someauthtoken')
res.json({id: 2});
});
app.listen(3030);
Your front-end code
import React, { useEffect } from 'react';
import axios from 'axios';
async function loginUser() {
try {
const res = await axios.get(
'http://localhost:3030/auth',
{
withCredentials: true,
}
)
return res
} catch (error) {
console.log(error)
}
}
function App() {
useEffect(() => {
loginUser();
}, [])
return (
<div>
</div>
);
}
export default App;
It is because you set httpOnly: true.
This will block the visibility to client side, like reading from javaScript document.cookie().
You can solve this by turn it off.
If you can't see your cookie in the browser, I think it is because you're setting hhtpOnly to true in the cookie's options.
cookie.httpOnly
Specifies the boolean value for the HttpOnly Set-Cookie attribute. When truthy, the HttpOnly attribute is set, otherwise it is not. By default, the HttpOnly attribute is set.
Note: be careful when setting this to true, as compliant clients will not allow client-side JavaScript to see the cookie in document.cookie
res.cookie('user_id', user.id, {
httpOnly: false, // try this
secure: isSecure,
signed: true
})
You need to configure cors in your backend server first.
First, install cors using npm i cors then in your express server add this line of code:
app.use(cors({
origin: "YOUR FRONTEND SITE URL HERE",
credentials: true,
}));
Then, in your frontend app where you are sending GET/POST requests to your backend, make sure to add in your request
If you've used fetch:
const res = await fetch('BACKEND SERVER URL', {
credentials: "include",
// other objects
});
If axios is used:
const res = await axios.post('BACKEND SERVER URL',
{ withCredentials: true },
// other objects,
);
This will solve the problem of storing cookies in frontend sent from backend.