Skip to content

Commit afd6dd7

Browse files
committed
Preserve current session when invalidating tokens
Fix User model to preserve the current session (provided via "options.accessToken") when invalidating access tokens after a change of email or password property.
1 parent f8b013d commit afd6dd7

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

common/models/user.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,12 @@ module.exports = function(User) {
647647
throw err;
648648
};
649649

650-
User._invalidateAccessTokensOfUsers = function(userIds, cb) {
650+
User._invalidateAccessTokensOfUsers = function(userIds, options, cb) {
651+
if (typeof options === 'function' && cb === undefined) {
652+
cb = options;
653+
options = {};
654+
}
655+
651656
if (!Array.isArray(userIds) || !userIds.length)
652657
return process.nextTick(cb);
653658

@@ -656,7 +661,14 @@ module.exports = function(User) {
656661
return process.nextTick(cb);
657662

658663
var AccessToken = accessTokenRelation.modelTo;
659-
AccessToken.deleteAll({userId: {inq: userIds}}, cb);
664+
665+
var query = {userId: {inq: userIds}};
666+
var tokenPK = AccessToken.definition.idName() || 'id';
667+
if (options.accessToken && tokenPK in options.accessToken) {
668+
query[tokenPK] = {neq: options.accessToken[tokenPK]};
669+
}
670+
671+
AccessToken.deleteAll(query, options, cb);
660672
};
661673

662674
/*!
@@ -873,7 +885,7 @@ module.exports = function(User) {
873885
}).map(function(u) {
874886
return u.id;
875887
});
876-
ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, next);
888+
ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, ctx.options, next);
877889
});
878890
};
879891

test/user.integration.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,54 @@ describe('users - integration', function() {
100100
done();
101101
});
102102
});
103+
104+
it('invalidates current session when options are not injected', function(done) {
105+
// "injectOptionsFromRemoteContext" is disabled by default,
106+
// therefore the code invalidating sessions cannot tell what
107+
// is the current session, and thus invalidates all sessions
108+
var url = '/api/users/' + userId;
109+
var self = this;
110+
this.request.patch(url)
111+
.send({email: '[email protected]'})
112+
.set('Authorization', accessToken)
113+
.expect(200, function(err) {
114+
if (err) return done(err);
115+
self.get(url)
116+
.set('Authorization', accessToken)
117+
.expect(401, done);
118+
});
119+
});
120+
121+
it('should preserve current session when invalidating tokens', function(done) {
122+
var UserWithContext = app.registry.createModel({
123+
name: 'UserWithContext',
124+
plural: 'ctx-users',
125+
base: 'User',
126+
injectOptionsFromRemoteContext: true
127+
});
128+
app.model(UserWithContext, {dataSource: 'db'});
129+
130+
var self = this;
131+
var CREDENTIALS = {email: '[email protected]', password: 'pass'};
132+
UserWithContext.create(CREDENTIALS, function(err, user) {
133+
if (err) return done(err);
134+
135+
UserWithContext.login(CREDENTIALS, function(err, token) {
136+
if (err) return done(err);
137+
138+
var url = '/api/ctx-users/' + user.id;
139+
self.request.patch(url)
140+
.send({email: '[email protected]'})
141+
.set('Authorization', token.id)
142+
.expect(200, function(err) {
143+
if (err) return done(err);
144+
self.get(url)
145+
.set('Authorization', token.id)
146+
.expect(200, done);
147+
});
148+
});
149+
});
150+
});
103151
});
104152

105153
describe('sub-user', function() {

test/user.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,19 @@ describe('User', function() {
22522252
});
22532253
});
22542254

2255+
it('preserves current session', function(done) {
2256+
var options = {accessToken: originalUserToken1};
2257+
user.updateAttribute('email', '[email protected]', options, function(err) {
2258+
if (err) return done(err);
2259+
AccessToken.find({where: {userId: user.id}}, function(err, tokens) {
2260+
if (err) return done(err);
2261+
var tokenIds = tokens.map(function(t) { return t.id; });
2262+
expect(tokenIds).to.eql([originalUserToken1.id]);
2263+
done();
2264+
});
2265+
});
2266+
});
2267+
22552268
it('preserves other user sessions if their password is untouched', function(done) {
22562269
var user1, user2, user1Token;
22572270
async.series([

0 commit comments

Comments
 (0)