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 #67 from DarkGhostHunter/master
Browse files Browse the repository at this point in the history
Minor fixes
  • Loading branch information
DarkGhostHunter authored Sep 6, 2021
2 parents 854560c + 70234ae commit ff3111c
Show file tree
Hide file tree
Showing 20 changed files with 72 additions and 83 deletions.
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
![](https://github.com/DarkGhostHunter/Laraguard/workflows/PHP%20Composer/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/DarkGhostHunter/Laraguard/badge.svg?branch=master)](https://coveralls.io/github/DarkGhostHunter/Laraguard?branch=master)


# Laraguard

Two-Factor Authentication via TOTP for all your users out-of-the-box.
Expand Down Expand Up @@ -35,7 +34,7 @@ This package adds a **Contract** to detect if, after the credentials are deemed

It includes a custom **view** and a **callback** to handle the Two-Factor authentication itself during login attempts.

With works without middleware or new guards, but you can go full manual if you want.
Works without middleware or new guards, but you can go full manual if you want.

## Usage

Expand Down Expand Up @@ -229,11 +228,11 @@ The following events are fired in addition to the default Authentication events.

Laraguard comes with two middleware for your routes: `2fa.enabled` and `2fa.confirm`.

> To avoid unexpected results, middleware only act on your users models with `TwoFactorAuthenticatable`. If a user model doesn't implement it, the middleware bypass any 2FA logic.
> To avoid unexpected results, middleware only act on your users models implementing the `TwoFactorAuthenticatable` contract. If a user model doesn't implement it, the middleware will 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.enabled` middleware. This middleware doesn't asks for codes, and doesn't checks for not-2FA-compatible users. It only checks if 2FA is enabled.
If you need to ensure the User has Two-Factor Authentication enabled before entering a given route, you can use the `2fa.enabled` middleware. This middleware only checks if 2FA is enabled.

```php
Route::get('system/settings')
Expand All @@ -243,7 +242,7 @@ Route::get('system/settings')

This middleware works much like Laravel's `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 the view easily with the one included in this package, with a URL to point the user to enable 2FA:
You can implement the view easily with the one included in this package, optionally with a URL to point the user to enable 2FA:

```php
use Illuminate\Support\Facades\Route;
Expand All @@ -265,23 +264,23 @@ Route::get('system/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`, if it has Two-Factor Authentication enabled.
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` if it has Two-Factor Authentication enabled.

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

Since a user without 2FA enabled won't be asked for a code, you can mix with middleware with `2fa.require` to enforce it.
Since a user without 2FA enabled won't be asked for a code, you use it with `2fa.require` to enforce it.

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

Laraguard automatically uses the [`Confirm2FACodeController`](src/Http/Controllers/Confirm2FACodeController.php) to handle the form view asking for the 2FA code for you. [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.
Laraguard uses its [`Confirm2FACodeController`](src/Http/Controllers/Confirm2FACodeController.php) to handle the form view. [You can point your own controller actions](#confirmation-middleware). The [`Confirms2FACode`](src/Http/Controllers/Confirms2FACode.php) trait will aid you in not reinventing the wheel.

## Validation

Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"name": "darkghosthunter/laraguard",
"description": "Two-Factor authentication using TOTP the easy way!",
"description": "On-premises 2FA Authentication for all your users out-of-the-box",
"keywords": [
"darkghosthunter",
"laraguard",
"laravel",
"authentication",
"2fa"
"2fa",
"two-factor",
"authentication"
],
"homepage": "https://github.com/darkghosthunter/laraguard",
"minimum-stability": "dev",
Expand Down
6 changes: 3 additions & 3 deletions config/laraguard.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@
| Safe Devices
|--------------------------------------------------------------------------
|
| Authenticating with Two Factor Codes can become very obnoxious when the
| Authenticating with Two-Factor Codes can become very obnoxious when the
| user does it every time. To "remember" a device where a 2FA code was
| validated to not ask again you can enable Safe Device to save it.
|
*/

'safe_devices' => [
'cookie' => '2fa_remember',
'cookie' => '_2fa_remember',
'enabled' => false,
'max_devices' => 3,
'expiration_days' => 14,
],

/*
|--------------------------------------------------------------------------
| Require Two Factor Middleware
| Require Two-Factor Middleware
|--------------------------------------------------------------------------
|
| When using the "2fa.confirm" middleware a view with a form will be used
Expand Down
18 changes: 9 additions & 9 deletions resources/lang/en/messages.php
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
<?php

return [
'title' => 'Two Factor Authentication',
'required' => 'Two Factor Authentication is required.',
'title' => 'Two-Factor Authentication',
'required' => 'Two-Factor Authentication is required.',
'back' => 'Go back',
'continue' => 'To continue, open up your Authenticator app and issue your 2FA code.',
'enable' => 'You need to enable Two Factor Authentication.',
'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.',
'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.',
'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.',
'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.',
],
];
];
2 changes: 1 addition & 1 deletion src/Events/TwoFactorDisabled.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class TwoFactorDisabled
{
/**
* The User using Two Factor Authentication.
* The User using Two-Factor Authentication.
*
* @var \DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Events/TwoFactorEnabled.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class TwoFactorEnabled
{
/**
* The User using Two Factor Authentication.
* The User using Two-Factor Authentication.
*
* @var \DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Events/TwoFactorRecoveryCodesDepleted.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class TwoFactorRecoveryCodesDepleted
{
/**
* The User using Two Factor Authentication.
* The User using Two-Factor Authentication.
*
* @var \DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Events/TwoFactorRecoveryCodesGenerated.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class TwoFactorRecoveryCodesGenerated
{
/**
* The User using Two Factor Authentication.
* The User using Two-Factor Authentication.
*
* @var \DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable
*/
Expand Down
7 changes: 0 additions & 7 deletions src/Exceptions/InvalidCodeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@

class InvalidCodeException extends ValidationException
{
/**
* A custom validation message.
*
* @var string
*/
protected string $validationMessage;

/**
* Create a new exception instance.
*
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/Confirm2FACodeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Confirm2FACodeController extends Controller

/*
|--------------------------------------------------------------------------
| Confirm Two Factor Code Controller
| Confirm Two-Factor Code Controller
|--------------------------------------------------------------------------
|
| This controller handles the 2FA TOTP code confirmation automatically.
Expand Down
5 changes: 5 additions & 0 deletions src/Http/Controllers/Confirms2FACode.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;

use function view;
use function response;
use function redirect;
use function now;

trait Confirms2FACode
{
/**
Expand Down
31 changes: 9 additions & 22 deletions src/Http/Middleware/ConfirmTwoFactorCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,15 @@
namespace DarkGhostHunter\Laraguard\Http\Middleware;

use Closure;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Routing\ResponseFactory;
use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

use function response;
use function url;
use function auth;

class ConfirmTwoFactorCode
{
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
* @param \Illuminate\Contracts\Routing\UrlGenerator $url
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
*/
public function __construct(
protected ResponseFactory $response,
protected UrlGenerator $url,
protected ?Authenticatable $user = null)
{
//
}

/**
* Handle an incoming request.
*
Expand All @@ -42,8 +27,8 @@ public function handle($request, Closure $next, string $redirectToRoute = '2fa.c
}

return $request->expectsJson()
? $this->response->json(['message' => trans('laraguard::messages.required')], 403)
: $this->response->redirectGuest($this->url->route($redirectToRoute));
? response()->json(['message' => trans('laraguard::messages.required')], 403)
: response()->redirectGuest(url()->route($redirectToRoute));
}

/**
Expand All @@ -53,7 +38,9 @@ public function handle($request, Closure $next, string $redirectToRoute = '2fa.c
*/
protected function userHasNotEnabledTwoFactorAuth(): bool
{
return ! ($this->user instanceof TwoFactorAuthenticatable && $this->user->hasTwoFactorEnabled());
$user = auth()->user();

return ! ($user instanceof TwoFactorAuthenticatable && $user->hasTwoFactorEnabled());
}

/**
Expand Down
24 changes: 8 additions & 16 deletions src/Http/Middleware/RequireTwoFactorEnabled.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,13 @@
namespace DarkGhostHunter\Laraguard\Http\Middleware;

use Closure;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Routing\ResponseFactory;
use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable;
use function response;
use function trans;
use function auth;

class RequireTwoFactorEnabled
{
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
*/
public function __construct(protected ResponseFactory $response, protected ?Authenticatable $user = null)
{
//
}

/**
* Handle an incoming request.
*
Expand All @@ -32,8 +22,8 @@ public function handle($request, Closure $next, string $redirectToRoute = '2fa.n
{
if ($this->hasTwoFactorAuthDisabled()) {
return $request->expectsJson()
? $this->response->json(['message' => trans('laraguard::messages.enable')], 403)
: $this->response->redirectToRoute($redirectToRoute);
? response()->json(['message' => trans('laraguard::messages.enable')], 403)
: response()->redirectToRoute($redirectToRoute);
}

return $next($request);
Expand All @@ -46,6 +36,8 @@ public function handle($request, Closure $next, string $redirectToRoute = '2fa.n
*/
protected function hasTwoFactorAuthDisabled(): bool
{
return $this->user instanceof TwoFactorAuthenticatable && ! $this->user->hasTwoFactorEnabled();
$user = auth()->user();

return $user instanceof TwoFactorAuthenticatable && ! $user->hasTwoFactorEnabled();
}
}
2 changes: 1 addition & 1 deletion src/Laraguard.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
use Illuminate\Validation\ValidationException;

use function app;
use function config;
use function trans;
use function validator;

class Laraguard
{
Expand Down
2 changes: 1 addition & 1 deletion src/LaraguardServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class LaraguardServiceProvider extends ServiceProvider
* @var string
*/
protected const MIGRATION_FILE = __DIR__ . '/../database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php';
protected const UPGRADE_FILE = __DIR__ . '/..database/migrations/0000_00_00_000000_upgrade_two_factor_authentications_table.php';
protected const UPGRADE_FILE = __DIR__ . '/../database/migrations/2020_04_02_000000_upgrade_two_factor_authentications_table.php';

/**
* Register the application services.
Expand Down
2 changes: 1 addition & 1 deletion src/TwoFactorAuthentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public function isSafeDevice(Request $request): bool
}

/**
* Returns the Two Factor Remember Token of the request.
* Returns the Two-Factor Remember Token of the request.
*
* @param \Illuminate\Http\Request $request
*
Expand Down
15 changes: 13 additions & 2 deletions tests/Http/Middleware/ConfirmTwoFactorEnabledTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,25 @@ protected function setUp(): void
$this->afterApplicationCreated([$this, 'createTwoFactorUser']);

$this->afterApplicationCreated(function () {
$this->app['router']->get('login', function () {
return 'login';
})->name('login');

$this->app['router']->get('intended', function () {
return 'ok';
})->name('intended')->middleware('web', 'auth', '2fa.confirm');
})->name('intended')->middleware('web', 'auth:web', '2fa.confirm');
});

parent::setUp();
}

public function test_guest_cant_access(): void
{
$this->assertGuest();

$this->get('intended')->assertRedirect('login');
}

public function test_continues_if_user_is_not_2fa_instance(): void
{
$this->actingAs(UserStub::create([
Expand Down Expand Up @@ -88,7 +99,7 @@ public function test_returns_ok_on_json_response(): void

$this->getJson('intended')
->assertSessionMissing('2fa.totp_confirmed_at')
->assertJson(['message' => 'Two Factor Authentication is required.'])
->assertJson(['message' => 'Two-Factor Authentication is required.'])
->assertStatus(403);

$this->postJson('2fa/confirm', [
Expand Down
2 changes: 1 addition & 1 deletion tests/Http/Middleware/RequireTwoFactorEnabledTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function test_user_2fa_not_enabled_cant_access(): void
$this->followingRedirects()->get('test')->assertSee('2fa.notice');

$this->getJson('test')
->assertJson(['message' => 'You need to enable Two Factor Authentication.'])
->assertJson(['message' => 'You need to enable Two-Factor Authentication.'])
->assertForbidden();
}

Expand Down
Loading

0 comments on commit ff3111c

Please sign in to comment.