From eac672a8708e8e53fb4eb7e777b25569b2527530 Mon Sep 17 00:00:00 2001 From: AGuyNamedJens Date: Sun, 10 Dec 2023 21:59:14 +0100 Subject: [PATCH] 2FA initial --- .gitignore | 10 +- .../Commands/Overrides/KeyGenerateCommand.php | 29 ++ app/Http/Controllers/Admin/UserController.php | 2 +- .../Controllers/Auth/Login2FAController.php | 80 ++++ .../Auth/LoginCheckpointController.php | 130 ++++++ app/Http/Kernel.php | 1 + app/Models/RecoveryToken.php | 34 ++ app/Models/User.php | 30 +- bootstrap/cache/.gitignore | 0 composer.json | 2 + composer.lock | 370 +++++++++++++++++- database/factories/UserFactory.php | 1 + package-lock.json | 2 +- routes/web.php | 6 +- .../default/views/auth/2fa-secret.blade.php | 23 ++ 15 files changed, 708 insertions(+), 12 deletions(-) create mode 100644 app/Console/Commands/Overrides/KeyGenerateCommand.php create mode 100644 app/Http/Controllers/Auth/Login2FAController.php create mode 100644 app/Http/Controllers/Auth/LoginCheckpointController.php create mode 100644 app/Models/RecoveryToken.php mode change 100755 => 100644 bootstrap/cache/.gitignore create mode 100644 themes/default/views/auth/2fa-secret.blade.php diff --git a/.gitignore b/.gitignore index ad9d095b..1e64e53e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /storage/*.key /vendor /storage/credit_deduction_log -storage/debugbar +/storage/debugbar .env .env.testing .env.backup @@ -20,10 +20,10 @@ yarn.lock .gitignore .env.dev .env.testing -storage/invoices.zip -storage/app/public/logo.png +/storage/invoices.zip +/storage/app/public/logo.png *vscode - Kopie.env -public/install/logs.txt +/public/install/logs.txt install.lock -public/install/logs/installer.log +/public/install/logs/*.log diff --git a/app/Console/Commands/Overrides/KeyGenerateCommand.php b/app/Console/Commands/Overrides/KeyGenerateCommand.php new file mode 100644 index 00000000..520a7e18 --- /dev/null +++ b/app/Console/Commands/Overrides/KeyGenerateCommand.php @@ -0,0 +1,29 @@ +input->isInteractive()) { + $this->output->warning('It appears you have already configured an application encryption key. Continuing with this process with overwrite that key and cause data corruption for any existing encrypted data. DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.'); + if (!$this->confirm('I understand the consequences of performing this command and accept all responsibility for the loss of encrypted data.')) { + return; + } + + if (!$this->confirm('Are you sure you wish to continue? Changing the application encryption key WILL CAUSE DATA LOSS. WE CANNOT HELP YOU RECOVER YOUR DATA IF YOU PROCEED.')) { + return; + } + } + + parent::handle(); + } +} diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 4956e565..ff3d7b91 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -97,7 +97,7 @@ class UserController extends Controller /** * Get a JSON response of users. * - * @return \Illuminate\Support\Collection|\App\models\User + * @return \Illuminate\Support\Collection|\App\Models\User */ public function json(Request $request) { diff --git a/app/Http/Controllers/Auth/Login2FAController.php b/app/Http/Controllers/Auth/Login2FAController.php new file mode 100644 index 00000000..8544db55 --- /dev/null +++ b/app/Http/Controllers/Auth/Login2FAController.php @@ -0,0 +1,80 @@ +middleware('guest'); + } + + /** + * @throws ValidationException + */ + public function authenticate(Request $request, User $user) + { + $google2fa = app(Google2FA::class); + + $valid = $google2fa->verifyKey('6TZBNEJT5I4VKTHN', $request->input('one_time_password')); + logger()->info('2FA Valid: ' . $valid); + if ($valid) { + // Authentication passed... + logger()->info('2FA Passed: ' . $request->input('one_time_password')); + return redirect()->route('home'); + } + else { + //throw error + logger()->info('2FA Failed: ' . $request->input('one_time_password')); + return redirect()->back()->withErrors([ + 'one_time_password' => 'The one time password is invalid.' + ]); + } + } + + // Create a new secret and display the QR code + public function Attempt2FA() + { + $google2fa = app(Google2FA::class); + $this->secret = $google2fa->generateSecretKey(); + + logger()->info('2FA Secret: ' . $this->secret); + $g2faUrl = $google2fa->getQRCodeUrl( + 'pragmarx', + 'google2fa@pragmarx.com', + '6TZBNEJT5I4VKTHN' + ); + + $writer = new Writer( + new ImageRenderer( + new RendererStyle(400), + new ImagickImageBackEnd() + ) + ); + + $qrcode_image = base64_encode($writer->writeString($g2faUrl)); + + return view('auth.2fa-secret')->with([ + 'qrcode_image' => $qrcode_image, + 'secret' => '6TZBNEJT5I4VKTHN' + ]); + } +} diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php new file mode 100644 index 00000000..8f884777 --- /dev/null +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -0,0 +1,130 @@ +hasTooManyLoginAttempts($request)) { + $this->sendLockoutResponse($request); + } + + $details = $request->session()->get('auth_confirmation_token'); + if (!$this->hasValidSessionData($details)) { + $this->sendFailedLoginResponse($request); // token expired + } + + if (!hash_equals($request->input('confirmation_token') ?? '', $details['token_value'])) { + $this->sendFailedLoginResponse($request); // token invalid + } + + try { + /** @var User $user */ + $user = User::query()->findOrFail($details['user_id']); + } catch (ModelNotFoundException) { + $this->sendFailedLoginResponse($request); // user not found + } + + // Recovery tokens go through a slightly different pathway for usage. + if (!is_null($recoveryToken = $request->input('recovery_token'))) { + if ($this->isValidRecoveryToken($user, $recoveryToken)) { + // If the recovery token is valid, send the login response + return $this->sendLoginResponse($request); + } + } else { + $decrypted = $this->encrypter->decrypt($user->totp_secret); + + if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) { + + return $this->sendLoginResponse($request); + } + } + + $this->sendFailedLoginResponse($request); // recovery token invalid + } + + /** + * Determines if a given recovery token is valid for the user account. If we find a matching token + * it will be deleted from the database. + * + * @throws \Exception + */ + protected function isValidRecoveryToken(User $user, string $value): bool + { + foreach ($user->recoveryTokens as $token) { + if (password_verify($value, $token->token)) { + $token->delete(); + + return true; + } + } + + return false; + } + + /** + * Determines if the data provided from the session is valid or not. This + * will return false if the data is invalid, or if more time has passed than + * was configured when the session was written. + */ + protected function hasValidSessionData(array $data): bool + { + $validator = $this->validation->make($data, [ + 'user_id' => 'required|integer|min:1', + 'token_value' => 'required|string', + 'expires_at' => 'required', + ]); + + if ($validator->fails()) { + return false; + } + + if (!$data['expires_at'] instanceof CarbonInterface) { + return false; + } + + if ($data['expires_at']->isBefore(CarbonImmutable::now())) { + return false; + } + + return true; + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index d0b1c7f7..9336ac29 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -77,6 +77,7 @@ class Kernel extends HttpKernel 'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class, 'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class, 'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class, + '2fa' => \PragmaRX\Google2FALaravel\Middleware::class, ]; } diff --git a/app/Models/RecoveryToken.php b/app/Models/RecoveryToken.php new file mode 100644 index 00000000..c747e491 --- /dev/null +++ b/app/Models/RecoveryToken.php @@ -0,0 +1,34 @@ + 'required|string', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 584da015..84f54298 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,6 +7,7 @@ use App\Notifications\WelcomeMessage; use App\Classes\PterodactylClient; use App\Settings\PterodactylSettings; use Illuminate\Contracts\Auth\MustVerifyEmail; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -67,6 +68,9 @@ class User extends Authenticatable implements MustVerifyEmail 'suspended', 'referral_code', 'email_verified_reward', + 'use_totp', + 'totp_secret', + 'totp_authenticated_at', ]; /** @@ -77,6 +81,8 @@ class User extends Authenticatable implements MustVerifyEmail protected $hidden = [ 'password', 'remember_token', + 'totp_secret', + 'totp_authenticated_at' ]; /** @@ -89,7 +95,9 @@ class User extends Authenticatable implements MustVerifyEmail 'last_seen' => 'datetime', 'credits' => 'float', 'server_limit' => 'float', - 'email_verified_reward' => 'boolean' + 'email_verified_reward' => 'boolean', + 'use_totp' => 'boolean', + 'totp_secret' => 'nullable|string' ]; public function __construct() @@ -313,4 +321,24 @@ class User extends Authenticatable implements MustVerifyEmail ->logOnlyDirty() ->dontSubmitEmptyLogs(); } + + public function recoveryTokens(): HasMany + { + return $this->hasMany(RecoveryToken::class); + } + + /** + * Interact with the 2fa secret attribute. + * + * @param string $value + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + protected function google2faSecret(): Attribute + { + return new Attribute( + get: fn ($value) => decrypt($value), + set: fn ($value) => encrypt($value), + ); + } + } diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100755 new mode 100644 diff --git a/composer.json b/composer.json index b316eec1..2c5b947d 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "require": { "php": "^8.1", "ext-intl": "*", + "bacon/bacon-qr-code": "^2.0", "biscolab/laravel-recaptcha": "^5.4", "doctrine/dbal": "^3.5.3", "guzzlehttp/guzzle": "^7.5", @@ -22,6 +23,7 @@ "league/flysystem-aws-s3-v3": "^3.12.2", "paypal/paypal-checkout-sdk": "^1.0.2", "paypal/rest-api-sdk-php": "^1.14.0", + "pragmarx/google2fa-laravel": "^2.1", "predis/predis": "*", "qirolab/laravel-themer": "^2.0.2", "socialiteproviders/discord": "^4.1.2", diff --git a/composer.lock b/composer.lock index 3f224c0c..7de90b72 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8a9b4a3cda2a919fa33f41527b679dce", + "content-hash": "976ab2160a9193f33527adc845d4b4cc", "packages": [ { "name": "aws/aws-crt-php", @@ -155,6 +155,60 @@ }, "time": "2023-04-26T18:21:04+00:00" }, + { + "name": "bacon/bacon-qr-code", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.1", + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" + }, + "time": "2022-12-07T17:46:57+00:00" + }, { "name": "barryvdh/laravel-dompdf", "version": "v2.0.1", @@ -358,6 +412,56 @@ ], "time": "2023-01-15T23:15:59+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 | ^8 | ^9", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" + }, + "time": "2023-08-25T16:18:39+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", @@ -3469,6 +3573,73 @@ ], "time": "2023-02-08T01:06:31+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.100", @@ -3990,6 +4161,201 @@ }, "time": "2023-04-25T09:01:03+00:00" }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.18", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + }, + "time": "2022-06-13T21:57:56+00:00" + }, + { + "name": "pragmarx/google2fa-laravel", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-laravel.git", + "reference": "035b799d6ea080d07722012c926c15c9dde66fd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/035b799d6ea080d07722012c926c15c9dde66fd7", + "reference": "035b799d6ea080d07722012c926c15c9dde66fd7", + "shasum": "" + }, + "require": { + "laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0", + "php": ">=7.0", + "pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*", + "phpunit/phpunit": "~5|~6|~7|~8|~9" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes.", + "pragmarx/recovery": "Generate recovery codes." + }, + "type": "library", + "extra": { + "component": "package", + "frameworks": [ + "Laravel" + ], + "branch-alias": { + "dev-master": "0.2-dev" + }, + "laravel": { + "providers": [ + "PragmaRX\\Google2FALaravel\\ServiceProvider" + ], + "aliases": { + "Google2FA": "PragmaRX\\Google2FALaravel\\Facade" + } + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FALaravel\\": "src/", + "PragmaRX\\Google2FALaravel\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-laravel/issues", + "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v2.1.1" + }, + "time": "2023-02-26T09:41:06+00:00" + }, + { + "name": "pragmarx/google2fa-qrcode", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "pragmarx/google2fa": ">=4.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "chillerlan/php-qrcode": "^1.0|^2.0|^3.0|^4.0", + "khanamiryan/qrcode-detector-decoder": "^1.0", + "phpunit/phpunit": "~4|~5|~6|~7|~8|~9" + }, + "suggest": { + "bacon/bacon-qr-code": "For QR Code generation, requires imagick", + "chillerlan/php-qrcode": "For QR Code generation" + }, + "type": "library", + "extra": { + "component": "package", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FAQRCode\\": "src/", + "PragmaRX\\Google2FAQRCode\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "QR Code package for Google2FA", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa", + "qr code", + "qrcode" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + }, + "time": "2021-08-15T12:53:48+00:00" + }, { "name": "predis/predis", "version": "v2.1.2", @@ -11093,5 +11459,5 @@ "platform-overrides": { "php": "8.1" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.3.0" } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index b0ae6fc6..a4b634dd 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -22,6 +22,7 @@ class UserFactory extends Factory 'email_verified_at' => $this->faker->dateTimeBetween('-30 days', now()), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), + 'use_totp' => false, ]; } } diff --git a/package-lock.json b/package-lock.json index 89d691a5..9bee0b9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "controlpanel", + "name": "billingV1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/routes/web.php b/routes/web.php index a6a2ab3c..b966cf72 100644 --- a/routes/web.php +++ b/routes/web.php @@ -23,6 +23,7 @@ use App\Http\Controllers\Admin\UsefulLinkController; use App\Http\Controllers\Admin\UserController; use App\Http\Controllers\Admin\VoucherController; use App\Http\Controllers\Admin\CouponController; +use App\Http\Controllers\Auth\Login2FAController; use App\Http\Controllers\Auth\SocialiteController; use App\Http\Controllers\HomeController; use App\Http\Controllers\NotificationController; @@ -46,6 +47,8 @@ use Illuminate\Support\Facades\Route; | contains the "web" middleware group. Now create something great! | */ +Route::get('/2fa', [Login2FAController::class, 'Attempt2FA'])->name('2fa'); +Route::post('/2fa/authenticate', [Login2FAController::class, 'authenticate'])->name('2fa.authenticate'); Route::middleware('guest')->get('/', function () { return redirect('login'); @@ -71,6 +74,7 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () { return back()->with('success', 'Verification link sent!'); })->middleware(['auth', 'throttle:3,1'])->name('verification.send'); + //normal routes Route::get('notifications/readAll', [NotificationController::class, 'readAll'])->name('notifications.readAll'); Route::resource('notifications', NotificationController::class); @@ -230,8 +234,6 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () { Route::resource("ticket/category", TicketCategoryController::class, ['as' => 'ticket']); }); - - Route::get('/home', [HomeController::class, 'index'])->name('home'); }); diff --git a/themes/default/views/auth/2fa-secret.blade.php b/themes/default/views/auth/2fa-secret.blade.php new file mode 100644 index 00000000..3fc769a6 --- /dev/null +++ b/themes/default/views/auth/2fa-secret.blade.php @@ -0,0 +1,23 @@ +@extends('layouts.app') +@php($website_settings = app(App\Settings\WebsiteSettings::class)) + + + + +
+ @csrf + + @error('one_time_password') + + {{ $message }} + + @enderror + + +
+ + + +@section('content') + +@endsection