Implemented two factor authentication with fortify

This commit is contained in:
Nima8FT 2025-04-21 12:08:22 +03:30
parent 8f1a7b3c47
commit 12b92ae3be
6 changed files with 149 additions and 4 deletions

View File

@ -6,11 +6,12 @@ use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable implements MustVerifyEmail class User extends Authenticatable implements MustVerifyEmail
{ {
/** @use HasFactory<\Database\Factories\UserFactory> */ /** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable; use HasFactory, Notifiable, TwoFactorAuthenticatable;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.

View File

@ -63,5 +63,13 @@ class FortifyServiceProvider extends ServiceProvider
Fortify::resetPasswordView(function (Request $request) { Fortify::resetPasswordView(function (Request $request) {
return view('auth.reset-password', ['request' => $request]); return view('auth.reset-password', ['request' => $request]);
}); });
Fortify::confirmPasswordView(function () {
return view('auth.confirm-password');
});
Fortify::twoFactorChallengeView(function () {
return view('auth.two-factor-challange');
});
} }
} }

View File

@ -150,7 +150,7 @@ return [
Features::updateProfileInformation(), Features::updateProfileInformation(),
Features::updatePasswords(), Features::updatePasswords(),
Features::twoFactorAuthentication([ Features::twoFactorAuthentication([
'confirm' => true, // 'confirm' => true,
'confirmPassword' => true, 'confirmPassword' => true,
// 'window' => 0, // 'window' => 0,
]), ]),

View File

@ -0,0 +1,39 @@
@extends('app')
@section('content')
<div class="h-screen w-full flex items-center justify-center bg-[#0d1b2a]">
<div class="bg-[#1b263b] p-8 rounded-xl shadow-xl max-w-sm w-full">
<h1 class="text-3xl font-semibold text-center text-[#e0e1dd] mb-6">Confirm Password</h1>
@if (session()->has('error'))
<div class="text-red-500 text-center mb-4">{{ session('error') }}</div>
@endif
<form method="POST" action="{{ route('password.confirm') }}" class="space-y-2">
@csrf
<div class="flex flex-col">
<label for="password" class="text-[#e0e1dd] mb-2">{{ __('Password') }}</label>
<input id="password" type="password" wire:model.lazy="password" name="password"
class="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required>
@error('password')
<span class="text-red-500 text-sm mt-1">{{ $message }}</span>
@enderror
</div>
<button type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg transition duration-300 cursor-pointer">
Confirm Password
</button>
@if (Route::has('password.request'))
<a class="text-blue-500 text-center mt-4 block" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,54 @@
@extends('app')
@section('content')
<div class="bg-[#0d1b2a] h-screen flex justify-center items-center space-x-6">
<div
class="flex justify-center items-center p-4 flex-wrap shadow-lg rounded-xl login-form size-150 max-w-[500px] bg-[#1b263b] h-auto text-center">
<h1 class="w-full font-bold text-4xl text-[#e0e1dd]">Two Factor Secret Code</h1>
<div class="w-[90%] p-5">
<p class="text-[#e0e1dd] text-xl mb-4 font-medium">
Please enter your secret code to login
</p>
<form method="POST" action="{{route('two-factor.login.store')}}">
@csrf
<div class="text-left my-5">
<label for="code" class="font-bold text-[#e0e1dd]">Code:</label>
<input wire:model.lazy='code' name="code" type="text" placeholder="Enter your text"
class="w-full my-2 px-4 py-2 border border-[#e0e1dd] text-[#e0e1dd] rounded-md shadow-sm focus:outline-none" />
<p class="w-full text-red-500">@error('code') {{ $message }} @enderror</p>
</div>
<div class="mt-8">
<button type="submit"
class="border border-[#e0e1dd] font-medium w-full p-2 rounded-md bg-[#e0e1dd] cursor-pointer hover:opacity-85 transition duration-300 ease-in-out text-black">Submit</button>
</div>
</form>
</div>
</div>
<div
class="flex justify-center items-center p-4 flex-wrap shadow-lg rounded-xl login-form size-150 max-w-[500px] bg-[#1b263b] h-auto text-center">
<h1 class="w-full font-bold text-4xl text-[#e0e1dd]">Two Factor Recovery Code</h1>
<div class="w-[90%] p-5">
<p class="text-[#e0e1dd] text-xl mb-4 font-medium">
Please enter your recovery code to login
</p>
<form method="POST" action="{{route('two-factor.login.store')}}">
@csrf
<div class="text-left my-5">
<label for="recovery_code" class="font-bold text-[#e0e1dd]">Recovery Code:</label>
<input wire:model.lazy='recovery_code' name="recovery_code" type="text"
placeholder="Enter your text"
class="w-full my-2 px-4 py-2 border border-[#e0e1dd] text-[#e0e1dd] rounded-md shadow-sm focus:outline-none" />
<p class="w-full text-red-500">@error('recovery_code') {{ $message }} @enderror</p>
</div>
<div class="mt-8">
<button type="submit"
class="border border-[#e0e1dd] font-medium w-full p-2 rounded-md bg-[#e0e1dd] cursor-pointer hover:opacity-85 transition duration-300 ease-in-out text-black">Submit</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@ -15,10 +15,10 @@
</div> </div>
<div class="mt-6 space-y-4"> <div class="mt-6 space-y-4">
<form action="{{route('logout')}}" method="POST"> <form action="{{route(name: 'logout')}}" method="POST">
@csrf @csrf
<button type="submit" <button type="submit"
class="block cursor-pointer text-center w-full border border-red-500 text-red-500 hover:bg-red-500 hover:text-white transition-all duration-300 rounded-lg py-2">Logout</button> class="block text-center w-full border border-red-500 text-red-500 hover:bg-red-500 hover:text-white transition-all duration-300 rounded-lg py-2 cursor-pointer">Logout</button>
</form> </form>
</div> </div>
</aside> </aside>
@ -27,6 +27,49 @@
<main class="h-screen w-full flex items-center justify-center bg-[#0d1b2a]"> <main class="h-screen w-full flex items-center justify-center bg-[#0d1b2a]">
<div class="bg-[#1b263b] p-8 rounded-xl shadow-xl max-w-[800px] w-full"> <div class="bg-[#1b263b] p-8 rounded-xl shadow-xl max-w-[800px] w-full">
<h1 class="text-3xl font-semibold text-center text-[#e0e1dd] mb-6">{{ auth()->user()->email }}</h1> <h1 class="text-3xl font-semibold text-center text-[#e0e1dd] mb-6">{{ auth()->user()->email }}</h1>
@if (session('status') === 'two-factor-authentication-enabled')
<div class="bg-green-100 text-green-800 p-4 mb-4 rounded-lg shadow-md border border-green-300">
<strong class="font-medium">Two-Factor Authentication is enabled!</strong>
</div>
@endif
@if (session('status') === 'two-factor-authentication-disabled')
<div class="bg-red-100 text-red-800 p-4 mb-4 rounded-lg shadow-md border border-red-300">
<strong class="font-medium">Two-Factor Authentication is disabled.</strong>
</div>
@endif
<form action="{{ route('two-factor.enable') }}" method="POST" class="space-y-4 text-center">
@csrf
@if (Auth::user()->two_factor_secret)
@method('DELETE')
<div class="w-48 h-48 mx-auto text-white bg-white">{!! Auth::user()->twoFactorQrCodeSvg() !!}</div>
<div>
<h4>Recovery Codes</h4>
<ul class="grid grid-cols-2 gap-3">
@foreach (json_decode(decrypt(auth()->user()->two_factor_recovery_codes)) as $recovery_code)
<li
class="bg-[#1e3a5f] text-sm text-[#cbd5e1] px-4 py-2 rounded-md border border-[#3b4b5c] tracking-wider">
{{ $recovery_code }}
</li>
@endforeach
</ul>
</div>
<button type="submit"
class="w-[50%] cursor-pointer bg-red-600 hover:bg-red-700 text-white font-semibold py-3 rounded-lg transition duration-300 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">
Disable Two-Factor Authentication
</button>
@else
<button type="submit"
class="w-[50%] cursor-pointer bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg transition duration-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
Enable Two-Factor Authentication
</button>
@endif
</form>
</div> </div>