unify totp check
the totp check is done in several places causing errors like 3552232e99
* ldap (addon)
* accesscontrol (dashboard)
* proxyauth
* directoryserver (exposed ldap)
* externalldap (the connector)
The code also makes externalldap auto-create work now across all the cases where there is a username
This commit is contained in:
40
src/users.js
40
src/users.js
@@ -331,10 +331,11 @@ async function verifyAppPassword(userId, password, identifier) {
|
||||
}
|
||||
|
||||
// identifier is only used to check if password is valid for a specific app
|
||||
async function verify(userId, password, identifier) {
|
||||
async function verify(userId, password, identifier, options) {
|
||||
assert.strictEqual(typeof userId, 'string');
|
||||
assert.strictEqual(typeof password, 'string');
|
||||
assert.strictEqual(typeof identifier, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const user = await get(userId);
|
||||
if (!user) throw new BoxError(BoxError.NOT_FOUND, 'User not found');
|
||||
@@ -356,42 +357,61 @@ async function verify(userId, password, identifier) {
|
||||
return user;
|
||||
}
|
||||
|
||||
const relaxedTotpCheck = !!options.relaxedTotpCheck; // will enforce totp only if totpToken is valid
|
||||
const totpToken = options.totpToken || null;
|
||||
|
||||
if (user.source === 'ldap') {
|
||||
const ldapUser = await externalLdap.verifyPassword(user, password);
|
||||
// currently we store twoFactorAuthenticationEnabled in the db as local so amend it to user object
|
||||
user.twoFactorAuthenticationEnabled = !!ldapUser.twoFactorAuthenticationEnabled;
|
||||
await externalLdap.verifyPassword(user, password, totpToken);
|
||||
} else {
|
||||
const saltBinary = Buffer.from(user.salt, 'hex');
|
||||
const [error, derivedKey] = await safe(pbkdf2Async(password, saltBinary, CRYPTO_ITERATIONS, CRYPTO_KEY_LENGTH, CRYPTO_DIGEST));
|
||||
if (error) throw new BoxError(BoxError.CRYPTO_ERROR, error);
|
||||
|
||||
const derivedKeyHex = Buffer.from(derivedKey, 'binary').toString('hex');
|
||||
if (derivedKeyHex !== user.password) throw new BoxError(BoxError.INVALID_CREDENTIALS);
|
||||
if (derivedKeyHex !== user.password) throw new BoxError(BoxError.INVALID_CREDENTIALS, 'Username and password does not match');
|
||||
|
||||
if (user.twoFactorAuthenticationEnabled) {
|
||||
if (totpToken) {
|
||||
const verified = speakeasy.totp.verify({ secret: user.twoFactorAuthenticationSecret, encoding: 'base32', token: totpToken, window: 2 });
|
||||
if (!verified) throw new BoxError(BoxError.INVALID_CREDENTIALS, 'Invalid totpToken');
|
||||
} else if (!relaxedTotpCheck) {
|
||||
throw new BoxError(BoxError.INVALID_CREDENTIALS, 'A totpToken must be provided');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function verifyWithUsername(username, password, identifier) {
|
||||
async function verifyWithUsername(username, password, identifier, options) {
|
||||
assert.strictEqual(typeof username, 'string');
|
||||
assert.strictEqual(typeof password, 'string');
|
||||
assert.strictEqual(typeof identifier, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const user = await getByUsername(username.toLowerCase());
|
||||
if (!user) throw new BoxError(BoxError.NOT_FOUND, 'User not found');
|
||||
if (user) return await verify(user.id, password, identifier, options);
|
||||
|
||||
return await verify(user.id, password, identifier);
|
||||
const [error, newUserId] = await safe(externalLdap.maybeCreateUser(username.toLowerCase()));
|
||||
if (error && error.reason === BoxError.BAD_STATE) throw new BoxError(BoxError.NOT_FOUND, 'User not found'); // no external ldap or no auto create
|
||||
if (error) {
|
||||
debug(`verifyWithUsername: failed to auto create user ${username}`, error);
|
||||
throw new BoxError(BoxError.NOT_FOUND, 'User not found');
|
||||
}
|
||||
|
||||
return await verify(newUserId, password, identifier, options);
|
||||
}
|
||||
|
||||
async function verifyWithEmail(email, password, identifier) {
|
||||
async function verifyWithEmail(email, password, identifier, options) {
|
||||
assert.strictEqual(typeof email, 'string');
|
||||
assert.strictEqual(typeof password, 'string');
|
||||
assert.strictEqual(typeof identifier, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const user = await getByEmail(email.toLowerCase());
|
||||
if (!user) throw new BoxError(BoxError.NOT_FOUND, 'User not found');
|
||||
|
||||
return await verify(user.id, password, identifier);
|
||||
return await verify(user.id, password, identifier, options);
|
||||
}
|
||||
|
||||
async function del(user, auditSource) {
|
||||
|
||||
Reference in New Issue
Block a user