I'm trying to check that a username is unique, and I gather that I'd need a custom validation for that. I've written the following code, but instead of returning the error in the array returned by .validate(), it just throws the error, which isn't the behaviour described in the docs and isn't what I want.
var User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
validate: {
isUnique: function (username) {
User.find({ where: { username: username }})
.done(function (err, user) {
if (err) {
throw err;
}
if (user) {
throw new Error('Username already in use');
}
});
}
}
},
The validation you are doing is asynchronous. You call the User.find method, and after that the validation method returns. Sequelize has no way of knowing that you are doing something async in your validation, unless you tell it so. Once your find call is done you throw the error, but the validation has completed, so there is no code to catch that error, which means the error is thrown and crashes the app
The way to tell sequelize that you are doing something async is to take a second argument to your function, which is a callback. If you call the callback without any arguments, the validation succeeded, if you give it an argument, the validation failed.
var User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
validate: {
isUnique: function (username, done) {
User.find({ where: { username: username }})
.done(function (err, user) {
if (err) {
done(err);
}
if (user) {
done(new Error('Username already in use'));
}
done();
});
}
}
},
Could you point me to the part of the documentation that mislead you, then we can hopefully correct it :)
Sequelize supports Promise style async operations. Specifically the Bluebird.js lib. Just change your function to specifically use the Promise -> next() pattern.
var User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
validate: {
isUnique: function (username) {
User.find({ where: { username: username }})
.then(function (user) {
if (user) {
throw new Error('Username already in use');
}
});
}
}
},
This will also handle any errors on User.find() for you.
See this example in their codebase
Of course the easiest way to handle unique constraints is by setting unique: true on the field definition itself:
var User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
unique: true
},
But this requires that you are either creating the table using Sequelize or already have a table with the unique constraint set.
This question got a valid answer which explains how the validation option works. But in this situation you don't have to check if a user exists on your own.
Sequelizejs has a findOrCreate() method, that checks if somethings exists before doing anything.
Very useful method :)
Related
Server error while using embedded passwordless login via phone number
General
Getting an error while verifying the code (OTP) sent to the phone number via webAuth.passwordlessLogin
Error:
Cannot read properties of undefined (reading 'name')
I have looked at similar posts, which suggest checking any custom rules. I have no rules defined.
function verify(e) {
e.preventDefault();
console.log("SUBMITTED!!")
webAuth.current.passwordlessLogin({
connection: 'sms',
phoneNumber: '+919140239869',
//email: 'utkarsh#tatsam.in',
verificationCode: code,
}, function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result);
}
});
}
function fetchAuth0Details(e){
e.preventDefault();
webAuth.current.parseHash({ hash: window.location.hash }, function(err, authResult) {
if (err) {
return console.log(err);
}
webAuth.current.client.userInfo(authResult.accessToken, function(err, user) {
alert(user);
});
});
}
I'm building a restful api using node express mongoose/mongo etc.. I'm trying to output an array of users that are being followed by a particular user. Here is the schema.
var UserSchema = new mongoose.Schema({
username: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true},
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
bio: String,
image: String,
following: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
}, {timestamps: true});
So every User has array of users in an array in the key 'following'. I'm trying to output that list by first finding the user record through it's own id and then mapping through this array to find the followed users for this current user.
router.get('/users/friends', auth.required, function(req, res, next) {
var limit = 20;
var offset = 0;
if(typeof req.query.limit !== 'undefined'){
limit = req.query.limit;
}
if(typeof req.query.offset !== 'undefined'){
offset = req.query.offset;
}
User.findById(req.payload.id)
.then(function(user){
if (!user) { return res.sendStatus(401); }
return res.json({
users: user.following.map(function(username){
User.findById(username)
.then(function(userlist){
console.log('userlist:',userlist.username);
return userlist.username;
})
.catch(next)
})
})
})
.catch(next);
});
Now the console.log in this code outputs the correct data in the js console but I can't seem to find a way to deliver this to the client. So far my efforts bring forth 'null' values in the client. The correct amount of records but just null values. Any ideas how to fix this?
revised my code to this after taking advice below. Now it manages to get the first record to the client but then errors out with
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
Blockquote
router.get('/users/friends', auth.required, function(req, res, next) {
var limit = 20;
var offset = 0;
if (typeof req.query.limit !== 'undefined') {
limit = req.query.limit;
}
if (typeof req.query.offset !== 'undefined') {
offset = req.query.offset;
}
User.findById(req.payload.id)
.then(function(user) {
if (!user) {
return res.sendStatus(401);
}
Promise.all(
user.following
).then(function(userarray) {
console.log(userarray);
userarray.forEach(function(userid) {
Promise.all([
User.find({
_id: {
$in: userid
}
})
.limit(Number(limit))
.skip(Number(offset))
.populate('author')
.exec()
]).then(function(results) {
userdetails = results[0];
var userdetailsCount = results[1];
return res.json({
userdetails: userdetails.map(function(userdetail){
return userdetail;
})
});
})
})
})
})
.catch(next);
});
Your problem section is:
return res.json({
users: user.following.map(function(username){
User.findById(username)
.then(function(userlist){
console.log('userlist:',userlist.username);
return userlist.username;
})
.catch(next)
})
})
The bit User.findById(username) will return a promise. But you are not awaiting on that promise. I'm guessing you think that the then function following that promise, which logs userlist.username to the console and returns it, should mean that your map function returns a list of userlist.username's. But this is not the case. Your map function is returning an array of promises.
What you really want is a feature like Bluebird's Promise.map: http://bluebirdjs.com/docs/api/promise.map.html (or, look for a similar feature, to deal with arrays of promises, in whichever promise library you happen to be using).
I apologise if this is a stupid question, please allow me to explain a little. I am running a MEAN application.
In the server routes for my CRUD generated module I have included two separate post requests to the same api end point.
app.route('/api/tasks/:taskId').all(tasksPolicy.isAllowed)
.get(tasks.read)
.put(tasks.update)
.delete(tasks.delete)
.post(tasks.newCo)
.post(tasks.newOffer);
Each of these perform a separate push request into my mongo document for task, based on the taskId.
When I run one function at a time, each individual function works successfully and pushes into the correct array.
However, when I run include both functions on the same page at the same time the newOffer function pushes a null value into the newCo array. And the newCo function continues to work successfully.
I have no idea why..
again, I apologise if this is a stupid question.
server.controller.js
/**
* Add a new comment
*/
exports.newCo = function(req, res) {
Task.findOneAndUpdate({_id: req.params.taskId},
{
"$push": {
comments: req.body.comment
}
}, {
new: true //to return updated document
})
.exec(function(error, task) {
if (error) {
return res.status(400).send({message: 'Failed to add comment due to invalid params!'});
}
return res.status(200).send(task);
});
};
/**
* Add a new offer
*/
exports.newOffer = function(req, res) {
Task.findOneAndUpdate({_id: req.params.taskId},
{
"$push": {
offers: req.body.offer
}
}, {
new: true //to return updated document
})
.exec(function(error, task) {
if (error) {
return res.status(400).send({message: 'Failed to add offer due to invalid params!'});
}
return res.status(200).send(task);
});
};
client.controller.js
vm.newCo = function() {
$http.post('/api/tasks/' + task._id , {comment: { comment: vm.task.newComment, user: vm.authentication.user, profileImageURL: vm.authentication.user.profileImageURL, displayName: vm.authentication.user.displayName } })
.then(successCallback, errorCallback);
function successCallback(res) {
$state.transitionTo($state.current, $state.params, {
reload: true,
inherit: false,
notify: true
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
};
//close new comment function
//new offer
vm.newOffer = function() {
$http.post('/api/tasks/' + task._id , {offer: { offerDesc: vm.task.offerDesc, offerPrice: vm.task.offerPrice, user: vm.authentication.user, profileImageURL: vm.authentication.user.profileImageURL, displayName: vm.authentication.user.displayName } })
.then(successCallback, errorCallback);
alert('it worked!');
function successCallback(res) {
$state.transitionTo($state.current, $state.params, {
reload: true,
inherit: false,
notify: true
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
};
//close new offer function
you couldn't use same api for two post method. you can use different methods for same api like (post, get, put, delete ...) but not same method multiple time.
you should use different api for two post method .
like for task api
app.route('/api/tasks/:taskId').all(tasksPolicy.isAllowed)
.get(tasks.read)
.put(tasks.update)
.delete(tasks.delete)
.post(tasks.newCo);
for offer api
app.route('/api/offer/:taskId').all(tasksPolicy.isAllowed)
.post(tasks.newOffer);
if you use same api and two post method then always call first post method so second one always unreachable. When you call this api to add offer for that task then call tasks.newCo function and when you want receive req.body.comment get null or undefined so add empty or null comment but never will add offer.
I'm running a MEAN stack with PassportJS for authentication, and I'm having an issue with my signup module interacting with my Angular controller. Basically, the errorCallback is never called, and I'm not sure how to properly use the Passport done() implementation.
I have a basic signup form that upon submission, calls this request:
$http.post('/api/signup', {
name: $scope.user.name,
email: $scope.user.email,
password: $scope.user.password,
userSince: new Date().now
}).then(
function successCallback(res) {
$rootScope.message = 'Account Created';
console.log('Success'+res);
console.dir(res,{depth:5});
$location.url('/signupConf');
}, function errorCallback(res) {
$rootScope.message = 'Failure, see console';
console.log('Error: '+res);
console.dir(res,{depth:5});
$location.url('/');
});
With the express route:
app.post('/api/signup', passport.authenticate('local-signup'),function(req, res) {
console.log('User: ' + req.user.email);
});
And finally the Passport (adapted from a Scotch.io tut) module, abridged a little:
passport.use('local-signup', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log("Signup Request: "+email);
process.nextTick(function() {
User.findOne({ 'email' : email }, function(err, user) {
if (err) { return done(err); }
// check to see if theres already a user with that email
if (user) {
console.log("User not created, already exsists: "+user);
return done(err, false, {message: 'Username already exsists.'});
} else {
// if there is no user with that email
// create the user
var newUser = new User();
//a bunch of data creation here
newUser.save(function(err) {
if (err) {throw err;}
console.log("Sucessfully created: "+newUser);
return done(null, newUser);
});
}
});
});
}));
Everything runs fine, users are created corrected, and if a user with a given email exists, a new one isn't written over it. However, no matter what, successCallback is called. When a username already exist, I can see a 401 error in the browser console. When its a bad request (i.e. not all fields filled), a 400 error.
All the server side console.logs work fine, leading me to think there's something wrong in my angular frontend, or how the backend is responding to the request.
(Scotch.io tutorial credit: https://scotch.io/tutorials/easy-node-authentication-setup-and-local)
The problem was sort of staring me in the face, it was in my route handling.
app.post('/api/signup', function(req, res, next) {
passport.authenticate('local-signup', function(err,user,response) {
//handle responses based on state of user and err
})
(req, res, next);
});
Hi I want to support both formbased authentication and http basic authentication in my app. Everything works as expected except when I use form based auth via angularjs with wrong credentials.
Instead of having my angular code handle the 401, the browser shows the BASIC auth dialog, caused by the WWW-Authenticate header.
How can I prevent that header from being added when the local strategy is used?
Or how can I support both mechanisms in a different way?
I use the following route in my express based app.
api.post('/authenticate', passport.authenticate(['local', 'basic'], { session: false }), function (req, res) {
This enables both authentication methods on that url. I repeat, when I use wrong credentials using formbased it shows me the basic auth dialog (I don't want that).
Following is how I registered the strategies.
passport.use(new BasicStrategy({ realm: 'Authentication failed. Wrong username or password.'}, verifyLocalUser));
passport.use(new LocalStrategy(verifyLocalUser));
This is how my verifyUser method looks like...
var verifyLocalUser = function (username, password, next) {
User.findOne({
username: username
}).select('fullname admin username password').exec(function (err, user) {
if (err) {
return next(err);
}
if (user && user.comparePasswords(password)) {
return next(null, user);
} else {
next(null, false, { message: 'Authentication failed. Wrong username or password.' });
}
});
}
Does anyone know how to support multiple authentication methods using passport.js?
For completeness, this is the angular code which authenticates me...
authFactory.signIn = function (username, password) {
return $http.post('/api/authenticate', {
username: username,
password: password
}).then(function (res) {
AuthToken.setToken(res.data.token);
return res.data;
}, function (res) {
console.warn(res);
});
};
instead of this:
next(null, false, { message: 'Authentication failed. Wrong username or password.' });
You can use this:
cb(new YourCustomError())
And "YourCustomError" can have a message, for me mine "YourCustomError" looks like:
class HttpError extends Error {
constructor (msg = 'Invalid Request', status = 400) {
super(msg)
this.status = status
}
}
class Forbidden extends HttpError {
constructor (msg = 'Forbidden') {
super(msg, 403)
}
}
Or probably new Error(<message>) will work properly for you, too