Skip to content

Commit 7f82ac9

Browse files
committed
Update password on login
1 parent c86eb73 commit 7f82ac9

File tree

3 files changed

+120
-2
lines changed

3 files changed

+120
-2
lines changed

src/contracts/Repository/PasswordHashService.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,18 @@ public function updatePasswordHashTypeOnChange(): bool;
5656
* @return bool
5757
*/
5858
public function updatePasswordHashTypeOnLogin(): bool;
59+
60+
/**
61+
* Returns true if the password hash needs to be rehashed.
62+
*
63+
* This is used to determine if the password hash should be updated when the user logs in.
64+
* It will return true if the hash type of the existing password hash does not match the provided hash type,
65+
* or if the defaults for PHP's password hashing options have changed (e.g., cost factor).
66+
*
67+
* @param string $passwordHash The existing password hash
68+
* @param int $hashType The hash type to check against
69+
*
70+
* @return bool
71+
*/
72+
public function passwordNeedsRehash(string $passwordHash, int $hashType): bool;
5973
}

src/lib/Repository/User/PasswordHashService.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,39 @@ public function updatePasswordHashTypeOnLogin(): bool
118118
{
119119
return $this->configResolver->getParameter('password_hash.update_type_on_login');
120120
}
121+
122+
public function passwordNeedsRehash(
123+
#[\SensitiveParameter]
124+
string $passwordHash,
125+
int $hashType
126+
): bool
127+
{
128+
switch ($hashType) {
129+
case User::PASSWORD_HASH_BCRYPT:
130+
return password_needs_rehash($passwordHash, PASSWORD_BCRYPT);
131+
132+
case User::PASSWORD_HASH_PHP_DEFAULT:
133+
return password_needs_rehash($passwordHash, PASSWORD_DEFAULT);
134+
135+
case User::PASSWORD_HASH_INVALID:
136+
return false;
137+
138+
case User::PASSWORD_HASH_ARGON2I:
139+
if (!defined('PASSWORD_ARGON2I')) {
140+
throw new PasswordHashTypeNotCompiled('PASSWORD_ARGON2I');
141+
}
142+
143+
return password_needs_rehash($passwordHash, PASSWORD_ARGON2I);
144+
145+
case User::PASSWORD_HASH_ARGON2ID:
146+
if (!defined('PASSWORD_ARGON2ID')) {
147+
throw new PasswordHashTypeNotCompiled('PASSWORD_ARGON2ID');
148+
}
149+
150+
return password_needs_rehash($passwordHash, PASSWORD_ARGON2ID);
151+
152+
default:
153+
throw new UnsupportedPasswordHashType($hashType);
154+
}
155+
}
121156
}

src/lib/Repository/UserService.php

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,13 @@ protected function comparePasswordHashForSPIUser(
13541354
#[\SensitiveParameter]
13551355
string $password
13561356
): bool {
1357-
return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm);
1357+
$isValidPassword = $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm);
1358+
1359+
if ($this->passwordHashService->updatePasswordHashTypeOnLogin()) {
1360+
$this->updatePasswordHashIfNeeded($password, $user->passwordHash, $user->id, $user->login, $user->email);
1361+
}
1362+
1363+
return $isValidPassword;
13581364
}
13591365

13601366
/**
@@ -1367,7 +1373,70 @@ protected function comparePasswordHashForAPIUser(
13671373
#[\SensitiveParameter]
13681374
string $password
13691375
): bool {
1370-
return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm);
1376+
$isValidPassword = $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm);
1377+
1378+
if ($this->passwordHashService->updatePasswordHashTypeOnLogin()) {
1379+
$this->updatePasswordHashIfNeeded($password, $user->passwordHash, $user->id, $user->login, $user->email);
1380+
}
1381+
1382+
return $isValidPassword;
1383+
}
1384+
1385+
private function updatePasswordHashIfNeeded(
1386+
#[\SensitiveParameter]
1387+
string $password,
1388+
#[\SensitiveParameter]
1389+
string $passwordHash,
1390+
int $userId,
1391+
string $login,
1392+
string $email
1393+
): void
1394+
{
1395+
$defaultPasswordHashAlgorithm = $this->passwordHashService->getDefaultHashType();
1396+
if (!$this->passwordHashService->passwordNeedsRehash($passwordHash, $defaultPasswordHashAlgorithm)) {
1397+
return;
1398+
}
1399+
1400+
try {
1401+
$newPasswordHash = $this->passwordHashService->createPasswordHash(
1402+
$password,
1403+
$defaultPasswordHashAlgorithm
1404+
);
1405+
} catch (Exception $e) {
1406+
if (isset($this->logger)) {
1407+
$this->logger->log(LogLevel::ERROR, $e->getMessage(), [
1408+
'exception' => $e,
1409+
]);
1410+
}
1411+
1412+
return;
1413+
}
1414+
1415+
$this->repository->beginTransaction();
1416+
try {
1417+
$this->userHandler->updatePassword(
1418+
new SPIUser(
1419+
[
1420+
'id' => $userId,
1421+
'login' => $login,
1422+
'email' => $email,
1423+
'passwordHash' => $newPasswordHash,
1424+
'hashAlgorithm' => $defaultPasswordHashAlgorithm,
1425+
'passwordUpdatedAt' => time(),
1426+
]
1427+
)
1428+
);
1429+
1430+
$this->repository->commit();
1431+
} catch (Exception $e) {
1432+
$this->repository->rollback();
1433+
1434+
if (isset($this->logger)) {
1435+
$this->logger->log(LogLevel::ERROR, $e->getMessage(), [
1436+
'exception' => $e,
1437+
]);
1438+
}
1439+
}
13711440
}
13721441

13731442
/**

0 commit comments

Comments
 (0)