Don't store a new key on every login

This commit is contained in:
Dane Everitt 2022-02-13 15:06:08 -05:00
parent 2d2352017d
commit b43e8835bb
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
3 changed files with 27 additions and 37 deletions

View file

@ -7,6 +7,7 @@ use Carbon\CarbonInterface;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use PragmaRX\Google2FA\Google2FA; use PragmaRX\Google2FA\Google2FA;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\SecurityKey; use Pterodactyl\Models\SecurityKey;
use Illuminate\Contracts\Encryption\Encrypter; use Illuminate\Contracts\Encryption\Encrypter;
use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialRequestOptions;
@ -81,12 +82,10 @@ class LoginCheckpointController extends AbstractLoginController
/** /**
* Authenticates a login request using a security key for a user. * Authenticates a login request using a security key for a user.
* *
* @param \Illuminate\Http\Request $request
*
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
*/ */
public function key(Request $request) public function key(Request $request): JsonResponse
{ {
$key = $request->session()->get(SecurityKey::PK_SESSION_NAME); $key = $request->session()->get(SecurityKey::PK_SESSION_NAME);
if (!$key instanceof PublicKeyCredentialRequestOptions) { if (!$key instanceof PublicKeyCredentialRequestOptions) {
@ -102,16 +101,15 @@ class LoginCheckpointController extends AbstractLoginController
SecurityKey::getPsrRequestFactory($request) SecurityKey::getPsrRequestFactory($request)
); );
if (!hash_equals($user->uuid, $source->getUserHandle())) { if (hash_equals($user->uuid, $source->getUserHandle())) {
throw new BadRequestHttpException('An unexpected error was encountered while validating that security key.');
}
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 * Extracts the user from the session data using the provided confirmation token.
* @return \Pterodactyl\Models\User
* *
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\DisplayException * @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 * will return false if the data is invalid, or if more time has passed than
* was configured when the session was written. * 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, [ $validator = $validation->make($data, [
'user_id' => 'required|integer|min:1', 'user_id' => 'required|integer|min:1',

View file

@ -54,24 +54,9 @@ class PublicKeyCredentialSourceRepository implements PublicKeyRepositoryInterfac
*/ */
public function saveCredentialSource(PublicKeyCredentialSource $source): void public function saveCredentialSource(PublicKeyCredentialSource $source): void
{ {
$key = $this->user->securityKeys()->make(); // no-op — we handle creation of the keys in StoreSecurityKeyService
//
$key->forceFill([ // If you put logic in here it is triggered on each login.
'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();
} }
/** /**

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Services\Users\SecurityKeys; namespace Pterodactyl\Services\Users\SecurityKeys;
use Ramsey\Uuid\Uuid;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
@ -9,7 +10,6 @@ use Pterodactyl\Models\SecurityKey;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialCreationOptions;
use Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository; use Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository;
use Pterodactyl\Repositories\SecurityKeys\PublicKeyCredentialSourceRepository;
class StoreSecurityKeyService class StoreSecurityKeyService
{ {
@ -60,15 +60,22 @@ class StoreSecurityKeyService
// Unfortunately this repository interface doesn't define a response — it is explicitly // 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 // void — so we need to just query the database immediately after this to pull the information
// we just stored to return to the caller. // 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 */ return $key;
$created = $user->securityKeys()
->where('public_key_id', base64_encode($source->getPublicKeyCredentialId()))
->first();
$created->update(['name' => $this->keyName ?? 'Security Key (' . Str::random() . ')']);
return $created;
} }
} }