Add support for 2 Factor Authentification (2FA)

This commit is contained in:
DariusIII
2020-12-28 01:32:20 +01:00
parent 6c621f9fb4
commit fa3ded8b41
24 changed files with 1181 additions and 20 deletions
@@ -159,12 +159,12 @@ class RegisterController extends Controller
$confirmPassword = $request->input('password_confirmation');
$email = $request->input('email');
// Get the default user role.
$userDefault = Role::query()->where('isdefault', '=', 1)->first();
// Get the default user role.
$userDefault = Role::query()->where('isdefault', '=', 1)->first();
if (! empty($error)) {
return $this->showRegistrationForm($request, $error);
}
if (! empty($error)) {
return $this->showRegistrationForm($request, $error);
}
if (Invite::isAllowed($inviteCode, $email) || Settings::settingValue('..registerstatus') !== Settings::REGISTER_STATUS_INVITE) {
$user = $this->create(
@@ -84,18 +84,18 @@ class ResetPasswordController extends Controller
$content = app('smarty.view')->fetch($theme.'/forgottenpassword.tpl');
app('smarty.view')->assign(
[
'content' => $content,
'title' => $title,
'meta_title' => $meta_title,
'meta_keywords' => $meta_keywords,
'meta_description' => $meta_description,
'email' => $ret['email'] ?? '',
'confirmed' => $confirmed,
'error' => $error,
'notice' => $onscreen,
]
);
[
'content' => $content,
'title' => $title,
'meta_title' => $meta_title,
'meta_keywords' => $meta_keywords,
'meta_description' => $meta_description,
'email' => $ret['email'] ?? '',
'confirmed' => $confirmed,
'error' => $error,
'notice' => $onscreen,
]
);
app('smarty.view')->display($theme.'/basepage.tpl');
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ class BasePageController extends Controller
*/
public function __construct()
{
$this->middleware(['auth', 'web'])->except('api', 'contact', 'showContactForm', 'callback', 'getNzb', 'terms', 'capabilities', 'movie', 'apiSearch', 'tv', 'details', 'failed', 'showRssDesc', 'fullFeedRss', 'categoryFeedRss', 'cartRss', 'myMoviesRss', 'myShowsRss');
$this->middleware(['auth', 'web', '2fa'])->except('api', 'contact', 'showContactForm', 'callback', 'getNzb', 'terms', 'capabilities', 'movie', 'apiSearch', 'tv', 'details', 'failed', 'showRssDesc', 'fullFeedRss', 'categoryFeedRss', 'cartRss', 'myMoviesRss', 'myShowsRss');
// Buffer settings/DB connection.
$this->settings = new Settings();
$this->smarty = app('smarty.view');
@@ -0,0 +1,101 @@
<?php
namespace App\Http\Controllers;
use App\Models\PasswordSecurity;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class PasswordSecurityController extends Controller
{
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/
public function show2faForm(Request $request)
{
$user = Auth::user();
$google2fa_url = '';
if ($user->passwordSecurity()->exists()) {
$google2fa_url = \Google2FA::getQRCodeInline(
config('app.name'),
$user->email,
$user->passwordSecurity->google2fa_secret
);
}
$data = [
'user' => $user,
'google2fa_url' => $google2fa_url,
];
return view('auth.2fa')->with('data', $data);
}
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
*/
public function generate2faSecret(Request $request)
{
$user = Auth::user();
// Add the secret key to the registration data
PasswordSecurity::create(
[
'user_id' => $user->id,
'google2fa_enable' => 0,
'google2fa_secret' => \Google2FA::generateSecretKey(),
]
);
return redirect('2fa')->with('success', 'Secret Key is generated, Please verify Code to Enable 2FA');
}
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
*/
public function enable2fa(Request $request)
{
$user = Auth::user();
$secret = $request->input('verify-code');
$valid = \Google2FA::verifyKey($user->passwordSecurity->google2fa_secret, $secret);
if ($valid) {
$user->passwordSecurity->google2fa_enable = 1;
$user->passwordSecurity->save();
return redirect('2fa')->with('success', '2FA is Enabled Successfully.');
}
return redirect('2fa')->with('error', 'Invalid Verification Code, Please try again.');
}
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function disable2fa(Request $request)
{
if (! (Hash::check($request->get('current-password'), Auth::user()->password))) {
// The passwords matches
return redirect()->back()->with('error', 'Your password does not match with your account password. Please try again.');
}
$validatedData = $request->validate([
'current-password' => 'required',
]);
$user = Auth::user();
$user->passwordSecurity->google2fa_enable = 0;
$user->passwordSecurity->save();
return redirect('2fa')->with('success', '2FA is now Disabled.');
}
}
+1
View File
@@ -63,5 +63,6 @@ class Kernel extends HttpKernel
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
'clearance' => \App\Http\Middleware\ClearanceMiddleware::class,
'2fa' => \App\Http\Middleware\Google2FAMiddleware::class,
];
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use App\Support\Google2FAAuthenticator;
use Closure;
class Google2FAMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$authenticator = app(Google2FAAuthenticator::class)->boot($request);
if ($authenticator->isAuthenticated()) {
return $next($request);
}
return $authenticator->makeRequestOneTimePasswordResponse();
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PasswordSecurity extends Model
{
use HasFactory;
/**
* @var array
*/
protected $guarded = [];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+8
View File
@@ -946,4 +946,12 @@ class User extends Authenticatable
{
static::whereVerified(0)->where('created_at', '<', now()->subDays(3))->delete();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function passwordSecurity(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(PasswordSecurity::class);
}
}
+2
View File
@@ -16,6 +16,8 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $namespace = 'App\Http\Controllers';
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, etc.
*
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Support;
use PragmaRX\Google2FALaravel\Exceptions\InvalidSecretKey;
use PragmaRX\Google2FALaravel\Support\Authenticator;
class Google2FAAuthenticator extends Authenticator
{
/**
* @return bool
*/
protected function canPassWithoutCheckingOTP(): bool
{
if (! $this->getUser()->passwordSecurity) {
return true;
}
return
! $this->getUser()->passwordSecurity->google2fa_enable ||
! $this->isEnabled() ||
$this->noUserIsAuthenticated() ||
$this->twoFactorAuthStillValid();
}
/**
* @return mixed
* @throws \PragmaRX\Google2FALaravel\Exceptions\InvalidSecretKey
*/
protected function getGoogle2FASecretKey()
{
$secret = $this->getUser()->passwordSecurity->{$this->config('otp_secret_column')};
if (is_null($secret) || empty($secret)) {
throw new InvalidSecretKey('Secret key cannot be empty.');
}
return $secret;
}
}
+1
View File
@@ -134,6 +134,7 @@
"php-ffmpeg/php-ffmpeg": "^0.14",
"php-http/guzzle6-adapter": "^1.1",
"php-http/message": "^1.6",
"pragmarx/google2fa-laravel": "^1.4",
"predis/predis": "^1.1",
"propaganistas/laravel-disposable-email": "^2.0",
"ramsey/uuid": "^4.0",
Generated
+332 -1
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": "6f13adce07c83b25e82e85fd0f403ef9",
"content-hash": "f507d2a8906f891af68933e1d18c169f",
"packages": [
{
"name": "aharen/omdbapi",
@@ -208,6 +208,55 @@
"description": "PHP Wrapper for Accessing the Steam Storefront API",
"time": "2017-08-27T15:14:06+00:00"
},
{
"name": "bacon/bacon-qr-code",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "3e9d791b67d0a2912922b7b7c7312f4b37af41e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/3e9d791b67d0a2912922b7b7c7312f4b37af41e4",
"reference": "3e9d791b67d0a2912922b7b7c7312f4b37af41e4",
"shasum": ""
},
"require": {
"dasprid/enum": "^1.0.3",
"ext-iconv": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phly/keep-a-changelog": "^1.4",
"phpunit/phpunit": "^7 | ^8 | ^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",
"time": "2020-10-30T02:02:47+00:00"
},
{
"name": "bhuvidya/laravel-countries",
"version": "v1.0.7",
@@ -1253,6 +1302,49 @@
],
"time": "2020-09-22T11:38:01+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4"
},
"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"
],
"time": "2020-10-02T16:03:48+00:00"
},
{
"name": "dborsatto/php-giantbomb",
"version": "v2.1.0",
@@ -4916,6 +5008,68 @@
],
"time": "2020-11-07T02:01:34+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c",
"reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c",
"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"
],
"time": "2020-12-06T15:14:20+00:00"
},
{
"name": "pear/archive_tar",
"version": "1.4.11",
@@ -5770,6 +5924,183 @@
],
"time": "2020-07-20T17:29:33+00:00"
},
{
"name": "pragmarx/google2fa",
"version": "8.0.0",
"source": {
"type": "git",
"url": "https://github.com/antonioribeiro/google2fa.git",
"reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/26c4c5cf30a2844ba121760fd7301f8ad240100b",
"reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b",
"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"
],
"time": "2020-04-05T10:47:18+00:00"
},
{
"name": "pragmarx/google2fa-laravel",
"version": "v1.4.1",
"source": {
"type": "git",
"url": "https://github.com/antonioribeiro/google2fa-laravel.git",
"reference": "f9014fd7ea36a1f7fffa233109cf59b209469647"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/f9014fd7ea36a1f7fffa233109cf59b209469647",
"reference": "f9014fd7ea36a1f7fffa233109cf59b209469647",
"shasum": ""
},
"require": {
"laravel/framework": ">=5.4.36|^8.0",
"php": ">=7.0",
"pragmarx/google2fa-qrcode": "^1.0"
},
"require-dev": {
"orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*",
"phpunit/phpunit": "~5|~6|~7|~8"
},
"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": [
"BSD-3-Clause"
],
"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"
],
"time": "2020-09-20T21:01:48+00:00"
},
{
"name": "pragmarx/google2fa-qrcode",
"version": "v1.0.3",
"source": {
"type": "git",
"url": "https://github.com/antonioribeiro/google2fa-qrcode.git",
"reference": "fd5ff0531a48b193a659309cc5fb882c14dbd03f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/fd5ff0531a48b193a659309cc5fb882c14dbd03f",
"reference": "fd5ff0531a48b193a659309cc5fb882c14dbd03f",
"shasum": ""
},
"require": {
"bacon/bacon-qr-code": "~1.0|~2.0",
"php": ">=5.4",
"pragmarx/google2fa": ">=4.0"
},
"require-dev": {
"khanamiryan/qrcode-detector-decoder": "^1.0",
"phpunit/phpunit": "~4|~5|~6|~7"
},
"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"
],
"time": "2019-03-20T16:42:58+00:00"
},
{
"name": "predis/predis",
"version": "v1.1.6",
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePasswordSecuritiesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_securities', function (Blueprint $table) {
$table->id();
$table->integer('user_id');
$table->boolean('google2fa_enable')->default(false);
$table->string('google2fa_secret')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_securities');
}
}
+105
View File
@@ -0,0 +1,105 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading"><strong>Two Factor Authentication</strong></div>
<div class="panel-body">
<p>Two factor authentication (2FA) strengthens access security by requiring two methods (also referred to as factors) to verify your identity. Two factor authentication protects against phishing, social engineering and password brute force attacks and secures your logins from attackers exploiting weak or stolen credentials.</p>
<br/>
<p>To Enable Two Factor Authentication on your Account, you need to do following steps</p>
<strong>
<ol>
<li>Click on Generate Secret Button , To Generate a Unique secret QR code for your profile</li>
<li>Verify the OTP from Google Authenticator Mobile App</li>
</ol>
</strong>
<br/>
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if(!($data['user']->passwordSecurity))
<form class="form-horizontal" method="POST" action="{{ route('generate2faSecret') }}">
{{ csrf_field() }}
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Generate Secret Key to Enable 2FA
</button>
</div>
</div>
</form>
@elseif(!$data['user']->passwordSecurity->google2fa_enable)
<strong>1. Scan this barcode with your Google Authenticator App:</strong><br/>
<img src="{{$data['google2fa_url'] }}" alt="">
<br/><br/>
<strong>2.Enter the pin the code to Enable 2FA</strong><br/><br/>
<form class="form-horizontal" method="POST" action="{{ route('enable2fa') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('verify-code') ? ' has-error' : '' }}">
<label for="verify-code" class="col-md-4 control-label">Authenticator Code</label>
<div class="col-md-6">
<input id="verify-code" type="password" class="form-control" name="verify-code" required>
@if ($errors->has('verify-code'))
<span class="help-block">
<strong>{{ $errors->first('verify-code') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Enable 2FA
</button>
</div>
</div>
</form>
@elseif($data['user']->passwordSecurity->google2fa_enable)
<div class="alert alert-success">
2FA is Currently <strong>Enabled</strong> for your account.
</div>
<p>If you are looking to disable Two Factor Authentication. Please confirm your password and Click Disable 2FA Button.</p>
<form class="form-horizontal" method="POST" action="{{ route('disable2fa') }}">
<div class="form-group{{ $errors->has('current-password') ? ' has-error' : '' }}">
<label for="change-password" class="col-md-4 control-label">Current Password</label>
<div class="col-md-6">
<input id="current-password" type="password" class="form-control" name="current-password" required>
@if ($errors->has('current-password'))
<span class="help-block">
<strong>{{ $errors->first('current-password') }}</strong>
</span>
@endif
</div>
</div>
<div class="col-md-6 col-md-offset-5">
{{ csrf_field() }}
<button type="submit" class="btn btn-primary ">Disable 2FA</button>
</div>
</form>
@endif
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+44
View File
@@ -0,0 +1,44 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Two Factor Authentication</div>
<div class="panel-body">
<p>Two factor authentication (2FA) strengthens access security by requiring two methods (also referred to as factors) to verify your identity. Two factor authentication protects against phishing, social engineering and password brute force attacks and secures your logins from attackers exploiting weak or stolen credentials.</p>
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
<strong>Enter the pin from Google Authenticator Enable 2FA</strong><br/><br/>
<form class="form-horizontal" action="{{ route('2faVerify') }}" method="POST">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('one_time_password-code') ? ' has-error' : '' }}">
<label for="one_time_password" class="col-md-4 control-label">One Time Password</label>
<div class="col-md-6">
<input name="one_time_password" class="form-control" type="text"/>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button class="btn btn-primary" type="submit">Authenticate</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+73
View File
@@ -0,0 +1,73 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,49 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Confirm Password') }}</div>
<div class="card-body">
{{ __('Please confirm your password before continuing.') }}
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Confirm Password') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,47 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,65 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+77
View File
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+28
View File
@@ -0,0 +1,28 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }},
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+83
View File
@@ -0,0 +1,83 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('/assets/js/all-js.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('assets/css/all-css.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
</body>
</html>
@@ -204,6 +204,16 @@
</tr>
</tbody>
</table>
<table class="data table table-striped">
<tbody>
<tr class="bg-aqua-active">
<td colspan="2" style="padding-left: 8px;"><strong>Enable or disable 2FA: </strong>
<a href="{{url("{'2fa'}")}}"> Here</a></td>
</tr>
<br>
</tbody>
</table>
{if {{App\Models\Settings::settingValue('site.main.userselstyle')}} == 1}
<table class="data table table-striped">
<tbody>
+11 -1
View File
@@ -64,6 +64,7 @@ use App\Http\Controllers\MusicController;
use App\Http\Controllers\MyMoviesController;
use App\Http\Controllers\MyShowsController;
use App\Http\Controllers\NfoController;
use App\Http\Controllers\PasswordSecurityController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\QueueController;
use App\Http\Controllers\RssController;
@@ -270,13 +271,18 @@ Route::group(['middleware' => ['isVerified']], function () {
Route::get('ajax_profile', [AjaxController::class, 'profile']);
Route::post('ajax_profile', [AjaxController::class, 'profile']);
Route::get('2fa', [PasswordSecurityController::class, 'show2faForm']);
Route::post('generate2faSecret', [PasswordSecurityController::class, 'generate2faSecret'])->name('generate2faSecret');
Route::post('2fa', [PasswordSecurityController::class, 'enable2fa'])->name('enable2fa');
Route::post('disable2fa', [PasswordSecurityController::class, 'disable2fa'])->name('disable2fa');
});
Route::get('forum-delete/{id}', [ForumController::class, 'destroy'])->middleware('role:Admin');
Route::post('forum-delete/{id}', [ForumController::class, 'destroy'])->middleware('role:Admin');
Route::group(['middleware' => ['role:Admin'], 'prefix' => 'admin', 'namespace' => 'Admin'], function () {
Route::group(['middleware' => ['role:Admin', '2fa'], 'prefix' => 'admin', 'namespace' => 'Admin'], function () {
Route::get('index', [AdminPageController::class, 'index']);
Route::get('anidb-delete/{id}', [AdminAnidbController::class, 'destroy']);
Route::post('anidb-delete/{id}', [AdminAnidbController::class, 'destroy']);
@@ -401,3 +407,7 @@ Route::group(['middleware' => ['role_or_permission:Admin|Moderator|edit release'
Route::get('release-edit', [AdminReleasesController::class, 'edit']);
Route::post('release-edit', [AdminReleasesController::class, 'edit']);
});
Route::post('2faVerify', function () {
return redirect(URL()->previous());
})->name('2faVerify')->middleware('2fa');