Files
moonwell-web/app/Services/WebAccountPasswordService.php
2026-10-04 20:21:28 +04:00

58 lines
2.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use InvalidArgumentException;
class WebAccountPasswordService
{
private const LEGACY_PREFIX = 'acsrp1:';
public function __construct(private readonly AzerothCoreSrpService $srp) {}
public function legacyCredential(string $username, string $saltHex, string $verifierHex): string
{
$username = strtoupper(trim($username));
$saltHex = strtoupper($saltHex);
$verifierHex = strtoupper($verifierHex);
if (! preg_match('/^[A-Z0-9]{3,16}$/D', $username)
|| ! preg_match('/^[A-F0-9]{64}$/D', $saltHex)
|| ! preg_match('/^[A-F0-9]{64}$/D', $verifierHex)) {
throw new InvalidArgumentException('Invalid legacy website credential.');
}
return self::LEGACY_PREFIX.$username.':'.$saltHex.':'.$verifierHex;
}
public function matches(User $user, string $password): bool
{
$stored = (string) $user->password;
if (! str_starts_with($stored, self::LEGACY_PREFIX)) {
return Hash::check($password, $stored);
}
if (! preg_match('/^acsrp1:([A-Z0-9]{3,16}):([A-F0-9]{64}):([A-F0-9]{64})$/D', $stored, $parts)
|| ! $this->srp->credentialsMatch($parts[1], $password, $parts[2], $parts[3])) {
return false;
}
// Upgrade only the credential we just verified. A concurrent password
// change must not be overwritten or authenticate the old password.
$updated = DB::connection($user->getConnectionName())->table('users')
->where('id', $user->id)->where('password', $stored)
->update(['password' => Hash::make($password), 'updated_at' => now()]);
if ($updated > 0) {
return true;
}
$fresh = $user->fresh();
return $fresh !== null
&& ! str_starts_with((string) $fresh->password, self::LEGACY_PREFIX)
&& Hash::check($password, $fresh->password);
}
}