Skip to content

Commit

Permalink
Implementation of Admin Two Factor Authentication Guard
Browse files Browse the repository at this point in the history
  • Loading branch information
hiamir committed Nov 1, 2022
1 parent de04459 commit 1624713
Show file tree
Hide file tree
Showing 18 changed files with 368 additions and 29 deletions.
2 changes: 2 additions & 0 deletions app/Actions/Fortify/AttemptToAuthenticate.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Auth\Events\Failed;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\LoginRateLimiter;
Expand Down Expand Up @@ -35,6 +36,7 @@ public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter)
{
$this->guard = $guard;
$this->limiter = $limiter;

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

namespace App\Actions\Fortify\Controllers;


use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Laravel\Fortify\Contracts\FailedTwoFactorLoginResponse;
use Laravel\Fortify\Contracts\TwoFactorChallengeViewResponse;
use Laravel\Fortify\Contracts\TwoFactorLoginResponse;
use Laravel\Fortify\Events\RecoveryCodeReplaced;
use Laravel\Fortify\Http\Requests\TwoFactorLoginRequest;
use App\Actions\Fortify\Requests\TwoFactorLoginRequest;

class TwoFactorAuthenticatedSessionController extends Controller
{
Expand All @@ -23,7 +27,7 @@ class TwoFactorAuthenticatedSessionController 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)
Expand All @@ -34,12 +38,12 @@ public function __construct(StatefulGuard $guard)
/**
* Show the two factor authentication challenge view.
*
* @param \Laravel\Fortify\Http\Requests\TwoFactorLoginRequest $request
* @param \Laravel\Fortify\Http\Requests\TwoFactorLoginRequest $request
* @return \Laravel\Fortify\Contracts\TwoFactorChallengeViewResponse
*/
public function create(TwoFactorLoginRequest $request): TwoFactorChallengeViewResponse
{
if (! $request->hasChallengedUser()) {
if (!$request->hasChallengedUser()) {
throw new HttpResponseException(redirect()->route('admin.login'));
}

Expand All @@ -49,25 +53,32 @@ public function create(TwoFactorLoginRequest $request): TwoFactorChallengeViewRe
/**
* Attempt to authenticate a new session using the two factor authentication code.
*
* @param \Laravel\Fortify\Http\Requests\TwoFactorLoginRequest $request
* @param \Laravel\Fortify\Http\Requests\TwoFactorLoginRequest $request
* @return mixed
*/
public function store(TwoFactorLoginRequest $request)
{
$user = $request->challengedUser();

if ($code = $request->validRecoveryCode()) {
$user->replaceRecoveryCode($code);

event(new RecoveryCodeReplaced($user, $code));
} elseif (! $request->hasValidCode()) {
} elseif (!$request->hasValidCode()) {
return app(FailedTwoFactorLoginResponse::class)->toResponse($request);
}

$this->guard->login($user, $request->remember());

$request->session()->regenerate();

return app(TwoFactorLoginResponse::class);
}

public function adminTwoFactorLogout(Request $request)
{
Auth::guard('admin')->logout();

$request->session()->invalidate();

$request->session()->regenerateToken();
return (Session::get('active_two_factor') ? redirect()->route('admin.login') : redirect()->route('login'));
}
}
54 changes: 54 additions & 0 deletions app/Actions/Fortify/PrepareAuthenticatedSession.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Actions\Fortify;

use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Support\Facades\Session;
use Laravel\Fortify\LoginRateLimiter;

class PrepareAuthenticatedSession
{
/**
* The guard implementation.
*
* @var \Illuminate\Contracts\Auth\StatefulGuard
*/
protected $guard;

/**
* The login rate limiter instance.
*
* @var \Laravel\Fortify\LoginRateLimiter
*/
protected $limiter;


/**
* Create a new class instance.
*
* @param \Laravel\Fortify\LoginRateLimiter $limiter
* @return void
*/
public function __construct(StatefulGuard $guard,LoginRateLimiter $limiter)
{
$this->guard = $guard;
$this->limiter = $limiter;
}

/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @param callable $next
* @return mixed
*/
public function handle($request, $next)
{
$request->session()->regenerate();

$this->limiter->clear($request);


return $next($request);
}
}
9 changes: 8 additions & 1 deletion app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Auth\Events\Failed;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Events\TwoFactorAuthenticationChallenged;
use Laravel\Fortify\Fortify;
Expand Down Expand Up @@ -39,6 +40,8 @@ public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter)

$this->guard = $guard;
$this->limiter = $limiter;
$model = $this->guard->getProvider()->getModel();

}

