Files
moonwell-web/app/Services/LauncherGameTicketService.php
2026-10-03 14:33:08 +04:00

51 lines
2.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\GameAccount;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\DB;
class LauncherGameTicketService
{
private const string ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
public function __construct(private readonly VMangosSrpService $srp) {}
/** @return array{account: string, ticket: string, expires_at: string} */
public function issue(GameAccount $account, string $launcherSessionToken, int $clientBuild): array
{
$username = $this->srp->normalizeUsername((string) $account->username);
if (preg_match('/^[A-Z0-9]{3,16}$/D', $username) !== 1) {
throw new \RuntimeException('Account name is incompatible with the 1.12.1 client.');
}
$ticket = '';
for ($i = 0; $i < 16; $i++) {
$ticket .= self::ALPHABET[random_int(0, strlen(self::ALPHABET) - 1)];
}
[$saltHex, $verifierHex] = $this->srp->makeRegistrationData($username, $ticket);
$now = CarbonImmutable::now('UTC');
$expires = $now->addSeconds(max(5, min(60, (int) config('moonwell.launcher.game_ticket_ttl'))));
DB::connection(config('moonwell.auth_connection'))->table('launcher_ticket')->upsert([[
'account_id' => (int) $account->id,
'generation_id' => random_bytes(16),
'srp_salt' => hex2bin($saltHex),
'srp_verifier' => hex2bin(str_pad($verifierHex, 64, '0', STR_PAD_LEFT)),
'client_build' => $clientBuild,
'launcher_session_hash' => hash('sha256', $launcherSessionToken, true),
'issued_at' => $now->format('Y-m-d H:i:s.u'),
'expires_at' => $expires->format('Y-m-d H:i:s.u'),
'claim_nonce' => null,
]], ['account_id'], ['generation_id', 'srp_salt', 'srp_verifier', 'client_build', 'launcher_session_hash', 'issued_at', 'expires_at', 'claim_nonce']);
return [
'account' => $username,
'ticket' => $ticket,
'expires_at' => $expires->format('Y-m-d\TH:i:s\Z'),
];
}
}