feat(auth): harden authentication and add configurable two-factor support
This commit is contained in:
34
app/Services/Auth/SecurityEventRecorder.php
Normal file
34
app/Services/Auth/SecurityEventRecorder.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SecurityEventRecorder
|
||||
{
|
||||
/**
|
||||
* Record a security event without credentials, tokens, or session IDs.
|
||||
*
|
||||
* @param array<string, bool|int|string|null> $context
|
||||
*/
|
||||
public function record(
|
||||
string $event,
|
||||
?User $user,
|
||||
Request $request,
|
||||
array $context = []
|
||||
): void {
|
||||
Log::channel(config('auth-ui.security.log_channel'))->notice(
|
||||
'Authentication security event',
|
||||
[
|
||||
'event' => $event,
|
||||
'user_id' => $user?->getKey(),
|
||||
'ip_address' => $request->ip(),
|
||||
'user_agent' => Str::limit((string) $request->userAgent(), 255, ''),
|
||||
...$context,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,16 @@ use Illuminate\Http\Request;
|
||||
|
||||
class TwoFactorChallenge
|
||||
{
|
||||
/**
|
||||
* Determine whether the user must enroll in two-factor authentication.
|
||||
*/
|
||||
public function enrollmentRequiredFor(User $user): bool
|
||||
{
|
||||
return (bool) config('auth-ui.features.two_factor')
|
||||
&& (bool) config('auth-ui.features.two_factor_required')
|
||||
&& ! $user->hasEnabledTwoFactorAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user must complete a two-factor challenge.
|
||||
*/
|
||||
@@ -25,6 +35,7 @@ class TwoFactorChallenge
|
||||
$request->session()->put([
|
||||
'login.id' => $user->getKey(),
|
||||
'login.remember' => $remember,
|
||||
'login.started_at' => now()->timestamp,
|
||||
]);
|
||||
$request->session()->regenerate();
|
||||
|
||||
|
||||
43
app/Services/Auth/TwoFactorEnrollmentState.php
Normal file
43
app/Services/Auth/TwoFactorEnrollmentState.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use Laravel\Fortify\Fortify;
|
||||
|
||||
class TwoFactorEnrollmentState
|
||||
{
|
||||
/**
|
||||
* Build the two-factor state shared by setup and profile screens.
|
||||
*
|
||||
* @return array{
|
||||
* available: bool,
|
||||
* enabled: bool,
|
||||
* pending: bool,
|
||||
* requiresPassword: bool,
|
||||
* qrCodeDataUri: string|null,
|
||||
* secretKey: string|null
|
||||
* }
|
||||
*/
|
||||
public function for(User $user): array
|
||||
{
|
||||
$available = (bool) config('auth-ui.features.two_factor');
|
||||
$enabled = $available && $user->hasEnabledTwoFactorAuthentication();
|
||||
$pending = $available
|
||||
&& filled($user->two_factor_secret)
|
||||
&& ! $enabled;
|
||||
|
||||
return [
|
||||
'available' => $available,
|
||||
'enabled' => $enabled,
|
||||
'pending' => $pending,
|
||||
'requiresPassword' => $user->hasPassword(),
|
||||
'qrCodeDataUri' => $pending
|
||||
? 'data:image/svg+xml;base64,'.base64_encode($user->twoFactorQrCodeSvg())
|
||||
: null,
|
||||
'secretKey' => $pending
|
||||
? Fortify::currentEncrypter()->decrypt($user->two_factor_secret)
|
||||
: null,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user