/**
Expand All @@ -50,8 +53,9 @@ public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter)
*/
public function handle($request, $next)
{
$user = $this->validateCredentials($request);

$user = $this->validateCredentials($request);
Session::put('active_two_factor','admin');
if (Fortify::confirmsTwoFactorAuthentication()) {
if (optional($user)->two_factor_secret &&
!is_null(optional($user)->two_factor_confirmed_at) &&
Expand Down Expand Up @@ -146,13 +150,16 @@ protected function twoFactorChallengeResponse($request, $user)
]);

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');
Expand Down
141 changes: 141 additions & 0 deletions app/Actions/Fortify/Requests/TwoFactorLoginRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

namespace App\Actions\Fortify\Requests;

use App\Http\Controllers\AdminController;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Laravel\Fortify\Contracts\FailedTwoFactorLoginResponse;
use Laravel\Fortify\Contracts\TwoFactorAuthenticationProvider;

class TwoFactorLoginRequest extends FormRequest
{
/**
* The user attempting the two factor challenge.
*
* @var mixed
*/
protected $challengedUser;

/**
* Indicates if the user wished to be remembered after login.
*
* @var bool
*/
protected $remember;

/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'code' => 'nullable|string',
'recovery_code' => 'nullable|string',
];
}

/**
* Determine if the request has a valid two factor code.
*
* @return bool
*/
public function hasValidCode()
{
return $this->code && tap(app(TwoFactorAuthenticationProvider::class)->verify(
decrypt($this->challengedUser()->two_factor_secret), $this->code
), function ($result) {
if ($result) {
$this->session()->forget('login.id');
}
});
}

/**
* Get the valid recovery code if one exists on the request.
*
* @return string|null
*/
public function validRecoveryCode()
{
if (! $this->recovery_code) {
return;
}

return tap(collect($this->challengedUser()->recoveryCodes())->first(function ($code) {
return hash_equals($this->recovery_code, $code) ? $code : null;
}), function ($code) {
if ($code) {
$this->session()->forget('login.id');
}
});
}

/**
* Determine if there is a challenged user in the current session.
*
* @return bool
*/
public function hasChallengedUser()
{
if ($this->challengedUser) {
return true;
}

// $model = app(StatefulGuard::class)->getProvider()->getModel();
$model = 'App\Models\Admin';

return $this->session()->has('login.id') &&
$model::find($this->session()->get('login.id'));
}

/**
* Get the user that is attempting the two factor challenge.
*
* @return mixed
*/
public function challengedUser()
{
if ($this->challengedUser) {
return $this->challengedUser;
}

// $model = app(StatefulGuard::class)->getProvider()->getModel();
$model = 'App\Models\Admin';
if (! $this->session()->has('login.id') ||
! $user = $model::find($this->session()->get('login.id'))) {
throw new HttpResponseException(
app(FailedTwoFactorLoginResponse::class)->toResponse($this)
);
}

return $this->challengedUser = $user;
}

/**
* Determine if the user wanted to be remembered after login.
*
* @return bool
*/
public function remember()
{
if (! $this->remember) {
$this->remember = $this->session()->pull('login.remember', false);
}

return $this->remember;
}
}
11 changes: 10 additions & 1 deletion app/Http/Controllers/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
use Illuminate\Routing\Pipeline;
use App\Actions\Fortify\AttemptToAuthenticate;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use App\Actions\Fortify\PrepareAuthenticatedSession;
use App\Actions\Fortify\RedirectIfTwoFactorAuthenticatable;
use App\Http\Responses\AdminLoginResponse;
use Laravel\Fortify\Contracts\LoginViewResponse;
Expand Down Expand Up @@ -37,6 +38,7 @@ public function __construct(StatefulGuard $guard)
{
$this->guard = $guard;


}

public function loginForm(){
Expand Down Expand Up @@ -76,6 +78,7 @@ public function store(LoginRequest $request)
*/
protected function loginPipeline(LoginRequest $request)
{

if (Fortify::$authenticateThroughCallback) {
return (new \Illuminate\Pipeline\Pipeline(app()))->send($request)->through(array_filter(
call_user_func(Fortify::$authenticateThroughCallback, $request)
Expand All @@ -90,9 +93,11 @@ 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,
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,

]));
}

Expand All @@ -104,12 +109,16 @@ protected function loginPipeline(LoginRequest $request)
*/
public function destroy(Request $request): AdminLogoutResponse
{

Auth::guard('admin')->logout();

$request->session()->invalidate();

$request->session()->regenerateToken();


return app(AdminLogoutResponse::class);
}


}
Loading

0 comments on commit 1624713

Please sign in to comment.