mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-11-25 17:54:57 +01:00
Make Login & API throttling editable using the .env file - Close #163
This commit is contained in:
parent
9913560787
commit
140cc70cef
17
.env.example
17
.env.example
@ -103,8 +103,25 @@ MAIL_FROM_NAME=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
|
||||
|
||||
#### API settings ####
|
||||
|
||||
# The maximum number of API calls in a minute from the same IP.
|
||||
# Once reached, all requests from this IP will be rejected until the minute has elapsed.
|
||||
#
|
||||
# Set to null to disable the API throttling.
|
||||
|
||||
THROTTLE_API=60
|
||||
|
||||
|
||||
#### Authentication settings ####
|
||||
|
||||
# The number of times per minute a user can fail to log in before being locked out.
|
||||
# Once reached, all login attempts will be rejected until the minute has elapsed.
|
||||
#
|
||||
# This setting applies to both email/password and webauthn login attemps.
|
||||
|
||||
LOGIN_THROTTLE=5
|
||||
|
||||
# The default authentication guard
|
||||
#
|
||||
# Supported:
|
||||
|
9
Dockerfile
vendored
9
Dockerfile
vendored
@ -156,7 +156,16 @@ ENV \
|
||||
MAIL_ENCRYPTION=null \
|
||||
MAIL_FROM_NAME=null \
|
||||
MAIL_FROM_ADDRESS=null \
|
||||
# API settings
|
||||
# The maximum number of API calls in a minute from the same IP.
|
||||
# Once reached, all requests from this IP will be rejected until the minute has elapsed.
|
||||
# Set to null to disable the API throttling.
|
||||
THROTTLE_API=60 \
|
||||
# Authentication settings
|
||||
# The number of times per minute a user can fail to log in before being locked out.
|
||||
# Once reached, all login attempts will be rejected until the minute has elapsed.
|
||||
# This setting applies to both email/password and webauthn login attemps.
|
||||
LOGIN_THROTTLE=5 \
|
||||
# The default authentication guard
|
||||
# Supported:
|
||||
# 'web-guard' : The Laravel built-in auth system (default if nulled)
|
||||
|
@ -27,6 +27,13 @@ class LoginController extends Controller
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* The login throttle.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $maxAttempts;
|
||||
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
@ -39,6 +46,8 @@ public function login(LoginRequest $request)
|
||||
{
|
||||
Log::info(sprintf('User login requested by %s from %s', var_export($request['email'], true), $request->ip()));
|
||||
|
||||
$this->maxAttempts = config('auth.throttle.login');
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
|
@ -20,6 +20,13 @@ class WebAuthnLoginController extends Controller
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* The login throttle.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $maxAttempts;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| WebAuthn Login Controller
|
||||
@ -67,6 +74,8 @@ public function login(WebauthnAssertedRequest $request)
|
||||
{
|
||||
Log::info(sprintf('User login via webauthn requested by %s from %s', var_export($request['email'], true), $request->ip()));
|
||||
|
||||
$this->maxAttempts = config('auth.throttle.login');
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
|
@ -73,7 +73,8 @@ private function getApiNamespace(string $version)
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->ip());
|
||||
$maxAttempts = config('2fauth.api.throttle');
|
||||
return is_null($maxAttempts) ? Limit::none() : Limit::perMinute($maxAttempts)->by($request->ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
'repository' => 'https://github.com/Bubka/2FAuth',
|
||||
'latestReleaseUrl' => 'https://api.github.com/repos/Bubka/2FAuth/releases/latest',
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 2FAuth config
|
||||
@ -29,6 +28,17 @@
|
||||
'appSubdirectory' => env('APP_SUBDIRECTORY', ''),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 2FAuth API config
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
*/
|
||||
|
||||
'api' => [
|
||||
'throttle' => env('THROTTLE_API', 60),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 2FAuth available translations
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
return [
|
||||
|
||||
'throttle' => [
|
||||
'login' => env('LOGIN_THROTTLE', 5),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|
@ -54,7 +54,16 @@ services:
|
||||
- MAIL_ENCRYPTION=null
|
||||
- MAIL_FROM_NAME=null
|
||||
- MAIL_FROM_ADDRESS=null
|
||||
# API settings
|
||||
# The maximum number of API calls in a minute from the same IP.
|
||||
# Once reached, all requests from this IP will be rejected until the minute has elapsed.
|
||||
# Set to null to disable the API throttling.
|
||||
- THROTTLE_API=60
|
||||
# Authentication settings
|
||||
# The number of times per minute a user can fail to log in before being locked out.
|
||||
# Once reached, all login attempts will be rejected until the minute has elapsed.
|
||||
# This setting applies to both email/password and webauthn login attemps.
|
||||
- LOGIN_THROTTLE=5
|
||||
# The default authentication guard
|
||||
# Supported:
|
||||
# 'web-guard' : The Laravel built-in auth system (default if nulled)
|
||||
|
39
tests/Api/v1/ThrottlingTest.php
Normal file
39
tests/Api/v1/ThrottlingTest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Api\v1\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Providers\RouteServiceProvider
|
||||
*/
|
||||
class ThrottlingTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_api_calls_are_throttled_using_config()
|
||||
{
|
||||
/**
|
||||
* @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
|
||||
*/
|
||||
$user = User::factory()->create();
|
||||
$throttle = 5;
|
||||
|
||||
Config::set('2fauth.api.throttle', $throttle);
|
||||
|
||||
$this->actingAs($user, 'api-guard');
|
||||
|
||||
for ($i=0; $i < $throttle - 1; $i++) {
|
||||
$this->json('GET', '/api/v1/twofaccounts/count');
|
||||
}
|
||||
|
||||
$this->json('GET', '/api/v1/twofaccounts/count')
|
||||
->assertOk();
|
||||
|
||||
$this->json('GET', '/api/v1/twofaccounts/count')
|
||||
->assertStatus(429);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
@ -140,19 +141,23 @@ public function test_user_login_with_invalid_credentials_returns_unauthorized()
|
||||
*/
|
||||
public function test_too_many_login_attempts_with_invalid_credentials_returns_too_many_request_error()
|
||||
{
|
||||
$throttle = 8;
|
||||
Config::set('auth.throttle.login', $throttle);
|
||||
|
||||
$post = [
|
||||
'email' => $this->user->email,
|
||||
'password' => self::WRONG_PASSWORD,
|
||||
];
|
||||
|
||||
for ($i=0; $i < $throttle - 1; $i++) {
|
||||
$this->json('POST', '/user/login', $post);
|
||||
$this->json('POST', '/user/login', $post);
|
||||
$this->json('POST', '/user/login', $post);
|
||||
$this->json('POST', '/user/login', $post);
|
||||
$this->json('POST', '/user/login', $post);
|
||||
$response = $this->json('POST', '/user/login', $post);
|
||||
}
|
||||
|
||||
$response->assertStatus(429);
|
||||
$this->json('POST', '/user/login', $post)
|
||||
->assertUnauthorized();
|
||||
|
||||
$this->json('POST', '/user/login', $post)
|
||||
->assertStatus(429);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,6 +278,9 @@ public function test_webauthn_invalid_login_returns_unauthorized()
|
||||
*/
|
||||
public function test_too_many_invalid_login_attempts_returns_too_many_request_error()
|
||||
{
|
||||
$throttle = 8;
|
||||
Config::set('auth.throttle.login', $throttle);
|
||||
|
||||
$this->user = User::factory()->create(['email' => self::EMAIL]);
|
||||
|
||||
$this->session(['_webauthn' => new \Laragear\WebAuthn\Challenge(
|
||||
@ -286,14 +289,15 @@ public function test_too_many_invalid_login_attempts_returns_too_many_request_er
|
||||
false,
|
||||
)]);
|
||||
|
||||
for ($i=0; $i < $throttle - 1; $i++) {
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
$response = $this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID);
|
||||
}
|
||||
|
||||
$response->assertStatus(429);
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID)
|
||||
->assertUnauthorized();
|
||||
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_INVALID)
|
||||
->assertStatus(429);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user