Separate invite and password reset token

This commit is contained in:
Johannes Zellner
2021-10-01 12:27:22 +02:00
parent c7b668b3a4
commit cb31e5ae8b
4 changed files with 37 additions and 15 deletions

View File

@@ -11,6 +11,7 @@ exports = module.exports = {
list,
listPaged,
get,
getByInviteToken,
getByResetToken,
getByUsername,
getByEmail,
@@ -57,7 +58,7 @@ exports = module.exports = {
const ORDERED_ROLES = [ exports.ROLE_USER, exports.ROLE_USER_MANAGER, exports.ROLE_ADMIN, exports.ROLE_OWNER ];
// the avatar field is special and not added here to reduce response sizes
const USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'creationTime', 'resetToken', 'displayName',
const USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'creationTime', 'inviteToken', 'resetToken', 'displayName',
'twoFactorAuthenticationEnabled', 'twoFactorAuthenticationSecret', 'active', 'source', 'role', 'resetTokenCreationTime', 'loginLocationsJson' ].join(',');
const DEFAULT_GHOST_LIFETIME = 6 * 60 * 60 * 1000; // 6 hours
@@ -133,7 +134,7 @@ function validateEmail(email) {
return null;
}
function validateResetToken(token) {
function validateToken(token) {
assert.strictEqual(typeof token, 'string');
if (token.length !== 64) return new BoxError(BoxError.BAD_FIELD, 'Invalid token'); // 256-bit hex coded token
@@ -220,6 +221,7 @@ async function add(email, data, auditSource) {
password: Buffer.from(derivedKey, 'binary').toString('hex'),
salt: salt.toString('hex'),
resetToken: '',
inviteToken: '',
displayName: displayName,
source: source,
role: role,
@@ -477,7 +479,7 @@ async function getByRole(role) {
async function getByResetToken(resetToken) {
assert.strictEqual(typeof resetToken, 'string');
let error = validateResetToken(resetToken);
let error = validateToken(resetToken);
if (error) throw error;
const result = await database.query(`SELECT ${USERS_FIELDS} FROM users WHERE resetToken=?`, [ resetToken ]);
@@ -486,6 +488,18 @@ async function getByResetToken(resetToken) {
return postProcess(result[0]);
}
async function getByInviteToken(inviteToken) {
assert.strictEqual(typeof inviteToken, 'string');
let error = validateToken(inviteToken);
if (error) throw error;
const result = await database.query(`SELECT ${USERS_FIELDS} FROM users WHERE inviteToken=?`, [ inviteToken ]);
if (result.length === 0) return null;
return postProcess(result[0]);
}
async function getByUsername(username) {
assert.strictEqual(typeof username, 'string');
@@ -691,15 +705,13 @@ async function sendInvite(user, options, auditSource) {
if (user.source) throw new BoxError(BoxError.CONFLICT, 'User is from an external directory');
const resetToken = hat(256);
const resetTokenCreationTime = new Date();
const inviteToken = hat(256);
user.resetToken = resetToken;
user.resetTokenCreationTime = resetTokenCreationTime;
await update(user, { resetToken, resetTokenCreationTime }, auditSource);
user.inviteToken = inviteToken;
await update(user, { inviteToken }, auditSource);
const directoryConfig = await settings.getDirectoryConfig();
let inviteLink = `${settings.dashboardOrigin()}/setupaccount.html?resetToken=${user.resetToken}&email=${encodeURIComponent(user.email)}`;
let inviteLink = `${settings.dashboardOrigin()}/setupaccount.html?inviteToken=${user.inviteToken}&email=${encodeURIComponent(user.email)}`;
if (user.username) inviteLink += `&username=${encodeURIComponent(user.username)}`;
if (user.displayName) inviteLink += `&displayName=${encodeURIComponent(user.displayName)}`;
@@ -718,9 +730,9 @@ async function setupAccount(user, data, auditSource) {
const directoryConfig = await settings.getDirectoryConfig();
if (directoryConfig.lockUserProfiles) return;
await update(user, _.pick(data, 'username', 'displayName'), auditSource);
await update(user, { username: data.username, displayName: data.displayName, inviteToken: '' }, auditSource);
await setPassword(user, data.password, auditSource); // setPassword clears the resetToken
await setPassword(user, data.password, auditSource);
const token = { clientId: tokens.ID_WEBADMIN, identifier: user.id, expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS };
const result = await tokens.add(token);