I'm using express-session to store auth token in the session. The problem i'm facing is that the session i set in the /authenticate (post route) is not undefined in the /join (get route). I have searched for the similar questions but that didn't help. Any idea what's going wrong in my code?
server.js
// All required modules loaded..
// Session config
app.use(
session({
secret: "mysessionsecret",
resave: false,
saveUninitialized: false,
cookie: { secure: false, maxAge: 6000000 }
})
);
// #route:authenticate
app.post("/authenticate", async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user) {
return res.status(400).json({ msg: "Invalid username entered" });
}
// Compare the password
const compare = await bcrypt.compare(password, user.password);
if (!compare) {
return res.status(400).json({ msg: "Incorrect password" });
}
// Create token of the user ID
jwt.sign(
{
userId: user.id
},
config.get("jwtSecret"),
{
expiresIn: "2d"
},
(err, token) => {
if (err) throw err;
if (!req.session.user_id) {
req.session.token = token;
console.log(req.session.token); // Accessable here
}
}
);
res.end();
} catch (error) {
return res.send("Server error");
}
});
// #route:get /join
app.get("/join", (req, res) => {
console.log(req.session.token); // token not accessable here. returns undefined
return res.end();
});
The token will not be in the cookie, that's on the server only. The cookie is just a session ID. The default name for the express-session cookie is connect.sid. There should be a cookie with that name.
If you don't see that cookie anywhere, try saveUninitialized:true. You may also try calling req.session.save() after you modify the session in your /authenticate route. Either one of those should cause the session cookie to get set.
When you set saveUnitialized: false, you tell express-session NOT to set your session cookie until you tell it to. Unfortunately, the doc doesn't really say how you tell it to now set the session cookie. My guess was that req.session.save() might do it or just turning saveUnitialized to true would also do it. You can experiment with only using one of the two changes, though now you have a session cookie so you'd have to clear it in order to test with just one of them.
Related
I have the following code:
app.post("/login", (req, res) => {
const { username, password } = req.body;
// dummy local database with custome helper functions to look up a user:
db.users.findByUsername(username, (err, user) => {
if (!user) return res.status(403).json({ msg: "No user found!" });
if (user.password === password) {
// Adding properties to session
req.session.authenticated = true;
req.session.user = {
username,
password,
};
console.log(req.session);
// Session is printed in terminal with the above properties. Works fine up to here.
res.redirect("/shop");
} else {
res.status(403).json({ msg: "Bad Credentials" });
}
});
});
I used express-session to create a session and i'm storing it in memory. I created a middleware that would allow a user to access a /shop page only if they're authenticated and have the req.session.authenticated property set to true. For some reason, after they log in, and they're redirected to the /shop page, the properties created in the session are no longer there. Here's the rest of the code:
Authentication middleware:
function ensureAuthentication(req, res, next) {
if (req.session.authenticated) {
// Properties that were added upon logging in are not attached.
return next();
} else {
res.status(403).json({ msg: "You're not authorized to view this page" });
}
}
Shop page
app.get("/shop", ensureAuthentication, (req, res) => {
// Send the user object to the view page:
res.render("shop", { user: req.session.user });
});
Any opinions? Am I missing something here? Does the order of how I have the endpoints written matter?
I'm using express-session and trying to implement a protected route with custom middleware.
[NOTE: I'm currently storing my session in-memory]
app.use(
session({
secret: "f4z4gs$Gcg",
cookie: { maxAge: 300000000, secure: true },
saveUninitialized: false,
resave: false,
store,
})
);
// MIDDLEWARE
function ensureAuthenticated(req, res, next) {
console.log(req.session) // This doesn't show the user and authenticated properties created in the POST login request
if (req.session.authenticated) {
return next();
} else {
res.status(403).json({ msg: "You're not authorized to view this page" });
}
};
app.post("/login", (req, res) => {
const { username, password } = req.body;
db.users.findByUsername(username, (err, user) => {
if (user) {
if (user.password === password) {
// Add your authenticated property below:
req.session.authenticated = true;
// Add the user object below:
req.session.user = {
username,
password,
};
// Send the session back to the client below:
res.json(req.session); // Properties show up here
} else {
res.status(403).json({ msg: "Bad Credentials" });
}
} else {
res.status(403).json({ msg: "No user found!" });
}
});
});
// PROTECTED ROUTE
app.get("/protected", ensureAuthenticated, (req, res) => {
res.render("profile");
});
Once a user logs in successfully, I try to add two properties into req.session: authenticated and the user object. However, once I login and try to access /protected with the middleware, my session properties do not persist (no user or authenticated property). Am I missing something?
Try setting secure to false in the cookie object. If you want it to be httpOnly, then just set httpOnly to true.
I am using Nuxt SSR with express session and I have a passport JS redirect from the server side
/**
* POST /signup
* Create a new local account.
*/
exports.postSignup = (req, res, next) => {
const validationErrors = [];
if (!validator.isEmail(req.body.email)) validationErrors.push({ msg: 'Please enter a valid email address.' });
if (!validator.isLength(req.body.password, { min: 8 })) validationErrors.push({ msg: 'Password must be at least 8 characters long' });
if (req.body.password !== req.body.confirmPassword) validationErrors.push({ msg: 'Passwords do not match' });
if (validationErrors.length) {
req.flash('errors', validationErrors);
return res.redirect('/signup');
}
req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false });
const user = new User({
email: req.body.email,
password: req.body.password
});
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (err) { return next(err); }
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user.save((err) => {
if (err) { return next(err); }
req.logIn(user, (err) => {
if (err) {
return next(err);
}
res.redirect('/');
});
});
});
};
If I call the redirect method? it would reload the page and clear Vuex state right?
How do I do this redirect from passport such that Vuex state is kept intact and client page does not refresh
It is indeed better to asynchronously handle form submissions to avoid page refresh as #Darius mentioned. But for completion's sake I'd like to mention that solutions do exist to persist your Vuex state, such as vuex-persistedstate.
It can be used to persist the state to localStorage, sessionStorage, or even cookies. It can also be used as a Nuxt plugin.
I know this question was asked many times in stack over flow. I tried every accepted answers but can't my local strategy into function. Here is my code
var express = require('express');
var app = express();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.set('trust proxy', 1); // trust first proxy
app.use(session({
secret: '564sdf4as564f56a7s765s4afjkgadxjkbadksj',
resave: true,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy({
usernameField:'userName',
passwordField:'password',
passReqToCallback : true
},function(request, userName, password, done) {
console.log(request);
UserAccount.findOne({'userName': userName} , function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, 'Incorrect username.' );
user.verifyPassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, 'Incorrect password.');
}
});
});
}));
passport.serializeUser(function(user, done) {
console.log('Serialize user called');
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('Deserialize user called');
UserAccount.findById(id, function(err, user) {
done(err, user);
});
});
Then I created a router like
var router = express.Router();
require('./controllers/user')(router,passport);
app.use('/api',router);
Then in my user controller I created signIn function like
app.post('/signIn',function (request,response,next){
var variables = request.body;
console.log(variables);
passport.authenticate('local', function(error, user, info) {
console.log(user);
if (error) { console.log(error); return next(err); }
if (!user) { return response.redirect('/login'); }
response.logIn(user, function(err) {
if (err) { return next(err); }
return response.redirect('/users/' + user.username);
});
})(request, response, next);
});
Then I send a request from "Postman"
{
"userName":"karthik#abc.com",
"password":"qwerty"
}
My mongodb userName and password fields are same.
In my db there is an account with this user name and password. But every time it return 'user' as 'false' inside authenticate. I tried to console my request inside local strategy but it never gets called. I don't understand What I done wrong here? Can some one help to solve this? Thank you very much.
You should name local strategy and use it in authenticate.
Use like this passport.use('local-strategy',new LocalStrategy({});
and like passport.authenticate('local-strategy');
I'm working in an application which uses a REST api using the MEAN stack and Passport JS to manage the authentication.
The authentication, we use JTW tokens for the communication between the backend and frontend. The token is generated based on local username and passwords.
Now I want to 'add' (authorize) the user's google account to the profile to use with google calendar API. (using this-> https://github.com/wanasit/google-calendar)
I've already have managed to send the user to the google authorization page, and get the token back from it. The problem is that when the user gets redirected to the page, it looses the JWT token where I check the user for the request.
Is there any other way to get the current logged in user, or to pass some custom callback authorization header/param when calling the authorize method?
auth.js:
var googleParams = {
clientID: config.auth.google.clientID,
clientSecret: config.auth.google.clientSecret,
callbackURL: config.auth.google.callbackURL
}
var googleStrategy = new GoogleStrategy(googleParams, function (token, refreshToken, profile, done) {
profile.token = token;
return done(null, profile);
});
routes:
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
the auth.authRedirect() function above is the closest solution I've found. It's a Express middleware wich redirects the user to a known route in the frontend where the user IS authenticated... but then I would not be able to fetch all his Google profile and information i need...
You have to be sure the app.use(session) its been called before any route.
...
app.use(session({
secret: 'secret'
}))
app.use(passport.initialize())
app.use(passport.session())
...
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
Your req.user won't be undefined in this case.
If it doen't work right way, I can put my whole code that I've created here.
Hope it help you! :)
So what I ended up doing was:
Authenticate the user making the request via JWT access_token
Get the user's ID and set it to the state option's property
The user is redirected to the google authorization page and choose the account (s)he wants to connect
(S)He gets redirected to my callback url with the state query param having the user's id
Now I just have to get that id, search the user in the database, and set the data I need from req.account which contains the user's openid profile.
var googleScope = ['openid', 'email', 'https://www.googleapis.com/auth/calendar'];
routes.get(
'/google',
auth.authenticate(),
auth.isLoggedIn,
function (req, res, next) {
var _id = '' + req.user._id; // convert to String... _id is an mongoose object
return auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true, state: _id })(req, res, next)
}
);
routes.get('/callback/google',
function (req, res, next) {
auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true })(req, res, next);
},
auth.saveUserData()
);
saveUserData= function () {
return function (req, res, next) {
if (req.query.state) {
var _id = req.query.state;
User.findOne({ _id, deleted: false, active: true })
.exec(function (err, user) {
if (err) {
res.send(err);
}
if (user) {
user.auth.google = {
id: req.account.id,
token: req.account.token,
email: (req.account.emails.length ? req.account.emails[0].value : null),
name: req.account.displayName
}
user.save(function (err, data) {
if (err) {
res.send(err);
} else {
res.redirect('/')
}
})
} else {
res.sendStatus(401);
}
})
} else {
res.sendStatus(400)
}
}