Skip to content

Commit aecc057

Browse files
committed
Initial commit.
0 parents  commit aecc057

20 files changed

Lines changed: 5538 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor/
2+
.DS_Store

README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Basic Laravel MFA
2+
3+
## Overview
4+
5+
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.
6+
7+
## Installation
8+
9+
```bash
10+
composer require theriftlab/laravel-mfa
11+
```
12+
13+
Optionally, publish the migration:
14+
15+
```bash
16+
php artisan vendor:publish --tag=mfa-migrations
17+
```
18+
19+
Then:
20+
21+
```bash
22+
php artisan migrate
23+
```
24+
25+
## Setup
26+
27+
### Add to User Model
28+
29+
First, you will need to mark your `User` model (or whatever model you are using for Auth) as ready for MFA:
30+
31+
```diff
32+
+use Mfa\Contracts\MfaUser;
33+
+use Mfa\Concerns\Mfa;
34+
...
35+
36+
-class User extends Authenticatable
37+
+class User extends Authenticatable implements MfaUser
38+
{
39+
use HasApiTokens;
40+
use HasFactory;
41+
+ use Mfa;
42+
use Notifiable;
43+
...
44+
}
45+
```
46+
47+
### Add to Auth Flow
48+
49+
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.
50+
51+
For example, in a [Breeze](https://github.com/laravel/breeze) setup, you might add these lines into `app/Http/Controllers/Auth/AuthenticatedSessionController`:
52+
53+
```diff
54+
use Mfa\Facades\MfaAuth;
55+
56+
...
57+
58+
public function store(LoginRequest $request)
59+
{
60+
$request->authenticate();
61+
$request->session()->regenerate();
62+
63+
+ if (MfaAuth::isActive()) {
64+
+ MfaAuth::trigger();
65+
+ return redirect()->route('mfa.sent');
66+
+ }
67+
68+
...
69+
}
70+
71+
...
72+
73+
public function destroy(Request $request)
74+
{
75+
+ if (MfaAuth::isActive()) {
76+
+ MfaAuth::logout();
77+
+ }
78+
79+
Auth::guard('web')->logout();
80+
...
81+
}
82+
```
83+
84+
### Configure & Add Views
85+
86+
The email containing the signed link is a very simple template, and can be published:
87+
88+
```bash
89+
php artisan vendor:publish --tag=mfa-views
90+
```
91+
92+
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`.
93+
94+
* `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 route `mfa.resend`, which will resend the signed link email. The `$errors` session data will contain an error message if an invalid link is clicked, and `session('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.
95+
96+
* `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.
97+
98+
**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.
99+
100+
### Configuring Your Routes
101+
102+
Finally, on whichever routes you wish to protect with MFA, you can add the `mfa` middleware after `auth` - for example:
103+
104+
```php
105+
Route::get('/dashboard', function () {
106+
return view('dashboard');
107+
})->middleware(['auth', 'mfa'])->name('dashboard');
108+
```
109+
110+
This will redirect any `Auth`ed but un`MFA`ed user back to display your `auth.mfa-sent` view.
111+
112+
## Configuration
113+
114+
The default config is fairly self-explanatory and looks like this:
115+
116+
```php
117+
// Whether MFA is active
118+
'active' => env('MFA_ACTIVE', true),
119+
120+
// How many minutes the signed link lasts before timing out
121+
'link_timeout' => env('MFA_LINK_TIMEOUT', 60),
122+
123+
// How many chars long the generated code should be
124+
'code_length' => env('MFA_CODE_LENGTH', 32),
125+
126+
// URL to redirect to when link has been authorized
127+
'redirect_url' => env('MFA_REDIRECT_URL', '/'),
128+
129+
// Which model will be adopting the MfaUser functionality
130+
'model' => env('MFA_MODEL', 'App\Models\User'),
131+
```
132+
133+
You may publish the config file if you wish to change the defaults:
134+
135+
```bash
136+
php artisan vendor:publish --tag=mfa-config
137+
```

composer.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "theriftlab/laravel-mfa",
3+
"description": "Bare-bones email-based 2FA using signed links.",
4+
"autoload": {
5+
"psr-4": {
6+
"Mfa\\": "src/"
7+
}
8+
},
9+
"authors": [
10+
{
11+
"name": "Robert Davies",
12+
"email": "robert@theriftlab.com"
13+
}
14+
],
15+
"require": {
16+
"php": "^8.0",
17+
"illuminate/broadcasting": "^8.6|^9.0",
18+
"illuminate/bus": "^8.6|^9.0",
19+
"illuminate/config": "^8.6|^9.0",
20+
"illuminate/database": "^8.6|^9.0",
21+
"illuminate/events": "^8.6|^9.0",
22+
"illuminate/http": "^8.6|^9.0",
23+
"illuminate/mail": "^8.6|^9.0",
24+
"illuminate/queue": "^8.6|^9.0",
25+
"illuminate/routing": "^8.6|^9.0",
26+
"illuminate/session": "^8.6|^9.0",
27+
"illuminate/support": "^8.6|^9.0"
28+
},
29+
"extra": {
30+
"laravel": {
31+
"providers": [
32+
"Mfa\\Providers\\MfaAuthServiceProvider"
33+
]
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)