srp->normalizeUsername((string) $account->username); $this->assertCompatibleAccountName($username); $ticket = $this->randomTicket(); [$saltHex, $verifierHex] = $this->srp->makeRegistrationData($username, $ticket); $generationId = random_bytes(16); $sessionHash = hash('sha256', $launcherSessionToken, true); $issuedAt = CarbonImmutable::now('UTC'); $ttl = max(5, min(60, (int) config('moonwell.launcher.game_ticket_ttl', 60))); $expiresAt = $issuedAt->addSeconds($ttl); $connection = DB::connection((string) config('moonwell.auth_connection')); $connection->transaction(function () use ( $connection, $account, $generationId, $saltHex, $verifierHex, $clientBuild, $sessionHash, $issuedAt, $expiresAt, ): void { $connection->table('launcher_ticket')->updateOrInsert( ['account_id' => (int) $account->id], [ 'generation_id' => $generationId, 'srp_salt' => hex2bin($saltHex), 'srp_verifier' => hex2bin($verifierHex), 'client_build' => $clientBuild, 'launcher_session_hash' => $sessionHash, 'issued_at' => $issuedAt, 'expires_at' => $expiresAt, ], ); }); return [ 'account' => $username, 'ticket' => $ticket, 'expires_at' => $expiresAt->format('Y-m-d\\TH:i:s\\Z'), ]; } private function randomTicket(): string { $ticket = ''; $lastIndex = strlen(self::ALPHABET) - 1; for ($i = 0; $i < self::TICKET_LENGTH; $i++) { $ticket .= self::ALPHABET[random_int(0, $lastIndex)]; } return $ticket; } private function assertCompatibleAccountName(string $username): void { if ( $username === '' || strlen($username) > 17 || preg_match('/^[!-<>-~]+$/D', $username) !== 1 ) { throw new RuntimeException('Game account is not compatible with the 3.3.5a authentication protocol.'); } } }