Merge pull request #10394 from overleaf/ii-password-reset-and-strength-checking
[web] Password reset strength checking and UI updates GitOrigin-RevId: 442a5c9e7e9d0a61d3ae649f3526bc3c02fd5704
This commit is contained in:
@@ -4,10 +4,12 @@ class InvalidEmailError extends Errors.BackwardCompatibleError {}
|
||||
class InvalidPasswordError extends Errors.BackwardCompatibleError {}
|
||||
class ParallelLoginError extends Errors.BackwardCompatibleError {}
|
||||
class PasswordMustBeDifferentError extends Errors.BackwardCompatibleError {}
|
||||
class PasswordReusedError extends Errors.BackwardCompatibleError {}
|
||||
|
||||
module.exports = {
|
||||
InvalidEmailError,
|
||||
InvalidPasswordError,
|
||||
ParallelLoginError,
|
||||
PasswordMustBeDifferentError,
|
||||
PasswordReusedError,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const {
|
||||
InvalidPasswordError,
|
||||
ParallelLoginError,
|
||||
PasswordMustBeDifferentError,
|
||||
PasswordReusedError,
|
||||
} = require('./AuthenticationErrors')
|
||||
const util = require('util')
|
||||
const HaveIBeenPwned = require('./HaveIBeenPwned')
|
||||
@@ -256,8 +257,22 @@ const AuthenticationManager = {
|
||||
if (match) {
|
||||
return callback(new PasswordMustBeDifferentError())
|
||||
}
|
||||
this._setUserPasswordInMongo(user, password, callback)
|
||||
HaveIBeenPwned.checkPasswordForReuseInBackground(password)
|
||||
|
||||
HaveIBeenPwned.checkPasswordForReuse(
|
||||
password,
|
||||
(error, isPasswordReused) => {
|
||||
if (error) {
|
||||
logger.err({ error }, 'cannot check password for re-use')
|
||||
}
|
||||
|
||||
if (!error && isPasswordReused) {
|
||||
return callback(new PasswordReusedError())
|
||||
}
|
||||
|
||||
// password is strong enough or the validation with the service did not happen
|
||||
this._setUserPasswordInMongo(user, password, callback)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
happy path or via an error (message or attributes).
|
||||
*/
|
||||
|
||||
const { callbackify } = require('util')
|
||||
const fetch = require('node-fetch')
|
||||
const crypto = require('crypto')
|
||||
const Settings = require('@overleaf/settings')
|
||||
@@ -89,29 +90,43 @@ async function isPasswordReused(password) {
|
||||
return score > 0
|
||||
}
|
||||
|
||||
function checkPasswordForReuseInBackground(password) {
|
||||
async function checkPasswordForReuse(password) {
|
||||
if (!Settings.apis.haveIBeenPwned.enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
isPasswordReused(password)
|
||||
.then(isReused => {
|
||||
Metrics.inc('password_re_use', {
|
||||
status: isReused ? 're-used' : 'unique',
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
// Make sure we do not leak any password details.
|
||||
if (!CODED_ERROR_MESSAGES.includes(err.message)) {
|
||||
err = new Error('hidden message')
|
||||
}
|
||||
err = new Error(err.message)
|
||||
try {
|
||||
const isReused = await isPasswordReused(password)
|
||||
|
||||
logger.err({ err }, 'cannot check password for re-use')
|
||||
Metrics.inc('password_re_use', { status: 'failure' })
|
||||
Metrics.inc('password_re_use', {
|
||||
status: isReused ? 're-used' : 'unique',
|
||||
})
|
||||
|
||||
return isReused
|
||||
} catch (err) {
|
||||
let error = err
|
||||
// Make sure we do not leak any password details.
|
||||
if (!CODED_ERROR_MESSAGES.includes(err.message)) {
|
||||
error = new Error('hidden message')
|
||||
}
|
||||
error = new Error(error.message)
|
||||
|
||||
Metrics.inc('password_re_use', { status: 'failure' })
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
function checkPasswordForReuseInBackground(password) {
|
||||
checkPasswordForReuse(password).catch(error => {
|
||||
logger.err({ error }, 'cannot check password for re-use')
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkPasswordForReuse: callbackify(checkPasswordForReuse),
|
||||
checkPasswordForReuseInBackground,
|
||||
promises: {
|
||||
checkPasswordForReuse,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -84,6 +84,12 @@ async function setNewUserPassword(req, res, next) {
|
||||
key: 'password-must-be-different',
|
||||
},
|
||||
})
|
||||
} else if (error.name === 'PasswordReusedError') {
|
||||
return res.status(400).json({
|
||||
message: {
|
||||
key: 'password-must-be-strong',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return res.status(500).json({
|
||||
message: req.i18n.translate('error_performing_request'),
|
||||
|
||||
@@ -103,6 +103,12 @@ async function changePassword(req, res, next) {
|
||||
res,
|
||||
req.i18n.translate('password_change_password_must_be_different')
|
||||
)
|
||||
} else if (error.name === 'PasswordReusedError') {
|
||||
return res.status(400).json({
|
||||
message: {
|
||||
key: 'password-must-be-strong',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user