Files
moonwell-web/app/Http/Controllers/Api/LauncherGameTicketController.php
2026-08-17 20:25:40 +04:00

71 lines
2.7 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\GameAccount;
use App\Services\LauncherGameTicketService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use OpenApi\Attributes as OA;
class LauncherGameTicketController extends Controller
{
public function __construct(private readonly LauncherGameTicketService $tickets) {}
#[OA\Post(
path: '/api/launcher/game-ticket',
summary: 'Issue a single-use game login ticket',
description: 'Creates short-lived SRP credentials for authserver. A new ticket atomically revokes the previous generation. The plaintext ticket is never stored.',
security: [['bearerAuth' => []]],
tags: ['Launcher Auth'],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ['client_build'],
properties: [
new OA\Property(property: 'client_build', type: 'integer', example: 12340),
],
),
),
responses: [
new OA\Response(
response: 200,
description: 'Ticket issued',
content: new OA\JsonContent(
required: ['account', 'ticket', 'expires_at'],
properties: [
new OA\Property(property: 'account', type: 'string', example: 'PLAYERONE'),
new OA\Property(property: 'ticket', type: 'string', pattern: '^[A-Z0-9]{16}$', example: '7W6D4M2Q9K8R3X5Z'),
new OA\Property(property: 'expires_at', type: 'string', format: 'date-time'),
],
),
),
new OA\Response(response: 401, description: 'Launcher authentication required'),
new OA\Response(response: 422, description: 'Client build is not allowed'),
new OA\Response(response: 429, description: 'Too many requests'),
],
)]
public function store(Request $request): JsonResponse
{
$clientBuild = (int) $request->validate([
'client_build' => [
'required',
'integer',
Rule::in([(int) config('moonwell.launcher.client_build', 12340)]),
],
])['client_build'];
/** @var GameAccount $account */
$account = $request->user();
$launcherSessionToken = $request->bearerToken();
abort_if($launcherSessionToken === null, 401);
return response()
->json($this->tickets->issue($account, $launcherSessionToken, $clientBuild))
->header('Cache-Control', 'no-store');
}
}