<?php namespace Pterodactyl\Http\Controllers\Auth; use Exception; use Pterodactyl\Models\User; use Illuminate\Http\Request; use Illuminate\Auth\AuthManager; use Illuminate\Http\JsonResponse; use LaravelWebauthn\Facades\Webauthn; use Webauthn\PublicKeyCredentialRequestOptions; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Contracts\Cache\Repository as CacheRepository; use Illuminate\Contracts\Config\Repository as ConfigRepository; class WebauthnController extends AbstractLoginController { private const SESSION_PUBLICKEY_REQUEST = 'webauthn.publicKeyRequest'; private CacheRepository $cache; public function __construct(AuthManager $auth, ConfigRepository $config, CacheRepository $cache) { parent::__construct($auth, $config); $this->cache = $cache; } /** * @return JsonResponse|void * * @throws \Illuminate\Validation\ValidationException * @throws \Pterodactyl\Exceptions\DisplayException */ public function auth(Request $request) { if ($this->hasTooManyLoginAttempts($request)) { $this->sendLockoutResponse($request); return; } $token = $request->input('confirmation_token'); try { /** @var \Pterodactyl\Models\User $user */ $user = User::query()->findOrFail($this->cache->get($token, 0)); } catch (ModelNotFoundException $exception) { $this->incrementLoginAttempts($request); $this->sendFailedLoginResponse( $request, null, 'The authentication token provided has expired, please refresh the page and try again.' ); return; } $this->auth->guard()->onceUsingId($user->id); try { $publicKey = $request->session()->pull(self::SESSION_PUBLICKEY_REQUEST); if (!$publicKey instanceof PublicKeyCredentialRequestOptions) { throw new ModelNotFoundException(trans('webauthn::errors.auth_data_not_found')); } $result = Webauthn::doAuthenticate( $user, $publicKey, $this->input($request, 'data'), ); if (!$result) { return new JsonResponse([ 'error' => [ 'message' => 'Nice attempt, you didn\'t pass the challenge.', ], ], JsonResponse::HTTP_I_AM_A_TEAPOT); } $this->cache->delete($token); return $this->sendLoginResponse($user, $request); } catch (Exception $e) { return new JsonResponse([ 'error' => [ 'message' => $e->getMessage(), ], ], JsonResponse::HTTP_FORBIDDEN); } } /** * Retrieve the input with a string result. */ private function input(Request $request, string $name, string $default = ''): string { $result = $request->input($name); return is_string($result) ? $result : $default; } }