2FA initial

This commit is contained in:
AGuyNamedJens 2023-12-10 21:59:14 +01:00
parent a471cb4021
commit eac672a870
15 changed files with 708 additions and 12 deletions

10
.gitignore vendored
View file

@ -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

View file

@ -0,0 +1,29 @@
<?php
namespace App\Console\Commands\Overrides;
use Illuminate\Foundation\Console\KeyGenerateCommand as BaseKeyGenerateCommand;
class KeyGenerateCommand extends BaseKeyGenerateCommand
{
/**
* Override the default Laravel key generation command to throw a warning to the user
* if it appears that they have already generated an application encryption key.
* Credits: Pterodactyl Panel
*/
public function handle()
{
if (!empty(config('app.key')) && $this->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();
}
}

View file

@ -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)
{

View file

@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use PragmaRX\Google2FA\Google2FA;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
class Login2FAController extends Controller
{
private $secret;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->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'
]);
}
}

View file

@ -0,0 +1,130 @@
<?php
namespace Pterodactyl\Http\Controllers\Auth;
use App\Models\User;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use PragmaRX\Google2FA\Google2FA;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
class LoginCheckpointController extends LoginController
{
use AuthenticatesUsers;
private const TOKEN_EXPIRED_MESSAGE = 'The authentication token provided has expired, please refresh the page and try again.';
/**
* LoginCheckpointController constructor.
*/
public function __construct(
private Encrypter $encrypter,
private Google2FA $google2FA,
private ValidationFactory $validation
) {
parent::__construct();
}
/**
* Handle a login where the user is required to provide a TOTP authentication
* token. Once a user has reached this stage it is assumed that they have already
* provided a valid username and password.
*
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
* @throws \Exception
* @throws \Illuminate\Validation\ValidationException
*/
public function __invoke(Request $request): JsonResponse
{
if ($this->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;
}
}

View file

@ -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,
];
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property int $user_id
* @property string $token
* @property CarbonImmutable $created_at
* @property User $user
*/
class RecoveryToken extends Model
{
/**
* There are no updates to this model, only inserts and deletes.
*/
public const UPDATED_AT = null;
public $timestamps = true;
protected bool $immutableDates = true;
public static array $validationRules = [
'token' => 'required|string',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View file

@ -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),
);
}
}

0
bootstrap/cache/.gitignore vendored Executable file → Normal file
View file

View file

@ -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",

370
composer.lock generated
View file

@ -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"
}

View file

@ -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,
];
}
}

2
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "controlpanel",
"name": "billingV1",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -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');
});

View file

@ -0,0 +1,23 @@
@extends('layouts.app')
@php($website_settings = app(App\Settings\WebsiteSettings::class))
<body class="hold-transition dark-mode login-page">
<img src="data:image/png;base64, {{$qrcode_image}} "/>
<form action="/2fa/authenticate" method="POST">
@csrf
<input name="one_time_password" type="text">
@error('one_time_password')
<span class="text-danger" role="alert">
<small><strong>{{ $message }}</strong></small>
</span>
@enderror
<button type="submit">Authenticate</button>
</form>
</body>
@section('content')
@endsection