Skip to content

Commit c7d07b6

Browse files
authored
Merge pull request #3103 from strongloop/backport/preserve-current-access-token
Preserve current session when invalidating tokens
2 parents dc2b653 + afd6dd7 commit c7d07b6

File tree

3 files changed

+85
-18
lines changed

3 files changed

+85
-18
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: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,15 +1989,15 @@ describe('User', function() {
19891989
User.login(currentEmailCredentials, function(err, accessToken1) {
19901990
if (err) return next(err);
19911991
assert(accessToken1.userId);
1992-
originalUserToken1 = accessToken1.id;
1992+
originalUserToken1 = accessToken1;
19931993
next();
19941994
});
19951995
},
19961996
function secondLoginWithOriginalEmail(next) {
19971997
User.login(currentEmailCredentials, function(err, accessToken2) {
19981998
if (err) return next(err);
19991999
assert(accessToken2.userId);
2000-
originalUserToken2 = accessToken2.id;
2000+
originalUserToken2 = accessToken2;
20012001
next();
20022002
});
20032003
},
@@ -2057,7 +2057,7 @@ describe('User', function() {
20572057
it('keeps sessions AS IS if firstName is added using `updateAttributes`', function(done) {
20582058
user.updateAttributes({'firstName': 'Janny'}, function(err, userInstance) {
20592059
if (err) return done(err);
2060-
assertUntouchedTokens(done);
2060+
assertPreservedTokens(done);
20612061
});
20622062
});
20632063

@@ -2068,7 +2068,7 @@ describe('User', function() {
20682068
email: currentEmailCredentials.email,
20692069
}, function(err, userInstance) {
20702070
if (err) return done(err);
2071-
assertUntouchedTokens(done);
2071+
assertPreservedTokens(done);
20722072
});
20732073
});
20742074

@@ -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([
@@ -2303,9 +2316,11 @@ describe('User', function() {
23032316
function assertPreservedTokens(done) {
23042317
AccessToken.find({where: {userId: user.id}}, function(err, tokens) {
23052318
if (err) return done(err);
2306-
expect(tokens.length).to.equal(2);
2307-
expect([tokens[0].id, tokens[1].id]).to.have.members([originalUserToken1,
2308-
originalUserToken2]);
2319+
var actualIds = tokens.map(function(t) { return t.id; });
2320+
actualIds.sort();
2321+
var expectedIds = [originalUserToken1.id, originalUserToken2.id];
2322+
expectedIds.sort();
2323+
expect(actualIds).to.eql(expectedIds);
23092324
done();
23102325
});
23112326
}
@@ -2317,14 +2332,6 @@ describe('User', function() {
23172332
done();
23182333
});
23192334
}
2320-
2321-
function assertUntouchedTokens(done) {
2322-
AccessToken.find({where: {userId: user.id}}, function(err, tokens) {
2323-
if (err) return done(err);
2324-
expect(tokens.length).to.equal(2);
2325-
done();
2326-
});
2327-
}
23282335
});
23292336

23302337
describe('Verification after updating email', function() {

0 commit comments

Comments
 (0)