sails-auth
sails-auth copied to clipboard
Local strategy update password
I am trying to update password ,is there any guide or documentation ?
Good question; I'm not sure that this usecase is supported currently. I'll look into it.
Has there been any progress with this, could really do with being able to update passwords.
I added the afterValidate lifecycle callback to my User model to perform the password update when a password is passed in:
afterValidate: function(updates, next) {
// Update the passport password if it was passed in
if(updates.password && this.user && this.user.id) {
Passport.update({user: this.user.id}, {password: updates.password}).exec(function(err, passport) {
next(err);
});
}
else {
next();
}
}
It's probably not the best implementation but it works. You'll also need to add a policy for the UserController to allow the update action:
UserController: {
create: true, // Allow anyone to register a new user
update: [ // Regular permissions for updates
'basicAuth',
'passport',
'sessionAuth',
'ModelPolicy',
'AuditPolicy',
'OwnerPolicy',
'PermissionPolicy',
'RolePolicy',
'CriteriaPolicy'
]
}
@GeekLad I took what you have and changed just a few things. This just makes sure that it sets the protocol as local (incase a user has multiple passports, but they can only have one local passport) and then made sure to delete the password after it's been updated so that it doesn't make it to the update lifecycle.
afterValidate: [
function updatePassword(values, next) {
// Update the passport password if it was passed in
if(values.password && this.user && this.user.id) {
Passport.update({user: this.user.id, protocol: 'local'}, {password: values.password})
.exec(function(err, passport) {
delete values.password;
next(err);
});
}
else {
next();
}
}
],
Even better, thanks!
On Mon, Nov 16, 2015, 6:47 PM Scott B. Wyatt [email protected] wrote:
@GeekLad https://github.com/GeekLad I took what you have an changed just a few things. This just makes sure that it sets the protocol as local (incase a user has multiple passports, but they can only have one local passport) and then made sure to delete the password after it's been updated so that it doesn't make it to the update lifecycle.
afterValidate: [ function updatePassword(values, next) { // Update the passport password if it was passed in if(values.password && this.user && this.user.id) { Passport.update({user: this.user.id, protocol: 'local'}, {password: values.password}) .exec(function(err, passport) { delete values.password; next(err); }); } else { next(); } } ],
— Reply to this email directly or view it on GitHub https://github.com/tjwebb/sails-auth/issues/52#issuecomment-157211378.
@scott-wyatt For some reason, this no longer works for me and I have no idea what I did to break it. When I run in debug mode and inspect the afterValidate
call, this
is assigned to the global
object instead of the User
instance that is being updated. Any idea what may be causing this?
@GeekLad, hmmm, I just ran test on it, and it is working perfectly in my use case... I always place my lifecycle callbacks in an array so that they can be merged with imported models. What version of sails are you using?
@scott-wyatt v0.11.2
@GeekLad just curious, if you change it back to what you had before does it work for you? Also, could you perhaps paste some of your User.js file here? I'd be happy to take a look. Mine is going to likely be a lot different than yours because I port generous portions of sails-auth and sails-permissions (thanks @tjwebb ) over to a hook called humpback-hook. Which has this functionality as well as an admin password reset.
@scott-wyatt I'm not really sure what changes I made that broke it. Here's what I have in User.js right now:
// api/models/User.js
var _ = require('lodash');
var _super = require('sails-permissions/api/models/User');
var bcrypt = require('sails-auth/node_modules/bcryptjs');
var usermail = require('usermail');
_.merge(exports, _super);
_.merge(exports, {
// Extend with custom logic here by adding additional fields, methods, etc.
attributes: {
username: {required: true},
email: {required: true},
email_validated: {
type: 'boolean',
required: true,
defaultsTo: false
},
validation_token: {
type: 'string',
required: false,
unique: true
},
roles: {
collection: 'Role'
},
// Remove stuff we don't want the user to see from the output
toJSON: function() {
delete this.validation_token;
return this;
}
},
// Update the passport password if it was passed in
afterValidate: function(values, next) {
// Update the passport password if it was passed in
if(values.password && values.id) {
Passport
.update({user: values.id, protocol: 'local'}, {password: values.password})
.exec(function(err, passport) {
delete values.password;
next(err);
});
}
else {
next();
}
},
// Before creating a new user, create the token
beforeCreate: function(values, next) {
values.validation_token = bcrypt.hashSync(String(new Date().getTime()));
console.log('Validation token for '+values.email+':');
console.log(values.validation_token);
next();
},
// Before an update, if a token was passed in, create a new one
beforeUpdate: function(values, next) {
if(values.new_token) {
values.validation_token = bcrypt.hashSync(String(new Date().getTime()));
console.log('New validation token:');
console.log(values.validation_token);
delete values.new_token;
}
next();
},
afterCreate: _.merge(_super.afterCreate, [
// Email the user the validation link after creation
function emailValidationLink(user, next) {
if(user.email != "[email protected]") {
usermail(user, {
validate_url: sails.getBaseurl()+"/validate-email?email="+user.email+"&validation_token="+user.validation_token,
subject: "Validate Email on "+sails.config.appName,
template: 'validate_email'
});
}
next();
}
])
});
I put some stuff in there for an email validation token, so that an email can be sent to the user with a link for them to verify their email address. To fix the problem I'm having with the this
object, I'm passing in the user ID in as part of the request, so that's why you see me assign the user ID from what's passed in through the request.
On a side note, do you know of a better way to generate a random token other than the way I'm performing a bcrypt hash on the system time?
@GeekLad for tokens first I consider if it would better be done emulating Basic Auth (base encoding the email address and password), that way I don't need to set or store it. Sometimes an actual token is needed in addition to Basic Auth, for that I like shortid because you can make unique and url friendly tokens. At least, that is my logic lol. I do try to avoid time only based tokens, because at scale it's possible for it to be duplicated.
As for the code above, I don't see anything wrong with it... You might try writing a unit test for each of the model methods and that may narrow it down...?
Pull requested in #135. If there is anyone interested and not merged here for "any reason" could check it here
This issue should be closed. #135 is merged and works. Documentation could be better though.
A user with permission to update users should be able to POST {password: 'newPassword'}
to /user/:userId
and it should update the user's password.