Clean out existing webauthn logic, implement base logic for base package

This commit is contained in:
Dane Everitt 2021-08-08 09:23:02 -07:00
parent 0103a0c31e
commit eaf12aec60
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
19 changed files with 584 additions and 1541 deletions

View File

@ -0,0 +1,74 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Client;
use Illuminate\Support\Str;
use Carbon\CarbonImmutable;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Cache\Repository;
use Pterodactyl\Http\Requests\Api\Client\Account\RegisterWebauthnTokenRequest;
use Pterodactyl\Services\Users\HardwareSecurityKeys\CreatePublicKeyCredentialsService;
class HardwareTokenController extends ClientApiController
{
private CreatePublicKeyCredentialsService $createPublicKeyCredentials;
private Repository $cache;
public function __construct(
Repository $cache,
CreatePublicKeyCredentialsService $createPublicKeyCredentials
) {
parent::__construct();
$this->cache = $cache;
$this->createPublicKeyCredentials = $createPublicKeyCredentials;
}
/**
* Returns all of the hardware security keys (WebAuthn) that exists for a user.
*/
public function index(Request $request): array
{
return [];
}
/**
* Returns the data necessary for creating a new hardware security key for the
* user.
*/
public function create(Request $request): JsonResponse
{
$tokenId = Str::random(64);
$credentials = $this->createPublicKeyCredentials->handle($request->user());
$this->cache->put("webauthn:$tokenId", [
'credentials' => $credentials->jsonSerialize(),
'user_entity' => $credentials->getUser()->jsonSerialize(),
], CarbonImmutable::now()->addMinutes(10));
return new JsonResponse([
'data' => [
'token_id' => $tokenId,
'credentials' => $credentials->jsonSerialize(),
],
]);
}
/**
* Stores a new key for a user account.
*/
public function store(RegisterWebauthnTokenRequest $request): JsonResponse
{
return new JsonResponse([]);
}
/**
* Removes a WebAuthn key from a user's account.
*/
public function delete(Request $request, int $webauthnKeyId): JsonResponse
{
return new JsonResponse([]);
}
}

View File

@ -1,127 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Client;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\WebauthnKey;
use LaravelWebauthn\Facades\Webauthn;
use Webauthn\PublicKeyCredentialCreationOptions;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Pterodactyl\Transformers\Api\Client\WebauthnKeyTransformer;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class WebauthnController extends ClientApiController
{
private const SESSION_PUBLICKEY_CREATION = 'webauthn.publicKeyCreation';
/**
* ?
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(Request $request): array
{
return $this->fractal->collection(WebauthnKey::query()->where('user_id', '=', $request->user()->id)->get())
->transformWith(WebauthnKeyTransformer::class)
->toArray();
}
/**
* ?
*/
public function register(Request $request): JsonResponse
{
if (!Webauthn::canRegister($request->user())) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.cannot_register_new_key'),
],
], JsonResponse::HTTP_FORBIDDEN);
}
$publicKey = Webauthn::getRegisterData($request->user());
$request->session()->put(self::SESSION_PUBLICKEY_CREATION, $publicKey);
$request->session()->save();
return new JsonResponse([
'public_key' => $publicKey,
]);
}
/**
* ?
*
* @return array|JsonResponse
*/
public function create(Request $request)
{
if (!Webauthn::canRegister($request->user())) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.cannot_register_new_key'),
],
], JsonResponse::HTTP_FORBIDDEN);
}
if ($request->input('register') === null) {
throw new BadRequestHttpException('Missing register data in request body.');
}
if ($request->input('name') === null) {
throw new BadRequestHttpException('Missing name in request body.');
}
try {
$publicKey = $request->session()->pull(self::SESSION_PUBLICKEY_CREATION);
if (!$publicKey instanceof PublicKeyCredentialCreationOptions) {
throw new ModelNotFoundException(trans('webauthn::errors.create_data_not_found'));
}
$webauthnKey = Webauthn::doRegister(
$request->user(),
$publicKey,
$request->input('register'),
$request->input('name'),
);
return $this->fractal->item($webauthnKey)
->transformWith(WebauthnKeyTransformer::class)
->toArray();
} catch (Exception $e) {
return new JsonResponse([
'error' => [
'message' => $e->getMessage(),
],
], JsonResponse::HTTP_FORBIDDEN);
}
}
/**
* ?
*/
public function deleteKey(Request $request, int $webauthnKeyId): JsonResponse
{
try {
WebauthnKey::query()
->where('user_id', $request->user()->getAuthIdentifier())
->findOrFail($webauthnKeyId)
->delete();
return new JsonResponse([
'deleted' => true,
'id' => $webauthnKeyId,
]);
} catch (ModelNotFoundException $e) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.object_not_found'),
],
], JsonResponse::HTTP_NOT_FOUND);
}
}
}

