I'm using passport js for validate users. Here is my strategy.
passport.use(
new Strategy(opts, async ( payload , done) => {
try {
let user = await User.findById(payload.id);
if (!user) throw new Error("User not found");
if(user.role !== payload.role) throw new Error("Hacker");
return done(null, user.getUserInfo());
} catch (e) {
console.log(e);
done(null, false);
}
})
);
And if all OK, I'm returning a user.getUserInfo(). Now, here is my route:
router.get("url", passport.authenticate("jwt", { session: false }), async (req, res) => {
try {
} catch (e) {
console.log(e);
}
});
And now, when user pass the validator, how can I get this data from return done() statement, or it is unrealistic to take data from there.
In routes, try - {session: true}
and inside req.user, you will get the user data.
Otherwise, a work around can be to set user data in request just before done callback function.
req.userData = user.getUserInfo();
Related
I'm using passport js for validate users. Here is my strategy.
passport.use(
new Strategy(opts, async ( payload , done) => {
try {
let user = await User.findById(payload.id);
if (!user) throw new Error("User not found");
if(user.role !== payload.role) throw new Error("Hacker");
return done(null, user.getUserInfo());
} catch (e) {
console.log(e);
done(null, false);
}
})
);
And if all OK, I'm returning a user.getUserInfo(). Now, here is my route:
router.get("url", passport.authenticate("jwt", { session: false }), async (req, res) => {
try {
} catch (e) {
console.log(e);
}
});
And now, when user pass the validator, how can I get this data from return done() statement, or it is unrealistic to take data from there. I'm a new in node js, so I don't know how to do that, or Is real to take data from passport.authenticate()?
In routes, Do:
{session: true}
and inside req.user, you will get the user data.
Otherwise, a work around can be to set user data in request just before done callback function.
req.userData = user.getUserInfo();
I have a /register router for signing up a user. I am using cookie-session (which is similar) instead of express-session for simplicity for now.
I am stuck on the part where I need to authenticate a user on sign up. I am confused about the functionality of req.sessions.save() and req.login(). I know req.login() is provided by passport.js, but I don't understand which one provides the req.session object.
I am new to passport.js and have read numerous articles, videos, and StackOverflow questions extensively to build up my knowledge. Honestly, the passport documentation has been quite a pain so far. I am still confused about how session initiation on signup should work. Many articles skipped the signup part. I thus request help on how to do it.
router.post('/register', (req, res, next) => {
console.log(req.body)
User.findOne({email: req.body.email}).then((currentUser) => {
if(currentUser){ // already exists
res.render('login')
} else { // if not, create user in our db
new User({
email: req.body.email
}).save().then((newUser) => {
passport.authenticate('local')(req, res, () => {
//>>>> //**This is where I don't know what to do**
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
})
})
});
}
});
})
const express = require("express");
const router = express.Router();
const passport = require("passport");
router.post("/register", (req, res, next) => {
User.findOne({ email: req.body.email }).then((currentUser) => {
if (currentUser) { // already exists
res.render('login')
} else { // if not, create user in our db
new User({
email: req.body.email
}).save();
}
});
passport.authenticate("local", function (err, user, info) {
if (err) {
return res.status(400).json({ errors: err });
}
if (!user) {
return res.status(400).json({errors:"No user found."});
// or save User : new User({email: req.body.email}).save();
}
req.login(user, function (err) {
if (err) {
return res.status(400).json({ errors: err });
}
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
});
return res.status(400).json({ success: `logged in ${user.id}` });
});
})(req, res, next);
});
module.exports = router;
passport.authenticate('local')(request, response, () => {
req.session.save((err) => {
if (err) {
return next(err)
}
res.redirect('http://localhost:3000')
})
}
I have an API / express router:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(500).json("DB Error");
}
});
Currently, on error, it returns 500 DB error. This is my controller:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(e);
if (doc) {
reject("Username already exists.");
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(e);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
What's the right way to return a 400 if username already exists, and a 500 if it was a database error?
Mongoose already uses promises, the use of new Promise is promise construction antipattern.
Express doesn't have the concept of controllers, there are only route handlers and middlewares. Since register should be very aware of the way it will be used in a response, there may be no need for another level of abstraction above route handler. There will be no problem when a function has access to handler parameters and can form a response in-place.
It can be:
router.post("/signup", async function (req, res) {
try {
const { body, password } = req.body;
const user = await User.findOne({ username: username }).lean();
if (user) {
res.status(400).json("Username already exists");
} else {
...
res.json(user);
}
} catch (e) {
res.status(500).json("DB Error");
}
});
In case route handler needs to be reused in multiple places with some variations, it could be refactored to higher-order function or some other helper that is aware of original req and res parameters.
You can change the way you are rejecting the Promise. I'd suggest something like:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(500);
if (doc) {
reject(400);
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(500);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
And in the route:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(e).json(e == 400 ? "Username already exists." : "DB Error");
}
});
Error: data and hash arguments required
i am doing simple, login signup and forgot password in node js using
bcrypt hash
code : for login
app.post('/login', (req, res) => {
console.log('login');
let {email, password} = req.body;
User.updateOne({email: email}, ' email password', (err, userData) => {
if (!err) {
let passwordCheck = bcrypt.compareSync(password, userData.password);
if (passwordCheck) {
console.log('login2');
req.session.user = {
email: userData.email,
id: userData._id
};
req.session.user.expires = new Date(Date.now() + 3 * 24 * 3600 * 1000);
res.status(200).send('You are logged in, Welcome!');
} else {
res.status(401).send('incorrect password');
console.log('login3');
}
} else {
res.status(401).send('invalid login credentials');
console.log('login4');
}
});
});
code for signUp :
app.post('/signup', (req, res) => {
let {email, password} = req.body;
let userData = {password: bcrypt.hashSync(password, 5, null), email };
console.log('out save');
let newUser = new User(userData);
newUser.save().then(error => {
if (!error) {
console.log('in save');
return res.status(200).json('signup successful');
} else {
if (error.code === 11000) {
return res.status(409).send('user already exist!');
} else {
console.log(JSON.stringigy(error, null, 2));
return res.status(500).send('error signing up user');
}
}
});
});
i have tried console logging few lines and turned out that the code doesn't go into signup
newUser.save();
tell me where i'm going wrong
The issue is with this line newUser.save().then(error => {. Do you notice the .then(). That is a resolved promise so it wouldn't be returning an error. Typically you would see something like this.
Promise()
.then((result) => {
// result is a resolved promise
})
.catch((error) => {
// error is a rejected promise
})
So you should try changing your code to this:
newUser.save()
.then(result => {
console.log('in save')
return res.status(200).json('signup successful')
})
.catch(error => {
if (error.code === 11000) {
return res.status(409).send('user already exist!')
} else {
console.log(JSON.stringigy(error, null, 2))
return res.status(500).send('error signing up user')
}
})
It looks like you're using mongoose, here is the API docs for Document.prototype.save() https://mongoosejs.com/docs/api.html#document_Document-save
Their documentation uses callback functions for the most part but if you scroll to the end of the .save() documentation you will see they show one example with a promise.
bcrypt.compareSync takes 2 parameters; passwordToCheck, passwordHash
You are getting error "bcrypt Error: data and hash arguments required"
This error means one or both parameters are either null or undefined,
In your case you need to make sure that password, userData.password are correctly going in function bcrypt.compareSync
I've implemented passport local strategy using async/await as below
const strategy = new LocalStrategy(
async(username, password, done) => {
try {
// Find the user given the username
const user = await User.findOne({ username });
// If not, send info
if (!user) {
return done(null, false, {
success: false,
message: 'User not found'
})
}
// Check if the password is correct
const isMatch = await user.isValidPassword(password);
// If not, send info
if (!isMatch) {
return done(null, false, {
success: false,
message: 'Invalid Password'
});
}
// Otherwise, return the user
done(null, user);
} catch (error) {
done(error, false);
}
}
);
passport.use(strategy);
And implemented custom callback in routes using the code below.
router.post('/login', async(req, res, next) => {
const { receivedUser, information } = await passport.authenticate('local');
// If a user is found
if (receivedUser) {
res.status(200).json({
success: true,
message: 'Authentication Successful'
});
} else {
// If user is not found
res.status(401).json(information);
}
};
);
There are errors in above custom callback implementation as receivedUser and information are 'undefined'. How to make changes to above custom callback using async/await to remove errors ?
Referred docs:
http://passportjs.org/docs/configure
http://passportjs.org/docs/username-password