I'm trying to connect my Mongodb with Auth0 authorization. I'm using Mlabs to host my db. Inside this db, I have a "users" collection, and the documents have "username", "password", "email, and "_id" keys. When I try to use the Auth0 login script, I get the following error: Not authorized for query on mydb.users. Below is the script:
function login(email, password, callback) {
mongo('<mongodb uri>', function (db) {
var users = db.collection('users');
console.log(users);
users.findOne({ email: email }, function (err, user) {
if (err) return callback(err);
if (!user) return callback(new WrongUsernameOrPasswordError(email));
bcrypt.compare(password, user.password, function (err, isValid) {
if (err) {
callback(err);
} else if (!isValid) {
callback(new WrongUsernameOrPasswordError(email));
} else {
callback(null, {
user_id: user._id.toString(),
nickname: user.nickname,
email: user.email
});
}
});
});
});
}
Any ideas why I might be getting this error? Thanks in advance!
You probably have to check for a connection to the db.
The callback should have an error parameter
mongo('<mongodb uri>', function (err,db) {
if(err){
console.log(err)
}
})
if that doesn't fix it then you have to assign read and write permissions to perform the intended operations (in your case read) on the specified database
Related
What I am attempting to do is write a statement to check if email exists in my mysql database when a user registers. In postman it sends me the correct error message of "user already taken" however the server crashes after and displays "cannot set headers after they are sent to the client." I have read similar posts but did not help.
//The following code is in my user.service.js file:
const pool = require("../../config/database");
module.exports = {
//Create new user
createUser: (data, callBack) =>{
pool.query(
`insert into registration(name, email, password, confirm_password)
values(?,?,?,?)`,
[
data.name,
data.email,
data.password,
data.confirm_password
],
(error, results, fields) =>{
if(error){
return callBack(error);
}
return callBack(null, results);
}
);
}
}
//The following code is in my user.controller.js file:
const {
createUser,
} = require("./user.service");
const pool = require("../../config/database");
module.exports = {
createUser: (req, res) =>{
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
})
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
}
}
//The following code is from user.router.js file:
const {
createUser,
} = require("./user.controller");
const router = require("express").Router();
router.post("/signup", createUser);
module.exports = router;
In your createUser function that is executed on the post request you are doing two things. First you check whether a user with the provided email exists and, second, you create a user. However, those functions are not executed consecutively, instead they are running simultaneously and thus create a race condition.
So going off on your example, if the email check query SELECT email FROM registration WHERE email = ? is faster and the user already exists, it will respond with:
return res.status(400).json({
message: 'User already taken'
})
but the createUser function (below) is still running and once it is finished, it will try to also send a response. Therefore, you are presented with an application crash in the console even though in the postman you can see the response stating that the user already exists.
In order to fix this error you should execute the createUser function only if the results.length is 0 inside the callback provided to the email check query, like so:
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
})
}
Now you execute the createUser function only if a user with the provided email doesn't exist, which effectively removes the race condition between the two functions.
I am new to NodeJS and I have been following a tutorial. I made some changes to the tutorial code in order to allow users to login by typing in their username or email instead of just username.
passport.use(
new LocalStrategy(
(username_or_email, password, done) => {
User.findOne({ email: username_or_email }, (err, user) => {
if (err) {
return done(err);
console.log(err);
}
if (!user) {
User.findOne({ username: username_or_email }, (err, user) => {
//If there is an error
if (err) {
return done(err);
console.log(err);
}
//If there is no user
if (!user) {
return done(null, false);
} else {
user.comparePassword(password, done);
}
});
} else {
user.comparePassword(password, done);
}
});
}
)
The code above works well and allows users to type in username OR password to login.
Now when I follow the tutorial for how to logout, their method doesn't work for me.
I have a route like this, it is supposed to log a user out.
userRouter.get(
'/logout',
passport.authenticate('jwt', { session: false }),
(req, res) => {
res.clearCookie('access_token');
res.json({ user: { username: '', role: '' }, success: true });
}
);
When I go to it in Postman it says "Unauthorized" and does not return anything else.
I believe it could be something to do with my 'jwt' set up, shown below.
passport.use(
new JwtStrategy(
{
jwtFromRequest: cookieExtractor,
secretOrKey: 'NoobCoder',
},
(payload, done) => {
console.log(payload);
User.findById({ _id: payload.sub }, (err, user) => {
if (err) {
return done(err, false);
console.log('1');
}
if (user) {
return done(null, user);
console.log('2');
} else {
return done(null, false);
console.log('3');
}
});
}
)
);
This is the cookieExtractor function that I use for jwtFromRequest
const cookieExtractor = (req) => {
let token = null;
console.log(token);
if (req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
};
The only console output I get is the console.log in the cookieExtractor.
Which makes me believe that that must be the point of failure. It is a "null" output as expected, and if I console.log the token, I get the current logged in users token. I believe the jwtFromRequest calls the cookieExtractor function but fails at some point soon after.
(Assume the hashing has been done) I am trying to do an authenticate user function, by comparing an entered password and its hash in my MongoDB collection. This is my the method in my model.js (copied from the bcrypt guide):
PetOwnerSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
And my controller.js:
exports.check = function (req, res) {
var email = req.body['email'];
var password = req.body['password'];
PetOwner.findOne({ "email" : email}, 'email', function(err, task) {
if (err)
res.send(err);
if ( task === null ){
res.json(0); // no matching result
console.log("Email is not yet registered");
} else {
task.comparePassword(password, task.password, function(err, isMatch) { //compare password and the already-hashed password in MongoDB
if (err) throw err;
if(isMatch){
res.json(1);
console.log("Found matching password to email");
}
else{
res.json(0);
console.log("Wrong password");
}
});
}
})
};
And when I fetch the check method, the node console prompts the error that the cb in my model.js is not a function. I have tried several workaround but none has worked so far. Is there any way to debug this?
PetOwnerSchema.methods.comparePassword = function(candidatePassword, cb)
Your function takes a single parameter so you cannot refer to a context of "this" outside the function, like you do by using "this.password".
If you add the password to compare with as a second parameter like so:
PetOwnerSchema.methods.comparePassword = function(candidatePassword, password2, cb)
You can then just compare the two inside the function as you are trying to do.
Hope it helps
I am new to Node and am trying to do some user registration form validation. I would like to refactor the following code to get rid of the pyramid if possible. Im using Express Validator within the userService module to check against empty fields and for email validation, but the isValueUnique function is just a Mongoose findOne query. Im using Graphics Magick to resize the image. Any suggestions most welcome:
router.post('/register', function(req, res, next){
userCheck = userService.checkRegistration(req)
if(userCheck.errors){
return res.render('user/register', {
errors: userCheck.errors,
message: req.flash('error'),
title: 'People Power | Register'
})
}
User.isValueUnique({username: req.body.username}, function(err, user){
if(user){
return res.render('user/register', { message: 'Username already taken. Please choose another.', title: 'People Power | Register' });
}else{
User.isValueUnique({ email: req.body.email }, function(err, user){
if(user){
return res.render('user/register', { message: 'Email already registered. Please try again.', title: 'People Power | Register' });
} else{
User.createUser(userCheck, function(err, user){
if(err) throw err;
userService.resizeImage(userCheck, function(){
req.login(user, function(err){
req.flash('success', 'You have registered successfully, and are now logged in!')
res.redirect('/')
})
});
});
}
})
}
})
});
You could use async#waterfall to improve "callback hell", so your code might looks like the following:
async.waterfall([
function (callback) {
User.isValueUnique({username: req.body.username}, callback);
},
function (user, callback) {
if (user) {
return res.render('user/register', { message: 'Username already taken. Please choose another.', title: 'People Power | Register' });
}
User.isValueUnique({ email: req.body.email}, callback);
}
function (user, callback) {
if (user) {
return res.render('user/register', { message: 'Email already registered. Please try again.', title: 'People Power | Register'};
}
User.createUser(userCheck, callback);
},
function (user, callback) {
userService.resizeImage(userCheck, callback);
},
function (callback) {
req.login(user, function(err) {
req.flash('success', 'You have registered successfully, and are now logged in!')
res.redirect('/')
});
}
], function (err) {
if (err) throw err;
});
Also I suggest to look at PassportJS module that already has suite of patterns for implementation of users registration feature.
I'm having problems with saving a document in MongoDB in my Nodejitsu/MongoHQ app. Locally all works fine, but the MongoHQ db requires authentification and it fails with working user/pass (connecting to MongoDB shell with the same credentials works fine). Here is the function:
var update_user = function(user, callback) {
var client = new Db(db, new Server(host, port, {}));
var update = function (err, collection) {
collection.findOne({uid:user.id}, function(err, doc) {
if (err) { throw err; }
if (!doc) doc = { uid: user.id }
doc.img = user.img;
doc.name = user.name;
collection.save(doc, {safe: true}, function(err, doc) {
if (err) { throw err; }
callback(user);
});
});
};
client.open(function(err, client) {
if (err) { throw err; }
client.authenticate(user, pass, function(err, result) {
client.collection('users', update);
});
});
}
What I'm doing wrong here?
UPD: err parameter of authenticate method has the following value:
{ [MongoError: auth fails] name: 'MongoError', errmsg: 'auth fails', ok: 0 }
I checked the stored password & username again, they work for logging in the MongoHQ shell.
If your credentials are for an admin user account you must target the admin database in your authenticate call:
client.authenticate(user, pass, {authdb: 'admin'}, function(err, result) {
client.collection('users', update);
});