I wanted to use my login data (username, ID) and store them in other tables so that I could access the other data stored in those tables. I am using Node.js and express for my server, and for the session I am using the express-session module. Here is the app.js and the session middleware and its default options
import session from 'express-session';
app.use(session({
name:'test',
secret: "thisismysecrctekeyfhrgfgrfrty84fwir767",
saveUninitialized: true,
resave: false
}));
//some other stuff
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use((_, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.use((req,res,next) => {
console.log('req.session',req.session);
next();
});
After this I use router for all my routes, Here is the login route where I save my username into the session
router.post('/login', (req, res, next) => {
// checks if email exists
User.findOne({ where : { //
name: req.body.name, //
}})
.then(dbUser => {
if (!dbUser) {
return res.status(404).json({message: "user not found"});
} else {
// password hash
bcrypt.compare(req.body.password, dbUser.password, (err, compareRes) => {
if (err) { // error while comparing
res.status(502).json({message: "error while checking user password"});
} else if (compareRes) {
// password match
const token = jwt.sign({ name: req.body.name }, 'secret', { expiresIn: '1h' });
const sessName= req.body.name;
req.session.name=sessName;
res.status(200).json({message: "user logged in", "token": token});
} else { // password doesnt match
res.status(401).json({message: "invalid credentials"});
};
});
};
})
.catch(err => {
console.log('error', err);
});
});
After the user enters the Login info it goes from the login route to the Auth route
router.get('/private', (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: 'not authenticated' });
};
const token = authHeader.split(' ')[1];
let decodedToken;
try {
decodedToken = jwt.verify(token, 'secret');
} catch (err) {
return res.status(500).json({ message: err.message || 'could not decode the token' });
};
if (!decodedToken) {
res.status(401).json({ message: 'unauthorized' });
} else {
res.status(200).json({ message: `here is your resource ${req.session.name}` });
};
});
If I console log the session it shows the username on the terminal, for other routes when I store some data onto the session and access it from other routes it works fine, but when I store the username from the login route it doesn't show up when I try to access it.
router.get('/session', (req,res,next)=>{
if (req.session.name) {
res.json({name: req.session.name})
} else {
res.json({name: null});
}
})
I am new to Node.js so it would be helpful if anyone could answer this.
After
const sessName= req.body.name;
req.session.name=sessName;
the name gets saved in the session from the login route, but it shows me a null value when I access it from other routes. Plus is it normal for the session to end and the username to disappear after I restart my server.
CodePudding user response:
Since both /login
and /private
look like they are being made by code, the problem is probably caused by that code not capturing the session cookie, retaining it and making sure it is sent with all subsequent requests on behalf of this same user.
That session cookie is the key to making express-session work. Upon first engagement with a client, the express-session middleware sees that there is no session cookie attached to this request. It creates a new one with an encrypted unique key in it, adds that key to the internal session store and returns that cookie with the http response.
For the session to work, the client that receives that cookie must retain it and send it with all future http requests from this client (until it expires). Since these http requests to /login
and /private
are being made programmatically, your code has to be set up properly to retain and send this cookie. It will depend upon what http library you are using to make these http requests for how exactly you go about retaining and resending the session cookie.
If these are made from Javascript running in a browser in a web page, then the browser will retain the cookie for you and you will just need appropriate settings on the http requests to make sure the cookie is sent. If you are doing cross-origin requests, then things get a little more complicated.
If these are made from your own code in your own app, then the precise steps to include the cookie will depend upon what http library you're using. Many http libraries have something often referred to as a cookie jar that can be associated with a given request/response to help maintain and send cookies.
Now that you've shown your client code, that code is using fetch()
to make these calls. That's the http request library your client is using.
Because it appears this may be a cross-origin request, to be sure that cookies are sent with requests made by fetch()
, you can add the credentials: "include"
option like this:
fetch(`${API_URL}/private`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
credentials: "include",
}).then(...)
If fetch()
doesn't send credentials (which include cookies), then your session cookie won't be sent and your server will think it has to create a new session on each new request so your session will never be maintained from one request to the next.