Add all files
This commit is contained in:
72
app/Livewire/Auth/Login.php
Normal file
72
app/Livewire/Auth/Login.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Livewire\Component;
|
||||
use App\Models\User;
|
||||
use App\Traits\Captchable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Livewire\Attributes\Validate;
|
||||
|
||||
class Login extends Component
|
||||
{
|
||||
use Captchable;
|
||||
|
||||
#[Validate('required|email')]
|
||||
public string $email = '';
|
||||
|
||||
#[Validate('required')]
|
||||
public string $password = '';
|
||||
|
||||
public $remember = false;
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->captcha();
|
||||
$this->validate();
|
||||
|
||||
if (RateLimiter::tooManyAttempts('login:' . $this->email, 5)) {
|
||||
$this->addError('email', 'Too many login attempts. Please try again in 60 seconds.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RateLimiter::increment('login:' . $this->email);
|
||||
|
||||
if (!Auth::attempt($this->only('email', 'password'), $this->remember)) {
|
||||
$this->addError('email', 'These credentials do not match our records.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check 2FA
|
||||
if (Auth::user()->tfa_secret) {
|
||||
Session::put('2fa', [
|
||||
'user_id' => Auth::id(),
|
||||
'remember' => $this->remember,
|
||||
'expires' => now()->addMinutes(5),
|
||||
]);
|
||||
|
||||
Auth::logout();
|
||||
|
||||
return $this->redirect(route('2fa'), true);
|
||||
}
|
||||
|
||||
RateLimiter::clear('login:' . $this->email);
|
||||
|
||||
event(new \App\Events\Auth\Login(User::find(Auth::id())));
|
||||
|
||||
$intendedUrl = session()->pull('url.intended', default: route('dashboard'));
|
||||
$isAdminRoute = str_starts_with($intendedUrl, url('/admin'));
|
||||
|
||||
// Redirect normally if it is an admin route, otherwise navigate using livewire
|
||||
return $this->redirect($intendedUrl, navigate: !$isAdminRoute);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
}
|
||||
21
app/Livewire/Auth/Logout.php
Normal file
21
app/Livewire/Auth/Logout.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Livewire\Component;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class Logout extends Component
|
||||
{
|
||||
public function logout()
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.logout');
|
||||
}
|
||||
}
|
||||
53
app/Livewire/Auth/Password/Request.php
Normal file
53
app/Livewire/Auth/Password/Request.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth\Password;
|
||||
|
||||
use App\Helpers\NotificationHelper;
|
||||
use App\Livewire\Component;
|
||||
use App\Models\User;
|
||||
use App\Traits\Captchable;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
||||
class Request extends Component
|
||||
{
|
||||
use Captchable;
|
||||
|
||||
public string $email = '';
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->captcha();
|
||||
|
||||
$this->validate([
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
|
||||
$rateLimitKey = 'password-reset:' . request()->ip();
|
||||
|
||||
if (RateLimiter::tooManyAttempts($rateLimitKey, 3)) {
|
||||
$this->addError('email', 'Too many password reset attempts. Please try again later.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RateLimiter::hit($rateLimitKey, 60);
|
||||
|
||||
// Find the user
|
||||
$user = User::where('email', $this->email)->first();
|
||||
|
||||
if ($user && !$user?->role) {
|
||||
NotificationHelper::passwordResetNotification($user, ['url' => url(route('password.reset', [
|
||||
'token' => Password::createToken($user),
|
||||
'email' => $user->email,
|
||||
], false))]);
|
||||
}
|
||||
|
||||
$this->notify('If the email address is associated with an account, you will receive an email with instructions on how to reset your password.', 'success');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.password.request');
|
||||
}
|
||||
}
|
||||
69
app/Livewire/Auth/Password/Reset.php
Normal file
69
app/Livewire/Auth/Password/Reset.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth\Password;
|
||||
|
||||
use App\Livewire\Component;
|
||||
use App\Models\User;
|
||||
use App\Traits\Captchable;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Attributes\Validate;
|
||||
|
||||
class Reset extends Component
|
||||
{
|
||||
use Captchable;
|
||||
|
||||
public string $token;
|
||||
|
||||
#[Url, Locked]
|
||||
public string $email = '';
|
||||
|
||||
#[Validate('required|string|min:8|confirmed')]
|
||||
public string $password = '';
|
||||
|
||||
#[Validate('required')]
|
||||
public string $password_confirmation = '';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (!$this->token || !Request::has('email')) {
|
||||
return abort(404);
|
||||
}
|
||||
// Validate token
|
||||
if (!Password::tokenExists(User::where('email', $this->email)->firstOrFail(), $this->token)) {
|
||||
return abort(404);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->captcha();
|
||||
|
||||
$status = Password::reset(
|
||||
['email' => $this->email, 'password' => $this->password, 'password_confirmation' => $this->password_confirmation, 'token' => $this->token],
|
||||
function ($user) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($this->password),
|
||||
])->setRememberToken(Str::random(60));
|
||||
|
||||
$user->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
return $status === Password::PASSWORD_RESET ? $this->redirect(route('login')) : $this->notify($status, 'error');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.password.reset');
|
||||
}
|
||||
}
|
||||
66
app/Livewire/Auth/Register.php
Normal file
66
app/Livewire/Auth/Register.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Attributes\DisabledIf;
|
||||
use App\Livewire\ComponentWithProperties;
|
||||
use App\Models\User;
|
||||
use App\Traits\Captchable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
#[DisabledIf('registration_disabled')]
|
||||
class Register extends ComponentWithProperties
|
||||
{
|
||||
use Captchable;
|
||||
|
||||
public string $first_name = '';
|
||||
|
||||
public string $last_name = '';
|
||||
|
||||
public string $email = '';
|
||||
|
||||
public string $password = '';
|
||||
|
||||
public string $password_confirmation = '';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->initializeProperties(null, User::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return array_merge([
|
||||
'first_name' => 'required|string|max:255',
|
||||
'last_name' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
], $this->getRulesForProperties());
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$validatedData = $this->validate();
|
||||
|
||||
$user = User::create([
|
||||
'first_name' => $this->first_name,
|
||||
'last_name' => $this->last_name,
|
||||
'email' => $this->email,
|
||||
'password' => Hash::make($this->password),
|
||||
]);
|
||||
|
||||
if (array_key_exists('properties', $validatedData)) {
|
||||
$this->updateProperties($user, $validatedData['properties']);
|
||||
}
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return $this->redirectIntended(route('dashboard'), true);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.register');
|
||||
}
|
||||
}
|
||||
74
app/Livewire/Auth/Tfa.php
Normal file
74
app/Livewire/Auth/Tfa.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Livewire\Component;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use RobThree\Auth\Providers\Qr\EndroidQrCodeProvider;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
|
||||
class Tfa extends Component
|
||||
{
|
||||
public $code;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$session = Session::get('2fa');
|
||||
if (!$session || $session['expires'] < now()) {
|
||||
return $this->redirect(route('login'), true);
|
||||
}
|
||||
}
|
||||
|
||||
public function verify()
|
||||
{
|
||||
$this->validate([
|
||||
'code' => 'required|numeric',
|
||||
]);
|
||||
|
||||
if (RateLimiter::tooManyAttempts('2fa', 5)) {
|
||||
$this->addError('code', 'Too many attempts.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RateLimiter::increment('2fa');
|
||||
|
||||
$session = Session::get('2fa');
|
||||
$user = User::findOrfail($session['user_id']);
|
||||
|
||||
if (!$user->tfa_secret) {
|
||||
return $this->redirect(route('login'), true);
|
||||
}
|
||||
|
||||
$tfa = new TwoFactorAuth(new EndroidQrCodeProvider, config('app.name'));
|
||||
if (!$tfa->verifyCode($user->tfa_secret, $this->code)) {
|
||||
return $this->addError('code', 'Invalid code.');
|
||||
}
|
||||
|
||||
// Check if code has been used before, preventing replay attacks
|
||||
if (Cache::has('tfa_used_code_' . $user->id . '_' . $this->code)) {
|
||||
return $this->addError('code', 'Invalid code.');
|
||||
}
|
||||
// Mark code as used for 60 seconds just in case
|
||||
Cache::put('tfa_used_code_' . $user->id . '_' . $this->code, true, 60);
|
||||
|
||||
Auth::loginUsingId($user->id, $session['remember']);
|
||||
|
||||
event(new \App\Events\Auth\Login(User::find(Auth::id())));
|
||||
|
||||
Session::forget('2fa');
|
||||
|
||||
RateLimiter::clear('2fa');
|
||||
|
||||
return $this->redirect(route('dashboard'), true);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.2fa');
|
||||
}
|
||||
}
|
||||
43
app/Livewire/Auth/VerifyEmail.php
Normal file
43
app/Livewire/Auth/VerifyEmail.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Helpers\NotificationHelper;
|
||||
use App\Livewire\Component;
|
||||
use App\Traits\Captchable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
||||
class VerifyEmail extends Component
|
||||
{
|
||||
use Captchable;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (Auth::user()->hasVerifiedEmail()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->captcha();
|
||||
|
||||
if (RateLimiter::tooManyAttempts('email-verification', 1)) {
|
||||
$this->addError('code', 'Too many attempts. Try again later.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationHelper::emailVerificationNotification(Auth::user());
|
||||
|
||||
RateLimiter::hit('email-verification', 120);
|
||||
|
||||
$this->notify('Verification email sent.');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('auth.verify-email');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user