This is a bare-bones email-based 2FA package which can be configured to send out an email containing a signed link upon successful authentication. Any routes you place under the provided mfa
middleware will be inaccessible until the link is clicked.
composer require theriftlab/laravel-mfa
Optionally, publish the migration:
php artisan vendor:publish --tag=mfa-migrations
Then:
php artisan migrate
First, you will need to mark your User
model (or whatever model you are using for Auth) as ready for MFA:
+use Mfa\Contracts\MfaUser;
+use Mfa\Concerns\Mfa;
...
-class User extends Authenticatable
+class User extends Authenticatable implements MfaUser
{
use HasApiTokens;
use HasFactory;
+ use Mfa;
use Notifiable;
...
}
Due to the non-standard nature of Laravel's auth/login flow, it is up to you to decide where/when to trigger & end the MFA session using the MfaAuth
facade, which expects an authenticated user to be present in order to work.
For example, in a Breeze setup, you might add these lines into app/Http/Controllers/Auth/AuthenticatedSessionController
:
use Mfa\Facades\MfaAuth;
...
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
+ if (MfaAuth::isActive()) {
+ MfaAuth::trigger();
+ return redirect()->route('mfa.sent');
+ }
...
}
...
public function destroy(Request $request)
{
+ if (MfaAuth::isActive()) {
+ MfaAuth::logout();
+ }
Auth::guard('web')->logout();
...
}
The email containing the signed link is a very simple template, and can be published:
php artisan vendor:publish --tag=mfa-views
There are also two view files which you will need to implement: resources/views/auth/mfa-sent.blade.php
and resources/views/auth/mfa-invalid.blade.php
.
-
mfa-sent.blade.php
is shown when the user is first authorized by Laravel's default auth process and is waiting for the MFA signed link email. This template can optionally contain a link / button to POST to named routemfa.resend
, which will resend the signed link email. The$errors
session data will contain an error message if an invalid link is clicked, andsession('status')
will contain a message if the link email is resent. A logout link is also a good idea on this page to restart the whole process, in case the wrong account is logged in. -
mfa-invalid.blade.php
is shown when the user is not authorized and an invalid link is clicked, and therefore any resend / logout options are not available.
Note: when the user is not authorized and a valid link is clicked from an email (eg. the initial default auth session might have timed out), the user will be automatically logged in.
Finally, on whichever routes you wish to protect with MFA, you can add the mfa
middleware after auth
- for example:
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'mfa'])->name('dashboard');
This will redirect any Auth
ed but unMFA
ed user back to display your auth.mfa-sent
view.
The default config is fairly self-explanatory and looks like this:
// Whether MFA is active
'active' => env('MFA_ACTIVE', true),
// How many minutes the signed link lasts before timing out
'link_timeout' => env('MFA_LINK_TIMEOUT', 60),
// How many chars long the generated code should be
'code_length' => env('MFA_CODE_LENGTH', 32),
// URL to redirect to when link has been authorized
'redirect_url' => env('MFA_REDIRECT_URL', '/'),
// Which model will be adopting the MfaUser functionality
'model' => env('MFA_MODEL', 'App\Models\User'),
You may publish the config file if you wish to change the defaults:
php artisan vendor:publish --tag=mfa-config