Skip to content

Commit 5200b28

Browse files
authored
Merge pull request #3021 from strongloop/fix/session-expiry-2x
Invalidate AccessTokens on password change
2 parents 66e4e5b + 4ee086d commit 5200b28

File tree

2 files changed

+311
-274
lines changed

2 files changed

+311
-274
lines changed

common/models/user.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,19 @@ module.exports = function(User) {
645645
err.statusCode = 422;
646646
throw err;
647647
};
648+
649+
User._invalidateAccessTokensOfUsers = function(userIds, cb) {
650+
if (!Array.isArray(userIds) || !userIds.length)
651+
return process.nextTick(cb);
652+
653+
var accessTokenRelation = this.relations.accessTokens;
654+
if (!accessTokenRelation)
655+
return process.nextTick(cb);
656+
657+
var AccessToken = accessTokenRelation.modelTo;
658+
AccessToken.deleteAll({userId: {inq: userIds}}, cb);
659+
};
660+
648661
/*!
649662
* Setup an extended user model.
650663
*/
@@ -809,8 +822,20 @@ module.exports = function(User) {
809822
var emailChanged;
810823
if (ctx.isNewInstance) return next();
811824
if (!ctx.where && !ctx.instance) return next();
812-
var where = ctx.where || { id: ctx.instance.id };
813-
ctx.Model.find({ where: where }, function(err, userInstances) {
825+
826+
var isPartialUpdateChangingPassword = ctx.data && 'password' in ctx.data;
827+
828+
// Full replace of User instance => assume password change.
829+
// HashPassword returns a different value for each invocation,
830+
// therefore we cannot tell whether ctx.instance.password is the same
831+
// or not.
832+
var isFullReplaceChangingPassword = !!ctx.instance;
833+
834+
ctx.hookState.isPasswordChange = isPartialUpdateChangingPassword ||
835+
isFullReplaceChangingPassword;
836+
837+
var where = ctx.where || {id: ctx.instance.id};
838+
ctx.Model.find({where: where}, function(err, userInstances) {
814839
if (err) return next(err);
815840
ctx.hookState.originalUserData = userInstances.map(function(u) {
816841
return { id: u.id, email: u.email };
@@ -828,24 +853,26 @@ module.exports = function(User) {
828853
ctx.data.emailVerified = false;
829854
}
830855
}
856+
831857
next();
832858
});
833859
});
834860

835861
User.observe('after save', function afterEmailUpdate(ctx, next) {
836-
if (!ctx.Model.relations.accessTokens) return next();
837-
var AccessToken = ctx.Model.relations.accessTokens.modelTo;
838862
if (!ctx.instance && !ctx.data) return next();
839-
var newEmail = (ctx.instance || ctx.data).email;
840-
if (!newEmail) return next();
841863
if (!ctx.hookState.originalUserData) return next();
842-
var idsToExpire = ctx.hookState.originalUserData.filter(function(u) {
843-
return u.email !== newEmail;
864+
865+
var newEmail = (ctx.instance || ctx.data).email;
866+
var isPasswordChange = ctx.hookState.isPasswordChange;
867+
868+
if (!newEmail && !isPasswordChange) return next();
869+
870+
var userIdsToExpire = ctx.hookState.originalUserData.filter(function(u) {
871+
return (newEmail && u.email !== newEmail) || isPasswordChange;
844872
}).map(function(u) {
845873
return u.id;
846874
});
847-
if (!idsToExpire.length) return next();
848-
AccessToken.deleteAll({ userId: { inq: idsToExpire }}, next);
875+
ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, next);
849876
});
850877
};
851878

0 commit comments

Comments
 (0)