I have been working on MERN stack application, I tried to create login module for my application , so that i have used google authentication in the server side using passportjs. Every thing working good when i use localhost,but when my app running on aws server then nodejs does not store my cookie session,so that passport deserializeUser does not working, and passport serializeUser working good.
*my react app running on digital ocean server.
*my node app running on aws server.
API call in my react app:
axios.get(url,{withCredentials: true}).then((response) =>{
console.log('login:',response.data)
}).catch((e)=>{
console.log('error:',e)
})
}
session setting in my node app:
router.use(cookieParser());
router.use( session( { secret: 'keyboard cat',
cookie: { maxAge: 24 * 60 * 60 * 1000 },
resave: true,
saveUninitialized: false
}
)
);
router.use(passport.initialize());
router.use(passport.session());
Cookie session and passport session are stored and working fine when the app in running my local host.
Related
I used google authentication in my MERN stack app using passport js. I have deployed frontend to vercel and backend to render. in my localhost, I can log in successfully and it returns cookies to my frontend from the backend. but in the deployed version when I try to log in it does log in but it doesn't return any cookies to the frontend. I did some research and found that cookies can't be shared between different domains. my question is should I host my backend in a subdomain and frontend in the main domain?
frontend: https://passport-frontend-seven.vercel.app/
backend : https://popupchat-backend.onrender.com/
// how i used express-session:
app.use(
session({
secret: "secretcode",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
sameSite: "none",
secure: true,
domain: process.env.CLIENT_URL,
maxAge: 1000 * 60 * 60 * 24 * 7, // One Week
},
})
);
app.set("trust proxy", 1);
Yes, you are correct in your understanding that cookies can't be shared between different domains. If you have your frontend and backend deployed on different domains, then you will have difficulty accessing the cookies set by the backend in the frontend.
Hosting your backend on a subdomain of your main domain is one solution to this problem. For example, if your main domain is example.com, you can host your backend at api.example.com. This way, both the frontend and the backend will be under the same main domain and you will be able to share cookies between them.
However, there are other solutions as well, such as using a token-based authentication system, where the backend returns a token after a successful login, and the frontend can store this token in local storage or a cookie. The frontend can then send this token in the header of subsequent API requests to authenticate the user on the backend. This approach can be more secure than using cookies, since cookies are often stored in plain text and can be easily compromised.
I have an api (nodejs + express) running on azure webapp service and a frontend (nuxtjs) running locally and on cloudlfare. My auth flow uses passportjs LocalStrategy and worked fine when I developed it and ran the api locally. Now that I have deployed the api on azure app service, my front end always gets a 401 not authorized response. I am pretty sure it is not an issue with the frontend nuxt app since the problem occurs only when trying to use the azure hosted api.
I am using express-session with a postgres database to store session information.
const sessionPool = new Pool() //if this becomes problematic consider sessionPool.end() in logout
auth.use(session({
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
cookie: {maxAge: 1000 * 60 * 60 * 24}, //one day
store: new (require('connect-pg-simple')(session))({
SameSite: 'none',
pool: sessionPool,
tableName: 'session'
}),
}))
Everything seems to work right at first. The user credentials get sent to the backend, they are run against the database and if they match an existing user it creates a session and CLAIMS to send the user info in the response. (some of) the cookies exist on the front end, but it seems like some are missing. When running the application locally the front end
stores 5 cookies but in production it only seems to store 3. All api calls that require authorization return 401 not authorized even though the client seems to have the right information and the backend shows they have a live session (I can see the session data in the db table).
//req.isAuthenticated() always returns false on the azure web app, but true when run locally
auth.get("/user", async (req, res) => {
try {
if (req.isAuthenticated()) {
res.json({ user: req.user });
} else {
console.log("User not authenticated");
res.sendStatus(401);
}
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
I believe it is an issue with the azure app service blocking my authorization flow. The app service is using the node 16 run time and windows os (so it's using iisnode). Anyone have any insight?
Azure app service is a reverse proxy, meaning client details aren't going to be as expected from a typical request. For example, the client IP address is available from the x-forwarded-for header instead.
You need to tell express that your app is running behind a proxy:
app.set("trust proxy", 1);
and at the same time, you should explicitly define the cookie domain and make sure httpOnly is enabled, to help prevent session theft from XSS attacks.
cookie: {
domain: 'my.website.com',
httpOnly: true,
maxAge: 24*3600000, // 24 hours
}
Learn more about running express behind a proxy.
I'm struggling with understanding cookie credentials in detail.
The main issue is that my frontend and backend run on different domains. On localhost, they have different ports (localhost:4200 and localhost:3000), and on my server different subdomain (example.com and api.example.com).
Basic setup:
Angular send post request to login:
this.http.post(environment.apiUrl + 'users', credentials)
NodeJS validates the request and creates an express-session (session in DB and cookie with sessionId):
app.use(session({
secret: process.env.SECRET,
saveUninitialized: false,
store: mongoDBStore,
name: 'node_session',
cookie: {
secure: true,
httpOnly: true,
maxAge: 5 * 60 * 1000
}
}));
app.post('/', (req, res) => {
// some validations
req.session.userId = result._id;
res.send(result._id);
}
My understanding
From my understanding cookies get stored for the server domain. So if both run on the same domain, on each request, the backend can use the session cookie. But with different domains, the session is empty on further requests.
Additionally, I found the cookie node_session in the frontend and backend. So if I add { withCredentials: true } to the requests of Angular, everything works. But in this case, I use the cookie from the frontend.
My Questions:
Is it secure to use the frontend cookie?
Why can I find the session cookie in the frontend and backend?
Why is my backend unable to find the session cookie?
session information is stored on the backend. frontend just stores uniq session identifier in cookies. If you want to understand somehow that requests came from the same user one of the options is to use {withCredentials: true}, without it cookies are just not saved to the client, and backend can not define, whether different queries were send by one client or not.
as an alternative to cookies there is a popular option to use authorization token, but it is a bit more difficult to setup
i'm working on micro services with express js with express-session set up like this:
server.use(session({
name: 'apps',
resave: false,
saveUninitialized: false,
secret: 'secretToken',
cookie: {
sameSite: false,
secure: false, // true
httpOnly: false, // false
}
}));
i apply the same setup with all services.
but then it's only work with just only one services. other services can't share the same data from other service, i'm putting access token in the req.session to let other apps having the same access token.
Is there a way for me to let different service using the same session?
The default server-side session storage is MemoryStore. Which means each service store the session data on their own process. Memory cannot be shared between different processes.
In order for microservices to share session data, we need separate storage, such as connect-redis, connect-mongo.
All microservices connect the redis or database server and use them as session data storage when initializing express-session middleware.
Workflow:
Client send HTTP request(with session id stored in a cookie) => Service A or Service B or Service C => express-session get the session data from redis or database by session-id => do your things.
I'm using express-session for my app. Cookies are set and users are authorised when deployed on Heroku which by default serves the app on a secure connection. When working locally on localhost with a non https connection the session is not initialised thus causing me to get a 401 back everytime. I cannot find any answers for this. My code is as follows:
app.use(session({
name: consts.SESSION_COOKIE_NAME,
secret: consts.SECRET_KEY,
saveUninitialized: false,
resave: true,
cookie:{
httpOnly: true,
secure: false
}
}));