diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php index 9e3233a54..21a8cba46 100644 --- a/app/Http/Controllers/Auth/LoginCheckpointController.php +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -7,6 +7,7 @@ use Carbon\CarbonInterface; use Pterodactyl\Models\User; use Illuminate\Http\Request; use PragmaRX\Google2FA\Google2FA; +use Illuminate\Http\JsonResponse; use Pterodactyl\Models\SecurityKey; use Illuminate\Contracts\Encryption\Encrypter; use Webauthn\PublicKeyCredentialRequestOptions; @@ -81,12 +82,10 @@ class LoginCheckpointController extends AbstractLoginController /** * Authenticates a login request using a security key for a user. * - * @param \Illuminate\Http\Request $request - * * @throws \Illuminate\Validation\ValidationException * @throws \Pterodactyl\Exceptions\DisplayException */ - public function key(Request $request) + public function key(Request $request): JsonResponse { $key = $request->session()->get(SecurityKey::PK_SESSION_NAME); if (!$key instanceof PublicKeyCredentialRequestOptions) { @@ -102,16 +101,15 @@ class LoginCheckpointController extends AbstractLoginController SecurityKey::getPsrRequestFactory($request) ); - if (!hash_equals($user->uuid, $source->getUserHandle())) { - throw new BadRequestHttpException('An unexpected error was encountered while validating that security key.'); + if (hash_equals($user->uuid, $source->getUserHandle())) { + return $this->sendLoginResponse($user, $request); } - return $this->sendLoginResponse($user, $request); + throw new BadRequestHttpException('An unexpected error was encountered while validating that security key.'); } /** - * @param \Illuminate\Http\Request $request - * @return \Pterodactyl\Models\User + * Extracts the user from the session data using the provided confirmation token. * * @throws \Illuminate\Validation\ValidationException * @throws \Pterodactyl\Exceptions\DisplayException @@ -168,7 +166,7 @@ class LoginCheckpointController extends AbstractLoginController * will return false if the data is invalid, or if more time has passed than * was configured when the session was written. */ - public static function isValidSessionData(ValidationFactory $validation, array $data): bool + protected static function isValidSessionData(ValidationFactory $validation, array $data): bool { $validator = $validation->make($data, [ 'user_id' => 'required|integer|min:1', diff --git a/app/Repositories/SecurityKeys/PublicKeyCredentialSourceRepository.php b/app/Repositories/SecurityKeys/PublicKeyCredentialSourceRepository.php index 089d82a56..d8dc92d18 100644 --- a/app/Repositories/SecurityKeys/PublicKeyCredentialSourceRepository.php +++ b/app/Repositories/SecurityKeys/PublicKeyCredentialSourceRepository.php @@ -54,24 +54,9 @@ class PublicKeyCredentialSourceRepository implements PublicKeyRepositoryInterfac */ public function saveCredentialSource(PublicKeyCredentialSource $source): void { - $key = $this->user->securityKeys()->make(); - - $key->forceFill([ - 'uuid' => Uuid::uuid4(), - 'user_id' => $this->user->id, - 'public_key_id' => $source->getPublicKeyCredentialId(), - 'public_key' => $source->getCredentialPublicKey(), - 'aaguid' => $source->getAaguid(), - 'type' => $source->getType(), - 'transports' => $source->getTransports(), - 'attestation_type' => $source->getAttestationType(), - 'trust_path' => $source->getTrustPath(), - 'user_handle' => $source->getUserHandle(), - 'counter' => $source->getCounter(), - 'other_ui' => $source->getOtherUI(), - ]); - - $key->saveOrFail(); + // no-op — we handle creation of the keys in StoreSecurityKeyService + // + // If you put logic in here it is triggered on each login. } /** diff --git a/app/Services/Users/SecurityKeys/StoreSecurityKeyService.php b/app/Services/Users/SecurityKeys/StoreSecurityKeyService.php index 5e7f65180..a262425f4 100644 --- a/app/Services/Users/SecurityKeys/StoreSecurityKeyService.php +++ b/app/Services/Users/SecurityKeys/StoreSecurityKeyService.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Services\Users\SecurityKeys; +use Ramsey\Uuid\Uuid; use Illuminate\Support\Str; use Pterodactyl\Models\User; use Webmozart\Assert\Assert; @@ -9,7 +10,6 @@ use Pterodactyl\Models\SecurityKey; use Psr\Http\Message\ServerRequestInterface; use Webauthn\PublicKeyCredentialCreationOptions; use Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository; -use Pterodactyl\Repositories\SecurityKeys\PublicKeyCredentialSourceRepository; class StoreSecurityKeyService { @@ -60,15 +60,22 @@ class StoreSecurityKeyService // Unfortunately this repository interface doesn't define a response — it is explicitly // void — so we need to just query the database immediately after this to pull the information // we just stored to return to the caller. - PublicKeyCredentialSourceRepository::factory($user)->saveCredentialSource($source); + /** @var \Pterodactyl\Models\SecurityKey $key */ + $key = $user->securityKeys()->forceCreate([ + 'uuid' => Uuid::uuid4(), + 'name' => $this->keyName ?? 'Security Key (' . Str::random() . ')', + 'public_key_id' => $source->getPublicKeyCredentialId(), + 'public_key' => $source->getCredentialPublicKey(), + 'aaguid' => $source->getAaguid(), + 'type' => $source->getType(), + 'transports' => $source->getTransports(), + 'attestation_type' => $source->getAttestationType(), + 'trust_path' => $source->getTrustPath(), + 'user_handle' => $user->uuid, + 'counter' => $source->getCounter(), + 'other_ui' => $source->getOtherUI(), + ]); - /** @var \Pterodactyl\Models\SecurityKey $created */ - $created = $user->securityKeys() - ->where('public_key_id', base64_encode($source->getPublicKeyCredentialId())) - ->first(); - - $created->update(['name' => $this->keyName ?? 'Security Key (' . Str::random() . ')']); - - return $created; + return $key; } }