View File

@ -8,7 +8,6 @@ use Illuminate\Http\Request;
use Illuminate\Auth\AuthManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\View\View;
use LaravelWebauthn\Facades\Webauthn;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Contracts\Cache\Repository as CacheRepository;
@ -92,30 +91,7 @@ class LoginController extends AbstractLoginController
return;
}
$webauthnKeys = $user->webauthnKeys()->get();
if (count($webauthnKeys) > 0) {
$token = Str::random(64);
$this->cache->put($token, $user->id, CarbonImmutable::now()->addMinutes(5));
$publicKey = Webauthn::getAuthenticateData($user);
$request->session()->put(self::SESSION_PUBLICKEY_REQUEST, $publicKey);
$request->session()->save();
$methods = [self::METHOD_WEBAUTHN];
if ($user->use_totp) {
$methods[] = self::METHOD_TOTP;
}
return new JsonResponse([
'complete' => false,
'methods' => $methods,
'confirmation_token' => $token,
'webauthn' => [
'public_key' => $publicKey,
],
]);
} elseif ($user->use_totp) {
if ($user->use_totp) {
$token = Str::random(64);
$this->cache->put($token, $user->id, CarbonImmutable::now()->addMinutes(5));

View File

@ -1,101 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Auth;
use Exception;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
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;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Client\Account;
use Pterodactyl\Http\Requests\Api\Client\AccountApiRequest;
class RegisterWebauthnTokenRequest extends AccountApiRequest
{
public function rules(): array
{
return [
'name' => ['string', 'required'],
'register' => ['string', 'required'],
'public_key' => ['string', 'required'],
];
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Pterodactyl\Models;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialDescriptor;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class HardwareSecurityKey extends Model
{
use HasFactory;
public const RESOURCE_NAME = 'hardware_security_key';
protected $attributes = [
'user_id' => 'int',
'transports' => 'array',
'trust_path' => 'array',
'other_ui' => 'array',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function toCredentialsDescriptor()
{
return new PublicKeyCredentialDescriptor(
$this->type,
$this->public_key_id,
$this->transports
);
}
public function toCredentialSource(): PublicKeyCredentialSource
{
return PublicKeyCredentialSource::createFromArray([
'publicKeyCredentialId' => $this->public_key_id,
'type' => $this->type,
'transports' => $this->transports,
'attestationType' => $this->attestation_type,
// 'trustPath' => $key->trustPath->jsonSerialize(),
'aaguid' => $this->aaguid,
'credentialPublicKey' => $this->public_key,
'userHandle' => $this->user_handle,
'counter' => $this->counter,
'otherUI' => $this->other_ui,
]);
}
}

View File

@ -210,9 +210,9 @@ class User extends Model implements
return $this->hasMany(RecoveryToken::class);
}
public function webauthnKeys(): HasMany
public function hardwareSecurityKeys(): HasMany
{
return $this->hasMany(WebauthnKey::class);
return $this->hasMany(HardwareSecurityKey::class);
}
/**

View File

@ -1,18 +0,0 @@
<?php
namespace Pterodactyl\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class WebauthnKey extends \LaravelWebauthn\Models\WebauthnKey
{
use HasFactory;
public const RESOURCE_NAME = 'webauthn_key';
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Pterodactyl\Repositories\Webauthn;
use Pterodactyl\Models\User;
use Illuminate\Container\Container;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialUserEntity;
use Pterodactyl\Models\HardwareSecurityKey;
use Webauthn\PublicKeyCredentialSourceRepository as PublicKeyRepositoryInterface;
class PublicKeyCredentialSourceRepository implements PublicKeyRepositoryInterface
{
protected User $user;
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Find a single hardware security token for a user by uzing the credential ID.
*/
public function findOneByCredentialId(string $id): ?PublicKeyCredentialSource
{
/** @var \Pterodactyl\Models\HardwareSecurityKey $key */
$key = $this->user->hardwareSecurityKeys()
->where('public_key_id', $id)
->first();
return $key ? $key->toCredentialSource() : null;
}
/**
* Find all of the hardware tokens that exist for the user using the given
* entity handle.
*/
public function findAllForUserEntity(PublicKeyCredentialUserEntity $entity): array
{
$results = $this->user->hardwareSecurityKeys()
->where('user_handle', $entity->getId())
->get();
return $results->map(function (HardwareSecurityKey $key) {
return $key->toCredentialSource();
})->values()->toArray();
}
/**
* Save a credential to the database and link it with the user.
*/
public function saveCredentialSource(PublicKeyCredentialSource $source): void
{
// todo: implement
}
/**
* Returns a new instance of the repository with the provided user attached.
*/
public static function factory(User $user): self
{
return Container::getInstance()->make(static::class, ['user' => $user]);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Pterodactyl\Services\Users\HardwareSecurityKeys;
use Webauthn\Server;
use Ramsey\Uuid\Uuid;
use Pterodactyl\Models\User;
use Webauthn\PublicKeyCredentialRpEntity;
use Pterodactyl\Models\HardwareSecurityKey;
use Webauthn\PublicKeyCredentialUserEntity;
use Webauthn\PublicKeyCredentialCreationOptions;
use Pterodactyl\Repositories\Webauthn\PublicKeyCredentialSourceRepository;
class CreatePublicKeyCredentialsService
{
protected PublicKeyCredentialRpEntity $rpEntity;
public function __construct()
{
$url = str_replace(['http://', 'https://'], '', config('app.url'));
$this->rpEntity = new PublicKeyCredentialRpEntity(config('app.name'), trim($url, '/'));
}
public function handle(User $user): PublicKeyCredentialCreationOptions
{
$id = Uuid::uuid4()->toString();
$entity = new PublicKeyCredentialUserEntity($user->uuid, $id, $user->email);
$excluded = $user->hardwareSecurityKeys->map(function (HardwareSecurityKey $key) {
return $key->toCredentialsDescriptor();
})->values()->toArray();
return $this->getServerInstance($user)->generatePublicKeyCredentialCreationOptions(
$entity,
PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
$excluded
);
}
protected function getServerInstance(User $user)
{
return new Server(
$this->rpEntity,
PublicKeyCredentialSourceRepository::factory($user)
);
}
}

View File

@ -2,7 +2,7 @@
namespace Pterodactyl\Transformers\Api\Client;
use Pterodactyl\Models\WebauthnKey;
use Pterodactyl\Models\HardwareSecurityKey;
use Pterodactyl\Transformers\Api\Transformer;
class WebauthnKeyTransformer extends Transformer
@ -12,15 +12,13 @@ class WebauthnKeyTransformer extends Transformer
*/
public function getResourceName(): string
{
return WebauthnKey::RESOURCE_NAME;
return HardwareSecurityKey::RESOURCE_NAME;
}
/**
* Return basic information about the currently logged in user.
*
* @param \Pterodactyl\Models\WebauthnKey|\LaravelWebauthn\Models\WebauthnKey $model
*/
public function transform($model): array
public function transform(HardwareSecurityKey $model): array
{
return [
'id' => $model->id,

View File

@ -17,8 +17,7 @@
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-zip": "*",
"asbiin/laravel-webauthn": "^1.1",
"aws/aws-sdk-php": "^3.192",
"aws/aws-sdk-php": "^3.186",
"doctrine/dbal": "^3.1",
"fideloper/proxy": "^4.4",
"guzzlehttp/guzzle": "^7.3",
@ -42,6 +41,7 @@
"spatie/laravel-query-builder": "^3.5",
"staudenmeir/belongs-to-through": "^2.11",
"symfony/yaml": "^5.3",
"web-auth/webauthn-lib": "^3.3",
"webmozart/assert": "^1.10"
},
"require-dev": {

1294
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,222 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| LaravelWebauthn Master Switch
|--------------------------------------------------------------------------
|
| This option may be used to disable LaravelWebauthn.
|
*/
'enable' => true,
/*
|--------------------------------------------------------------------------
| Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to Webauthn routes, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => [
'web',
'auth',
],
/*
|--------------------------------------------------------------------------
| Prefix path
|--------------------------------------------------------------------------
|
| The uri prefix for all webauthn requests.
|
*/
'prefix' => 'webauthn',
'authenticate' => [
/*
|--------------------------------------------------------------------------
| View to load after middleware login request.
|--------------------------------------------------------------------------
|
| The name of blade template to load whe a user login and it request to validate
| the Webauthn 2nd factor.
|
*/
'view' => 'webauthn::authenticate',
/*
|--------------------------------------------------------------------------
| Redirect with callback url after login.
|--------------------------------------------------------------------------
|
| Save the destination url, then after a successful login, redirect to this
| url.
|
*/
'postSuccessCallback' => true,
/*
|--------------------------------------------------------------------------
| Redirect route
|--------------------------------------------------------------------------
|
| If postSuccessCallback if false, redirect to this route after login
| request is complete.
| If empty, send a json response to let the client side redirection.
|
*/
'postSuccessRedirectRoute' => '',
],
'register' => [
/*
|--------------------------------------------------------------------------
| View to load on register request.
|--------------------------------------------------------------------------
|
| The name of blade template to load when a user request a creation of
| Webauthn key.
|
*/
'view' => 'webauthn::register',
/*
|--------------------------------------------------------------------------
| Redirect route
|--------------------------------------------------------------------------
|
| The route to redirect to after register key request is complete.
| If empty, send a json response to let the client side redirection.
|
*/
'postSuccessRedirectRoute' => '',
],
/*
|--------------------------------------------------------------------------
| Session name
|--------------------------------------------------------------------------
|
| Name of the session parameter to store the successful login.
|
*/
'sessionName' => 'webauthn_auth',
/*
|--------------------------------------------------------------------------
| Webauthn challenge length
|--------------------------------------------------------------------------
|
| Length of the random string used in the challenge request.
|
*/
'challenge_length' => 32,
/*
|--------------------------------------------------------------------------
| Webauthn timeout (milliseconds)
|--------------------------------------------------------------------------
|
| Time that the caller is willing to wait for the call to complete.
|
*/
'timeout' => 60000,
/*
|--------------------------------------------------------------------------
| Webauthn extension client input
|--------------------------------------------------------------------------
|
| Optional authentication extension.
| See https://www.w3.org/TR/webauthn/#client-extension-input
|
*/
'extensions' => [],
/*
|--------------------------------------------------------------------------
| Webauthn icon
|--------------------------------------------------------------------------
|
| Url which resolves to an image associated with the entity.
| See https://www.w3.org/TR/webauthn/#dom-publickeycredentialentity-icon
|
*/
'icon' => null,
/*
|--------------------------------------------------------------------------
| Webauthn Attestation Conveyance
|--------------------------------------------------------------------------
|
| This parameter specify the preference regarding the attestation conveyance
| during credential generation.
| See https://www.w3.org/TR/webauthn/#attestation-convey
|
*/
'attestation_conveyance' => \Webauthn\PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
/*
|--------------------------------------------------------------------------
| Google Safetynet ApiKey
|--------------------------------------------------------------------------
|
| Api key to use Google Safetynet.
| See https://developer.android.com/training/safetynet/attestation
|
*/
'google_safetynet_api_key' => '',
/*
|--------------------------------------------------------------------------
| Webauthn Public Key Credential Parameters
|--------------------------------------------------------------------------
|
| List of allowed Cryptographic Algorithm Identifier.
| See https://www.w3.org/TR/webauthn/#alg-identifier
|
*/
'public_key_credential_parameters' => [
\Cose\Algorithms::COSE_ALGORITHM_ES256,
\Cose\Algorithms::COSE_ALGORITHM_RS256,
],
/*
|--------------------------------------------------------------------------
| Webauthn Authenticator Selection Criteria
|--------------------------------------------------------------------------
|
| Requirement for the creation operation.
| See https://www.w3.org/TR/webauthn/#authenticatorSelection
|
*/
'authenticator_selection_criteria' => [
/*
| See https://www.w3.org/TR/webauthn/#attachment
*/
'attachment_mode' => \Webauthn\AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE,
'require_resident_key' => false,
/*
| See https://www.w3.org/TR/webauthn/#userVerificationRequirement
*/
'user_verification' => \Webauthn\AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED,
],
];

View File

@ -2,7 +2,7 @@
namespace Database\Factories;
use Pterodactyl\Models\WebauthnKey;
use Pterodactyl\Models\HardwareSecurityKey;
use Illuminate\Database\Eloquent\Factories\Factory;
class WebauthnKeyFactory extends Factory
@ -10,7 +10,7 @@ class WebauthnKeyFactory extends Factory
/**
* The name of the factory's corresponding model.
*/
protected $model = WebauthnKey::class;
protected $model = HardwareSecurityKey::class;
/**
* Define the model's default state.

View File

@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateHardwareSecurityKeysTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('hardware_security_keys', function (Blueprint $table) {
$table->id();
$table->char('uuid', 36);
$table->unsignedInteger('user_id')->references('id')->on('users')->onDelete('cascade');
$table->text('public_key_id');
$table->text('public_key');
$table->char('aaguid', 36);
$table->string('type');
$table->json('transports');
$table->string('attestation_type');
$table->json('trust_path');
$table->text('user_handle');
$table->unsignedInteger('counter');
$table->json('other_ui');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('hardware_security_keys');
}
}

View File

@ -30,10 +30,10 @@ Route::group(['prefix' => '/account'], function () {
Route::post('/api-keys', [Client\ApiKeyController::class, 'store']);
Route::delete('/api-keys/{identifier}', [Client\ApiKeyController::class, 'delete']);
Route::get('/webauthn', 'WebauthnController@index')->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::get('/webauthn/register', 'WebauthnController@register')->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::post('/webauthn/register', 'WebauthnController@create')->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::delete('/webauthn/{id}', 'WebauthnController@deleteKey')->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::get('/webauthn', [Client\HardwareTokenController::class, 'index'])->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::get('/webauthn/register', [Client\HardwareTokenController::class, 'create'])->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::post('/webauthn/register', [Client\HardwareTokenController::class, 'store'])->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::delete('/webauthn/{id}', [Client\HardwareTokenController::class, 'delete'])->withoutMiddleware(RequireTwoFactorAuthentication::class);
Route::get('/ssh', 'SSHKeyController@index');
Route::post('/ssh', 'SSHKeyController@store');

View File

@ -20,7 +20,6 @@ Route::group(['middleware' => 'guest'], function () {
// Login endpoints.
Route::post('/login', 'LoginController@login')->middleware('recaptcha');
Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
Route::post('/login/checkpoint/key', 'WebauthnController@auth');
// Forgot password route. A post to this endpoint will trigger an
// email to be sent containing a reset token.

View File

@ -4,7 +4,7 @@ namespace Pterodactyl\Tests\Unit\Http\Middleware;
use Mockery as m;
use Pterodactyl\Models\User;
use Pterodactyl\Models\WebauthnKey;
use Pterodactyl\Models\HardwareSecurityKey;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Exceptions\Http\TwoFactorAuthRequiredException;
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
@ -66,7 +66,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
/** @var \Pterodactyl\Models\User $user */
$user = User::factory()
->has(WebauthnKey::factory()->count(1))
->has(HardwareSecurityKey::factory()->count(1))
->create(['use_totp' => false]);
$this->setRequestUserModel($user);
@ -141,7 +141,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
/** @var \Pterodactyl\Models\User $user */
$user = User::factory()
->has(WebauthnKey::factory()->count(1))
->has(HardwareSecurityKey::factory()->count(1))
->create(['use_totp' => false]);
$this->setRequestUserModel($user);
@ -255,7 +255,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
config()->set('pterodactyl.auth.2fa_required', RequireTwoFactorAuthentication::LEVEL_ADMIN);
/** @var \Pterodactyl\Models\User $user */
$user = User::factory()->has(WebauthnKey::factory()->count(1))->create(['use_totp' => false]);
$user = User::factory()->has(HardwareSecurityKey::factory()->count(1))->create(['use_totp' => false]);
$this->setRequestUserModel($user);
$this->assertFalse($user->use_totp);
@ -278,7 +278,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
/** @var \Pterodactyl\Models\User $user */
$user = User::factory()
->has(WebauthnKey::factory()->count(1))
->has(HardwareSecurityKey::factory()->count(1))
->create(['use_totp' => false, 'root_admin' => true]);
$this->setRequestUserModel($user);