Skip to content
This repository has been archived by the owner on Feb 17, 2022. It is now read-only.

Commit

Permalink
Merge pull request #31 from DarkGhostHunter/2.0
Browse files Browse the repository at this point in the history
2.0
  • Loading branch information
DarkGhostHunter committed Jun 8, 2020
2 parents 8f42a48 + 18076d5 commit 88856ce
Show file tree
Hide file tree
Showing 20 changed files with 944 additions and 109 deletions.
134 changes: 113 additions & 21 deletions README.md
Expand Up @@ -27,6 +27,8 @@ This package _silently_ enables authentication using 6 digits codes, without Int
+ [Deactivation](#deactivation)
* [Events](#events)
* [Middleware](#middleware)
* [Validation](#validation)
* [Translations](#translations)
* [Protecting the Login](#protecting-the-login)
* [Configuration](#configuration)
+ [Listener](#listener)
Expand All @@ -35,6 +37,7 @@ This package _silently_ enables authentication using 6 digits codes, without Int
+ [Cache Store](#cache-store)
+ [Recovery](#recovery)
+ [Safe devices](#safe-devices)
+ [Confirmation Middleware](#confirmation-middleware)
+ [Secret length](#secret-length)
+ [TOTP configuration](#totp-configuration)
+ [QR Code Configuration](#qr-code-configuration)
Expand All @@ -58,17 +61,15 @@ This package was made to be the less invasive possible, but you can go full manu

## Usage

First, publish the migration with:

php artisan vendor:publish --provider="DarkGhostHunter\Laraguard\LaraguardServiceProvider" --tag="migrations"

> The default migration assumes you are using integers for your user model IDs. If you are using UUIDs, or some other format, adjust the format of the morphs `authenticatable` fields in the published migration before continuing.
After publishing the migration, you can create the `two_factor_authentications` table by running the migration:
First, create the `two_factor_authentications` table by running the migration:

php artisan migrate

This will create a table to handle the Two Factor Authentication information for each model you set.
This will create a table to handle the Two Factor Authentication information for each model you want to attach to 2FA.

> If you need to modify the migration from this package, you can publish it to override whatever you need.
>
> php artisan vendor:publish --provider="DarkGhostHunter\Laraguard\LaraguardServiceProvider" --tag="migrations"
Add the `TwoFactorAuthenticatable` _contract_ and the `TwoFactorAuthentication` trait to the User model, or any other model you want to make Two Factor Authentication available.

Expand Down Expand Up @@ -114,14 +115,16 @@ public function prepareTwoFactor(Request $request)

> When you use `createTwoFactorAuth()` on someone with Two Factor Authentication already enabled, the previous data becomes permanently invalid. This ensures a User **never** has two Shared Secrets enabled at any given time.
Then, the User must confirm the Shared Secret with a Code generated by their Authenticator app. This `confirmTwoFactorAuth()` method will automatically enable it if the code is valid.
Then, the User must confirm the Shared Secret with a Code generated by their Authenticator app. The `confirmTwoFactorAuth()` method will automatically enable it if the code is valid.

```php
public function confirmTwoFactor(Request $request)
{
return $request->user()->confirmTwoFactorAuth(
$activated = $request->user()->confirmTwoFactorAuth(
$request->input('2fa_code')
);

// ...
}
```

Expand All @@ -146,7 +149,7 @@ public function confirmTwoFactor(Request $request)

You're free on how to show these codes to the User, but **ensure** you show them one time after a successfully enabling Two Factor Authentication, and ask him to print them somewhere.

> These Recovery Codes are handled automatically when the User issues a Code. If it's a recovery code, the package will use it and invalidate it.
> These Recovery Codes are handled automatically when the User validates one. If it's a recovery code, the package will use and mark it as invalid.
The User can generate a fresh batch of codes using `generateRecoveryCodes()`, which automatically invalidates the previous batch.

Expand Down Expand Up @@ -194,27 +197,93 @@ The following events are fired in addition to the default Authentication events.
* `TwoFactorRecoveryCodesGenerated`: An User has generated a new set of Recovery Codes.
* `TwoFactorDisabled`: An User has disabled Two Factor Authentication.

> You can use `TwoFactorRecoveryCodesDepleted` to notify the User to create more Recovery Codes, send him to his email a new batch of codes.
> You can use `TwoFactorRecoveryCodesDepleted` to tell the User to create more Recovery Codes.
## Middleware

If you need to ensure the User has Two Factor Authentication enabled before entering a given route, you can use the `2fa` middleware.
Laraguard comes with two middleware for your routes: `2fa.require` and `2fa.confirm`.

> To avoid unexpected results, these middleware only act on your users models with `TwoFactorAuthenticatable`. If a user model doesn't implements it, the middleware bypass any 2FA logic.
### Require 2FA

If you need to ensure the User has Two Factor Authentication enabled before entering a given route, you can use the `2fa.require` middleware.

```php
Route::get('system/settings')
->uses('SystemSettingsController@show')
->middleware('2fa');
->middleware('2fa.require');
```

This middleware works much like the `verified` middleware: if the User has not enabled Two Factor Authentication, it will be redirected to a route name containing the warning, which is `2fa.notice` by default.

You can implement this easily using this package:
You can implement the view easily with the one included in this package:

```php
Route::view('2fa-required', 'laraguard::notice')->name('2fa.notice');
```

Alternatively, you can use a custom controller action to also include a link to where he can enable Two Factor Authentication.

```php
public function notice()
{
return view('2fa.notice', [
'url' => url('account/settings')
]);
}
```

### Confirm 2FA

Much like the [`password.confirm` middleware](https://laravel.com/docs/authentication#password-confirmation), you can also ask the user to confirm an action using `2fa.confirm`.

```php
Route::get('api/token')
->uses('ApiTokenController@show')
->middleware('2fa.confirm');
```

Laraguard automatically uses the [`Confirm2FACodeController`](src/Http/Controllers/Confirm2FACodeController.php) to handle the form view and the code confirmation for you.

Alternatively, [you can point your own controller actions](#confirmation-middleware) to handle the form view and confirmation. Better yet, you can start with the [`Confirms2FACode`](src/Http/Controllers/Confirms2FACode.php) trait to avoid reinventing the wheel.

## Validation

Sometimes you may want to manually trigger a TOTP validation in any part of your application for the authenticated user. You can validate a TOTP code for the authenticated user using the `totp_code` rule.

```php
public function checkTotp(Request $request)
{
$request->validate([
'code' => 'required|totp_code'
]);

// ...
}
```

This rule will succeed if the user is authenticated, is has Two Factor Authentication enabled, and the code is correct.

## Translations

Laraguard comes with translation files (only for english) that you can use immediately in your application. These are also used for the [validation rule](#validation).

```php
Route::view('2fa-required', 'laraguard::notice')
->name('2fa.notice');
public function disableTwoFactorAuth()
{
// ...

session()->flash('2fa_disabled', trans('laraguard::messages.disabled'));

return back();
}
```

To add your own in your language, publish the translation files. These will be located in `resources/vendor/laraguard`:

php artisan vendor:publish --provider="DarkGhostHunter\Laraguard\LaraguardServiceProvider" --tag="translations"

## Protecting the Login

Two Factor Authentication can be victim of brute-force attacks. The attacker will need between 16.000~34.000 requests each second to get the correct code, or less depending on the lifetime of the code.
Expand Down Expand Up @@ -269,7 +338,12 @@ return [
'enabled' => false,
'max_devices' => 3,
'expiration_days' => 14,
],
],
'confirm' => [
'timeout' => 10800,
'view' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@showConfirmForm',
'action' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@confirm'
],
'secret_length' => 20,
'issuer' => env('OTP_TOTP_ISSUER'),
'totp' => [
Expand Down Expand Up @@ -376,6 +450,24 @@ You can change the maximum number of devices saved and the amount of days of val

> When re-enabling Two Factor Authentication, the list of devices is automatically invalidated.
### Confirmation Middleware

```php
return [
'confirm' => [
'timeout' => 10800, // 3 hours
'view' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@showConfirmForm',
'action' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@confirm'
],
];
```

If the `view` or `action` are not `null`, the `2fa/notice` and `2fa/confirm` routes will be registered to handle 2FA code notice and confirmation for the [`2fa.confirm` middleware](#confirm-2fa). If you disable it, you will have to register the routes and controller actions yourself.

This array also sets by how much to "remember" the 2FA Code confirmation, and the actions used to show the view to confirm the 2FA Code with also the action to handle the confirmation.

You may want to change these, specially if you want your own view to show the confirmation form.

### Secret length

```php
Expand Down Expand Up @@ -416,7 +508,7 @@ This configuration values are always passed down to the authentication app as UR

These values are printed to each 2FA data record inside the application. Changes will only take effect for new activations.

> It's not recommended to edit these parameters if you plan to use publicly available Authenticator apps, since some of them **may not support non-standard configuration**, like more digits, different period of seconds or other algorithms.
> Do not edit these parameters if you plan to use publicly available Authenticator apps, since some of them **may not support non-standard configuration**, like more digits, different period of seconds or other algorithms.
### QR Code Configuration

Expand All @@ -438,9 +530,9 @@ This controls the size and margin used to create the QR Code, which are created
You can override the view, which handles the Two Factor Code verification for the User. It receives this data:

* `$action`: The full URL where the form should send the login credentials.
* `$credentials`: An `array|null` containing the User credentials used for the login.
* `$credentials`: An `array` containing the User credentials used for the login.
* `$user`: The User instance trying to authenticate.
* `$error`: If the Two Factor Code is invalid.
* `$error`: If the Two Factor Code is invalid.
* `$remember`: If the "remember" checkbox has been filled.

The way it works is very simple: it will hold the User credentials in a hidden input while it asks for the Two Factor Code. The User will send everything again along with the Code, the application will ensure its correct, and complete the log in.
Expand Down
39 changes: 28 additions & 11 deletions config/laraguard.php
Expand Up @@ -53,8 +53,8 @@
*/

'cache' => [
'store' => null,
'prefix' => '2fa.code'
'store' => null,
'prefix' => '2fa.code',
],

/*
Expand All @@ -70,8 +70,8 @@

'recovery' => [
'enabled' => true,
'codes' => 10,
'length' => 8,
'codes' => 10,
'length' => 8,
],

/*
Expand All @@ -86,11 +86,28 @@
*/

'safe_devices' => [
'enabled' => false,
'max_devices' => 3,
'enabled' => false,
'max_devices' => 3,
'expiration_days' => 14,
],

/*
|--------------------------------------------------------------------------
| Require Two Factor Middleware
|--------------------------------------------------------------------------
|
| When using the "2fa.confirm" middleware a view with a form will be used
| to ask the user for a TOTP code, an a controller action to receive it.
| You can change both actions and also when to forget the confirmation.
|
*/

'confirm' => [
'timeout' => 10800, // 3 hours
'view' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@showConfirmForm',
'action' => 'DarkGhostHunter\Laraguard\Http\Controllers\Confirm2FACodeController@confirm'
],

/*
|--------------------------------------------------------------------------
| Secret Length
Expand Down Expand Up @@ -118,9 +135,9 @@
'issuer' => env('OTP_TOTP_ISSUER'),

'totp' => [
'digits' => 6,
'seconds' => 30,
'window' => 1,
'digits' => 6,
'seconds' => 30,
'window' => 1,
'algorithm' => 'sha1',
],

Expand All @@ -136,7 +153,7 @@
*/

'qr_code' => [
'size' => 400,
'margin' => 4
'size' => 400,
'margin' => 4,
],
];
22 changes: 22 additions & 0 deletions resources/lang/en/messages.php
@@ -0,0 +1,22 @@
<?php

return [
'required' => 'Two Factor Authentication is required.',
'continue' => 'To continue, open up your Authenticator app and issue your 2FA code.',
'enable' => 'You need to enable Two Factor Authentication.',

'fail_confirm' => 'The code to activate Two Factor Authentication is invalid.',
'enabled' => 'Two Factor Authentication has been enabled for your account.',
'disabled' => 'Two Factor Authentication has been disabled for your account.',

'safe_device' => 'We won\'t ask you for Two Factor Authentication codes in this device for some time.',

'confirm' => 'Confirm code',
'switch_on' => 'Go to enable Two Factor Authentication.',

'recovery_code' => [
'used' => 'You have used a Recovery Code. Remember to regenerate them if you have used almost all.',
'depleted' => 'You have used all your Recovery Codes. Please use alternate authentication methods to continue.',
'generated' => 'You have generated a new set of Recovery Codes. Any previous set of codes have been invalidated.',
],
];
5 changes: 5 additions & 0 deletions resources/lang/en/validation.php
@@ -0,0 +1,5 @@
<?php

return [
'totp_code' => 'The Code is invalid or has expired.',
];
18 changes: 9 additions & 9 deletions resources/views/auth.blade.php
Expand Up @@ -9,26 +9,26 @@
@if($remember)
<input type="hidden" name="remember" value="on">
@endif

<p class="text-center">
{{ __('To log in, open up your Authenticator app and issue the 6-digit code.') }}
{{ trans('laraguard::messages.continue') }}
</p>
<div class="form-row justify-content-center py-3">
<div class="col-sm-8 col-8 mb-3">
<input type="text" name="2fa_code" id="2fa_code"
<input type="text" name="{{ $input }}" id="{{ $input }}"
class="@if($error) is-invalid @endif form-control form-control-lg"
minlength="6" placeholder="123456" required>
@if($error)
<div class="invalid-feedback">
{{ __('The Code is invalid or has expired.') }}
{{ trans('laraguard::validation.totp_code') }}
</div>
@endif
</div>
</div>
<div class="col-auto mb-3">
<button type="submit" class="btn btn-primary btn-lg">
{{ __('Log in') }}
</button>
<div class="w-100"></div>
<div class="col-auto mb-3">
<button type="submit" class="btn btn-primary btn-lg">
{{ trans('laraguard::messages.confirm') }}
</button>
</div>
</div>
</form>
@endsection

0 comments on commit 88856ce

Please sign in to comment.