diff --git a/app/config/collections.php b/app/config/collections.php index bdc14d9105..16c0f32c4c 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1234,6 +1234,17 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('passwordHistory'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => ['json', 'encrypt'], + ], [ '$id' => ID::custom('password'), 'type' => Database::VAR_STRING, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index af5c8e4f53..4afa5a2090 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1516,6 +1516,13 @@ App::patch('/v1/account/password') throw new Exception(Exception::USER_INVALID_CREDENTIALS); } + $history = $user->getAttribute('passwordHistory', []); + $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + + if(in_array($newPassword, $history)) { + throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED, 'The password was recently used', 409); + } + $user = $dbForProject->updateDocument('users', $user->getId(), $user ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) ->setAttribute('hash', Auth::DEFAULT_ALGO) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index dce493b024..bd3278a60c 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -791,6 +791,13 @@ App::patch('/v1/users/:userId/password') throw new Exception(Exception::USER_NOT_FOUND); } + $history = $user->getAttribute('passwordHistory', []); + $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + + if(in_array($newPassword, $history)) { + throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED, 'The password was recently used', 409); + } + $user ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) ->setAttribute('hash', Auth::DEFAULT_ALGO) diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index d25cfb0d40..3940ad6da9 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -64,6 +64,7 @@ class Exception extends \Exception public const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited'; public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists'; public const USER_NOT_FOUND = 'user_not_found'; + public const USER_PASSWORD_RECENTLY_USED = 'password_recently_used'; public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists'; public const USER_PASSWORD_MISMATCH = 'user_password_mismatch'; public const USER_SESSION_NOT_FOUND = 'user_session_not_found';