diff --git a/app/Actions/Fortify/AdminRedirectIfTwoFactorAuthenticatable.php b/app/Actions/Fortify/AdminRedirectIfTwoFactorAuthenticatable.php new file mode 100644 index 0000000..b7a9152 --- /dev/null +++ b/app/Actions/Fortify/AdminRedirectIfTwoFactorAuthenticatable.php @@ -0,0 +1,166 @@ +guard = $guard; + $this->limiter = $limiter; + $this->model = $this->guard->getProvider()->getModel(); + } + + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @param callable $next + * @return mixed + */ + public function handle($request, $next) + { + $user = $this->validateCredentials($request); + ($this->model==='App\Models\Admin') ? Session::put('active_two_factor','admin') : Session::put('active_two_factor','web'); + if (Fortify::confirmsTwoFactorAuthentication()) { + if (optional($user)->two_factor_secret && + !is_null(optional($user)->two_factor_confirmed_at) && + in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) { + return $this->twoFactorChallengeResponse($request, $user); + } else { + return $next($request); + } + } + + if (optional($user)->two_factor_secret && + in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) { + return $this->twoFactorChallengeResponse($request, $user); + } + + return $next($request); + } + + /** + * Attempt to validate the incoming credentials. + * + * @param \Illuminate\Http\Request $request + * @return mixed + */ + protected function validateCredentials($request) + { + if (Fortify::$authenticateUsingCallback) { + return tap(call_user_func(Fortify::$authenticateUsingCallback, $request), function ($user) use ($request) { + if (!$user) { + $this->fireFailedEvent($request); + + $this->throwFailedAuthenticationException($request); + } + }); + } + + $model = $this->guard->getProvider()->getModel(); + + return tap($model::where(Fortify::username(), $request->{Fortify::username()})->first(), function ($user) use ($request) { + if (!$user || !$this->guard->getProvider()->validateCredentials($user, ['password' => $request->password])) { + $this->fireFailedEvent($request, $user); + + $this->throwFailedAuthenticationException($request); + } + }); + } + + /** + * Throw a failed authentication validation exception. + * + * @param \Illuminate\Http\Request $request + * @return void + * + * @throws \Illuminate\Validation\ValidationException + */ + protected function throwFailedAuthenticationException($request) + { + $this->limiter->increment($request); + + throw ValidationException::withMessages([ + Fortify::username() => [trans('auth.failed')], + ]); + } + + /** + * Fire the failed authentication attempt event with the given arguments. + * + * @param \Illuminate\Http\Request $request + * @param \Illuminate\Contracts\Auth\Authenticatable|null $user + * @return void + */ + protected function fireFailedEvent($request, $user = null) + { + event(new Failed(config('fortify.guard'), $user, [ + Fortify::username() => $request->{Fortify::username()}, + 'password' => $request->password, + ])); + } + + /** + * Get the two factor authentication enabled response. + * + * @param \Illuminate\Http\Request $request + * @param mixed $user + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function twoFactorChallengeResponse($request, $user) + { + $request->session()->put([ + 'login.id' => $user->getKey(), + 'login.remember' => $request->filled('remember'), + ]); + + TwoFactorAuthenticationChallenged::dispatch($user); + $table = $user->getTable(); + Session::put('table', $table); + + if ($user->getTable() == 'admins') { + + return $request->wantsJson() + ? response()->json(['admin.two_factor' => true]) + : redirect()->route('admin.two-factor.login'); + } else { + + return $request->wantsJson() + ? response()->json(['two_factor' => true]) + : redirect()->route('two-factor.login'); + } + + } +} diff --git a/app/Actions/Fortify/Controllers/TwoFactorAuthenticatedSessionController.php b/app/Actions/Fortify/Controllers/TwoFactorAuthenticatedSessionController.php index 2228c82..565fdc5 100644 --- a/app/Actions/Fortify/Controllers/TwoFactorAuthenticatedSessionController.php +++ b/app/Actions/Fortify/Controllers/TwoFactorAuthenticatedSessionController.php @@ -74,11 +74,13 @@ public function store(TwoFactorLoginRequest $request) public function adminTwoFactorLogout(Request $request) { + $session=Session::get('active_two_factor'); Auth::guard('admin')->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); - return (Session::get('active_two_factor') ? redirect()->route('admin.login') : redirect()->route('login')); + + return (($session === 'admin') ? redirect()->route('admin.login') : redirect()->route('login')); } } diff --git a/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php b/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php index c64ae10..4f109d6 100644 --- a/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php +++ b/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php @@ -19,7 +19,7 @@ class RedirectIfTwoFactorAuthenticatable * * @var \Illuminate\Contracts\Auth\StatefulGuard */ - protected $guard; + protected $guard,$model; /** * The login rate limiter instance. @@ -37,11 +37,9 @@ class RedirectIfTwoFactorAuthenticatable */ public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter) { - $this->guard = $guard; $this->limiter = $limiter; - $model = $this->guard->getProvider()->getModel(); - + $this->model = $this->guard->getProvider()->getModel(); } /** @@ -53,9 +51,8 @@ public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter) */ public function handle($request, $next) { - $user = $this->validateCredentials($request); - Session::put('active_two_factor','admin'); + ($this->model==='App\Models\Admin') ? Session::put('active_two_factor','admin') : Session::put('active_two_factor','web'); if (Fortify::confirmsTwoFactorAuthentication()) { if (optional($user)->two_factor_secret && !is_null(optional($user)->two_factor_confirmed_at) && diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index df7e64e..9e6d306 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Actions\Fortify\AdminRedirectIfTwoFactorAuthenticatable; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Http\Request; use Illuminate\Routing\Controller; @@ -39,9 +40,11 @@ public function __construct(StatefulGuard $guard) $this->guard = $guard; + } public function loginForm(){ + return view('auth.login',['guard'=>'admin']); } @@ -64,7 +67,8 @@ public function create(Request $request): LoginViewResponse */ public function store(LoginRequest $request) { - + Session::forget('guard'); + Session::put('guard','admin'); return $this->loginPipeline($request)->then(function ($request) { return app(AdminLoginResponse::class); }); @@ -94,7 +98,7 @@ protected function loginPipeline(LoginRequest $request) return (new Pipeline(app()))->send($request)->through(array_filter([ config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class, - Features::enabled(Features::twoFactorAuthentication()) ? RedirectIfTwoFactorAuthenticatable::class : null, + Features::enabled(Features::twoFactorAuthentication()) ? AdminRedirectIfTwoFactorAuthenticatable::class : null, AttemptToAuthenticate::class, PrepareAuthenticatedSession::class, @@ -109,7 +113,6 @@ protected function loginPipeline(LoginRequest $request) */ public function destroy(Request $request): AdminLogoutResponse { - Auth::guard('admin')->logout(); $request->session()->invalidate(); diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 4d83a28..dcf8835 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -6,15 +6,14 @@ use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Routing\Pipeline; -use App\Actions\Fortify\AttemptToAuthenticate; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Session; +use Laravel\Fortify\Actions\AttemptToAuthenticate; use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled; use Laravel\Fortify\Actions\PrepareAuthenticatedSession; use App\Actions\Fortify\RedirectIfTwoFactorAuthenticatable; -use App\Http\Responses\AdminLoginResponse; +use Laravel\Fortify\Contracts\LoginResponse; use Laravel\Fortify\Contracts\LoginViewResponse; -use App\Http\Responses\LogoutResponse; +use Laravel\Fortify\Contracts\LogoutResponse; use Laravel\Fortify\Features; use Laravel\Fortify\Fortify; use Laravel\Fortify\Http\Requests\LoginRequest; @@ -31,49 +30,46 @@ class UserController extends Controller /** * Create a new controller instance. * - * @param \Illuminate\Contracts\Auth\StatefulGuard $guard + * @param \Illuminate\Contracts\Auth\StatefulGuard $guard * @return void */ public function __construct(StatefulGuard $guard) { $this->guard = $guard; - $model = ($guard->getProvider()->getModel()); - if ($model === 'App\Models\User') Session::put('guard', 'web'); - } - public function loginForm(){ - return view('auth.login',['guard'=>'admin']); } /** * Show the login view. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @return \Laravel\Fortify\Contracts\LoginViewResponse */ public function create(Request $request): LoginViewResponse { + return app(LoginViewResponse::class); } /** * Attempt to authenticate a new session. * - * @param \Laravel\Fortify\Http\Requests\LoginRequest $request + * @param \Laravel\Fortify\Http\Requests\LoginRequest $request * @return mixed */ public function store(LoginRequest $request) { - + Session::forget('guard'); + Session::put('guard','web'); return $this->loginPipeline($request)->then(function ($request) { - return app(AdminLoginResponse::class); + return app(LoginResponse::class); }); } /** * Get the authentication pipeline instance. * - * @param \Laravel\Fortify\Http\Requests\LoginRequest $request + * @param \Laravel\Fortify\Http\Requests\LoginRequest $request * @return \Illuminate\Pipeline\Pipeline */ protected function loginPipeline(LoginRequest $request) @@ -85,7 +81,7 @@ protected function loginPipeline(LoginRequest $request) } if (is_array(config('fortify.pipelines.login'))) { - return (new \Illuminate\Pipeline\Pipeline(app()))->send($request)->through(array_filter( + return (new Pipeline(app()))->send($request)->through(array_filter( config('fortify.pipelines.login') )); } @@ -101,7 +97,7 @@ protected function loginPipeline(LoginRequest $request) /** * Destroy an authenticated session. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @return \Laravel\Fortify\Contracts\LogoutResponse */ public function destroy(Request $request): LogoutResponse diff --git a/app/Http/Middleware/CheckGuard.php b/app/Http/Middleware/CheckGuard.php index d149737..54c7cac 100644 --- a/app/Http/Middleware/CheckGuard.php +++ b/app/Http/Middleware/CheckGuard.php @@ -4,6 +4,8 @@ use Closure; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Session; class CheckGuard @@ -17,9 +19,16 @@ class CheckGuard */ public function handle(Request $request, Closure $next) { - if (Session::get('active_two_factor') === 'admin') { - return redirect()->route('admin.two-factor.login'); + if (Route::currentRouteName() !== 'admin.two-factor.login') { + return redirect()->route('admin.two-factor.login'); + } + } else if (Session::get('active_two_factor') === 'web') { + if (Route::currentRouteName() !== 'two-factor.login') { + return redirect()->route('two-factor.login'); + } + } else if (Auth::guard('admin')->check() || Auth::guard('web')->check()) { + return redirect()->back(); } return $next($request); } diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index f9fe264..0454e04 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -1,10 +1,12 @@ app - ->when([AdminController::class, AttemptToAuthenticate::class, RedirectIfTwoFactorAuthenticatable::class,ConfirmPassword::class,PrepareAuthenticatedSession::class, - PasswordResetLinkController::class, NewPasswordController::class,ProfileInformationController::class, UpdateUserProfileInformation::class,StatefulGuard::class, + ->when([AdminController::class, AttemptToAuthenticate::class, AdminRedirectIfTwoFactorAuthenticatable::class,ConfirmPassword::class,PrepareAuthenticatedSession::class, + PasswordResetLinkController::class, NewPasswordController::class,ProfileInformationController::class, UpdateUserProfileInformation::class, TwoFactorAuthenticatedSessionController::class,TwoFactorLoginRequest::class]) ->needs(StatefulGuard::class) ->give(function () { + return Auth::guard('admin'); }); + $this->app + ->when([UserController::class,RedirectIfTwoFactorAuthenticatable::class]) + ->needs(StatefulGuard::class) + ->give(function () { + + return Auth::guard('web'); + }); + $this->app->instance(FailedTwoFactorLoginResponse::class, new class implements FailedTwoFactorLoginResponse { public function toResponse($request) { diff --git a/resources/views/navigation-menu.blade.php b/resources/views/navigation-menu.blade.php index fcfab70..4037798 100644 --- a/resources/views/navigation-menu.blade.php +++ b/resources/views/navigation-menu.blade.php @@ -10,7 +10,7 @@ @@ -131,7 +131,7 @@ class="inline-flex items-center px-3 py-2 border border-transparent text-sm lead {{ __('Manage Account') }} - + {{ __('Profile') }} @@ -144,7 +144,7 @@ class="inline-flex items-center px-3 py-2 border border-transparent text-sm lead
-
+ @csrf 'admin.' ], function () { Route::middleware('guard-check')->get('login', [\App\Http\Controllers\AdminController::class, 'loginForm'])->name('login'); - Route::post('login', [\App\Http\Controllers\AdminController::class, 'store']); + Route::middleware('guard-check')->post('login', [\App\Http\Controllers\AdminController::class, 'store']); Route::post('two-factor-logout', [TwoFactorAuthenticatedSessionController::class, 'adminTwoFactorLogout'])->name('twoFactorLogout'); // Route::get('forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'create'])->name('password.request'); // Route::post('forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'store'])->name('password.email'); // Route::get('reset-password/{token}', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'create'])->name('password.reset'); // Route::post('reset-password', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'store'])->name('password.update'); - Route::get('/forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'create'])->name('password.request'); - Route::get('/reset-password/{token}', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'create'])->name('password.reset'); - Route::post('/forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'store'])->name('password.email'); - Route::post('/reset-password', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'store'])->name('password.update'); - Route::get('/two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])->name('two-factor.login'); + Route::middleware('guard-check')->get('/forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'create'])->name('password.request'); + Route::middleware('guard-check')->get('/reset-password/{token}', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'create'])->name('password.reset'); + Route::middleware('guard-check')->post('/forgot-password', [\App\Actions\Fortify\Controllers\PasswordResetLinkController::class, 'store'])->name('password.email'); + Route::middleware('guard-check')->post('/reset-password', [\App\Actions\Fortify\Controllers\NewPasswordController::class, 'store'])->name('password.update'); + Route::middleware('guard-check')->get('/two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])->name('two-factor.login'); Route::post('/two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'store'])->name('two-factor.store');; // Route::put('/user/profile-information', [ProfileInformationController::class, 'update']) diff --git a/routes/web.php b/routes/web.php index fa8a615..db4ba0d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,9 @@ 'guest:web', ], function () { - Route::middleware('guard-check')->get('/login', [\Laravel\Fortify\Http\Controllers\AuthenticatedSessionController::class, 'create'])->name('login'); + Route::middleware('guard-check')->get('/login', [UserController::class, 'create'])->name('login'); + Route::post('/login', [UserController::class, 'store']); + Route::middleware('guard-check')->get('/two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])->name('two-factor.login'); + Route::middleware('guard-check')->get('/forgot-password', [PasswordResetLinkController::class, 'create'])->name('password.request'); + Route::middleware('guard-check')->get('/reset-password/{token}', [NewPasswordController::class, 'create'])->name('password.reset'); + Route::middleware('guard-check')->post('/forgot-password', [PasswordResetLinkController::class, 'store'])->name('password.email'); + Route::middleware('guard-check')->post('/reset-password', [NewPasswordController::class, 'store'])->name('password.update'); Route::middleware('guard-check')->get('/two-factor-challenge', [TwoFactorAuthenticatedSessionController::class, 'create'])->name('two-factor.login'); });