fix: Invalidate access tokens on password change

pull/279/head
Simon Tagne 3 years ago
parent e9a65bb641
commit 266a762641
No known key found for this signature in database
GPG Key ID: 1567DE3ADB69D589

@ -5,6 +5,7 @@ import request from '../request';
import selectors from '../../../selectors'; import selectors from '../../../selectors';
import actions from '../../../actions'; import actions from '../../../actions';
import api from '../../../api'; import api from '../../../api';
import { setAccessToken } from '../../../utils/access-token-storage';
export function* createUser(data) { export function* createUser(data) {
yield put(actions.createUser(data)); yield put(actions.createUser(data));
@ -109,13 +110,18 @@ export function* updateUserPassword(id, data) {
yield put(actions.updateUserPassword(id, data)); yield put(actions.updateUserPassword(id, data));
let user; let user;
let accessToken;
try { try {
({ item: user } = yield call(request, api.updateUserPassword, id, data)); ({ item: user, accessToken } = yield call(request, api.updateUserPassword, id, data));
} catch (error) { } catch (error) {
yield put(actions.updateUserPassword.failure(id, error)); yield put(actions.updateUserPassword.failure(id, error));
return; return;
} }
if (accessToken !== undefined) {
yield call(setAccessToken, accessToken);
}
yield put(actions.updateUserPassword.success(user)); yield put(actions.updateUserPassword.success(user));
} }

@ -1,6 +1,7 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Config from '../constants/Config'; import Config from '../constants/Config';
import socket from '../api/socket';
export const setAccessToken = (accessToken) => { export const setAccessToken = (accessToken) => {
Cookies.set(Config.ACCESS_TOKEN_KEY, accessToken, { Cookies.set(Config.ACCESS_TOKEN_KEY, accessToken, {
@ -11,6 +12,8 @@ export const setAccessToken = (accessToken) => {
Cookies.set(Config.ACCESS_TOKEN_VERSION_KEY, Config.ACCESS_TOKEN_VERSION, { Cookies.set(Config.ACCESS_TOKEN_VERSION_KEY, Config.ACCESS_TOKEN_VERSION, {
expires: Config.ACCESS_TOKEN_EXPIRES, expires: Config.ACCESS_TOKEN_EXPIRES,
}); });
socket.headers = { Cookie: document.cookie };
}; };
export const getAccessToken = () => { export const getAccessToken = () => {

@ -66,6 +66,27 @@ module.exports = {
throw Errors.USER_NOT_FOUND; throw Errors.USER_NOT_FOUND;
} }
// Disconnect all sockets from this user except the current one
const tempRoom = `temp:${user.id}`;
sails.sockets.addRoomMembersToRooms(`user:${user.id}`, tempRoom, () => {
if (currentUser.id === user.id && this.req.isSocket) {
sails.sockets.leave(this.req, tempRoom, () => {
sails.sockets.leaveAll(tempRoom);
});
} else {
sails.sockets.leaveAll(tempRoom);
}
});
if (currentUser.id === user.id) {
const accessToken = sails.helpers.utils.signToken(user.id);
return {
accessToken,
item: user,
};
}
return { return {
item: user, item: user,
}; };

@ -56,6 +56,8 @@ module.exports = {
if (!_.isUndefined(inputs.values.password)) { if (!_.isUndefined(inputs.values.password)) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
inputs.values.password = bcrypt.hashSync(inputs.values.password, 10); inputs.values.password = bcrypt.hashSync(inputs.values.password, 10);
// eslint-disable-next-line no-param-reassign
inputs.values.passwordChangedAt = new Date();
if (Object.keys(inputs.values).length === 1) { if (Object.keys(inputs.values).length === 1) {
isOnlyPasswordChange = true; isOnlyPasswordChange = true;

@ -11,6 +11,6 @@ module.exports = {
}, },
fn(inputs) { fn(inputs) {
return jwt.sign(inputs.payload, sails.config.session.secret); return jwt.sign({ sub: inputs.payload }, sails.config.session.secret);
}, },
}; };

@ -11,14 +11,27 @@ module.exports = function defineCurrentUserHook(sails) {
const getUser = async (accessToken) => { const getUser = async (accessToken) => {
let id; let id;
let iat;
let decodedToken;
try { try {
id = sails.helpers.utils.verifyToken(accessToken); decodedToken = sails.helpers.utils.verifyToken(accessToken);
} catch (error) { } catch (error) {
return null; return null;
} }
return sails.helpers.users.getOne(id); if (_.isString(decodedToken)) {
id = decodedToken;
iat = 1;
} else {
id = decodedToken.sub;
iat = decodedToken.iat;
}
return sails.helpers.users.getOne({
id,
passwordChangedAt: { '<=': new Date((iat + 1) * 1000) },
});
}; };
return { return {

@ -67,6 +67,11 @@ module.exports = {
type: 'ref', type: 'ref',
columnName: 'deleted_at', columnName: 'deleted_at',
}, },
passwordChangedAt: {
type: 'ref',
columnName: 'password_changed_at',
defaultsTo: new Date(0).toUTCString(),
},
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
// ║╣ ║║║╠╩╗║╣ ║║╚═╗ // ║╣ ║║║╠╩╗║╣ ║║╚═╗

@ -0,0 +1,11 @@
module.exports.up = async (knex) =>
knex.schema.table('user_account', (table) => {
/* Columns */
table.timestamp('password_changed_at', true).defaultsTo(new Date(0).toUTCString());
});
module.exports.down = async (knex) =>
knex.schema.table('user_account', (table) => {
table.dropColumn('password_changed_at');
});
Loading…
Cancel
Save