From cbcd167515679514ceda1bdbb9cce62ad3be7f3b Mon Sep 17 00:00:00 2001 From: laserhybiz <100562257+laserhybiz@users.noreply.github.com> Date: Wed, 23 Apr 2025 11:36:08 +0300 Subject: [PATCH 1/3] merge migrations --- .../0001_01_01_000000_create_users_table.php | 3 ++ ..._01_000003_add_two_factor_auth_columns.php | 36 ------------------- 2 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 database/migrations/0001_01_01_000003_add_two_factor_auth_columns.php diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 05fb5d9e..9f5acbac 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -17,6 +17,9 @@ public function up(): void $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); + $table->text('two_factor_secret')->nullable(); + $table->text('two_factor_recovery_codes')->nullable(); + $table->timestamp('two_factor_confirmed_at')->nullable(); $table->rememberToken(); $table->timestamps(); }); diff --git a/database/migrations/0001_01_01_000003_add_two_factor_auth_columns.php b/database/migrations/0001_01_01_000003_add_two_factor_auth_columns.php deleted file mode 100644 index 94ef1822..00000000 --- a/database/migrations/0001_01_01_000003_add_two_factor_auth_columns.php +++ /dev/null @@ -1,36 +0,0 @@ -after('password', function (Blueprint $table) { - $table->text('two_factor_secret')->nullable(); - $table->text('two_factor_recovery_codes')->nullable(); - $table->timestamp('two_factor_confirmed_at')->nullable(); - }); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('users', function (Blueprint $table) { - $table->dropColumn([ - 'two_factor_secret', - 'two_factor_recovery_codes', - 'two_factor_confirmed_at', - ]); - }); - } -}; From f4fdf2687478e23219b790fe10a376132e6275c5 Mon Sep 17 00:00:00 2001 From: laserhybiz <100562257+laserhybiz@users.noreply.github.com> Date: Wed, 23 Apr 2025 11:36:08 +0300 Subject: [PATCH 2/3] use model casts --- .../Auth/TwoFactorAuthChallengeController.php | 6 ++--- .../Settings/TwoFactorAuthController.php | 10 ++++----- app/Models/User.php | 2 ++ tests/Feature/Auth/TwoFactorAuthTest.php | 22 +++++++++---------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php b/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php index a936a99b..0e0c9391 100644 --- a/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php +++ b/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php @@ -55,7 +55,7 @@ public function store(Request $request) */ protected function authenticateUsingCode(Request $request, User $user) { - $secret = decrypt($user->two_factor_secret); + $secret = $user->two_factor_secret; $valid = app(VerifyTwoFactorCode::class)($secret, $request->code); if ($valid) { @@ -77,7 +77,7 @@ protected function authenticateUsingCode(Request $request, User $user) */ protected function authenticateUsingRecoveryCode(Request $request, User $user) { - $recoveryCodes = json_decode(decrypt($user->two_factor_recovery_codes), true); + $recoveryCodes = $user->two_factor_recovery_codes; // Process the recovery code - this handles validation and removing the used code $updatedCodes = app(ProcessRecoveryCode::class)($recoveryCodes, $request->recovery_code); @@ -89,7 +89,7 @@ protected function authenticateUsingRecoveryCode(Request $request, User $user) } // Update the user's recovery codes, removing the used code - $user->two_factor_recovery_codes = encrypt(json_encode($updatedCodes)); + $user->two_factor_recovery_codes = $updatedCodes; $user->save(); // Complete the authentication process diff --git a/app/Http/Controllers/Settings/TwoFactorAuthController.php b/app/Http/Controllers/Settings/TwoFactorAuthController.php index 2995d622..fa030d2c 100644 --- a/app/Http/Controllers/Settings/TwoFactorAuthController.php +++ b/app/Http/Controllers/Settings/TwoFactorAuthController.php @@ -33,7 +33,7 @@ public function show(Request $request) return Inertia::render('settings/TwoFactor', [ 'confirmed' => $confirmed, - 'recoveryCodes' => !is_null($user->two_factor_secret) ? json_decode(decrypt($user->two_factor_recovery_codes)) : [], + 'recoveryCodes' => !is_null($user->two_factor_secret) ? $user->two_factor_recovery_codes : [], ]); } @@ -49,7 +49,7 @@ public function enable(Request $request) [$qrCode, $secret] = app(GenerateQrCodeAndSecretKey::class)($request->user()); $request->user()->forceFill([ - 'two_factor_secret' => encrypt($secret) + 'two_factor_secret' => $secret ])->save(); return response()->json([ @@ -72,7 +72,7 @@ public function confirm(Request $request) ]); // Get the secret key from the user's record - $secret = decrypt($request->user()->two_factor_secret); + $secret = $request->user()->two_factor_secret; // Verify the code $valid = app(VerifyTwoFactorCode::class)($secret, $request->code); @@ -83,7 +83,7 @@ public function confirm(Request $request) // Update user with recovery codes and confirm 2FA $request->user()->forceFill([ - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now() ])->save(); @@ -111,7 +111,7 @@ public function regenerateRecoveryCodes(Request $request) $codes = app(GenerateNewRecoveryCodes::class)($request->user()); $request->user()->forceFill([ - 'two_factor_recovery_codes' => encrypt(json_encode($codes)) + 'two_factor_recovery_codes' => $codes ])->save(); return response()->json([ diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b77..8ccb022d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -43,6 +43,8 @@ protected function casts(): array return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'two_factor_secret' => 'encrypted', + 'two_factor_recovery_codes' => 'encrypted:array', ]; } } diff --git a/tests/Feature/Auth/TwoFactorAuthTest.php b/tests/Feature/Auth/TwoFactorAuthTest.php index a1c8bbf3..59a8691b 100644 --- a/tests/Feature/Auth/TwoFactorAuthTest.php +++ b/tests/Feature/Auth/TwoFactorAuthTest.php @@ -66,8 +66,8 @@ public function test_can_manually_enable_two_factor_authentication() // Manually enable 2FA for the user $user->forceFill([ - 'two_factor_secret' => encrypt($secret), - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_secret' => $secret, + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now(), ])->save(); @@ -100,8 +100,8 @@ public function test_user_with_two_factor_enabled_is_redirected_to_challenge_pag // Manually enable 2FA for the user $user->forceFill([ - 'two_factor_secret' => encrypt($secret), - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_secret' => $secret, + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now(), ])->save(); @@ -131,8 +131,8 @@ public function test_can_authenticate_with_recovery_code() // Manually enable 2FA for the user $user->forceFill([ - 'two_factor_secret' => encrypt($secret), - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_secret' => $secret, + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now(), ])->save(); @@ -150,7 +150,7 @@ public function test_can_authenticate_with_recovery_code() // Verify the recovery code was removed $user->refresh(); - $updatedRecoveryCodes = json_decode(decrypt($user->two_factor_recovery_codes)); + $updatedRecoveryCodes = $user->two_factor_recovery_codes; $this->assertCount(count($recoveryCodes) - 1, $updatedRecoveryCodes); $this->assertNotContains($recoveryCode, $updatedRecoveryCodes); @@ -196,8 +196,8 @@ public function test_user_with_two_factor_confirmed_can_access_dashboard() // Manually enable 2FA for the user $user->forceFill([ - 'two_factor_secret' => encrypt($secret), - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_secret' => $secret, + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now(), ])->save(); @@ -227,8 +227,8 @@ public function test_can_disable_two_factor_authentication() // Manually enable 2FA for the user $user->forceFill([ - 'two_factor_secret' => encrypt($secret), - 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + 'two_factor_secret' => $secret, + 'two_factor_recovery_codes' => $recoveryCodes, 'two_factor_confirmed_at' => now(), ])->save(); From 0add866b952ecc9a42e2bf45dfdff23a4aa578f4 Mon Sep 17 00:00:00 2001 From: laserhybiz <100562257+laserhybiz@users.noreply.github.com> Date: Wed, 23 Apr 2025 11:36:08 +0300 Subject: [PATCH 3/3] run pint --- .../CompleteTwoFactorAuthentication.php | 5 +- .../DisableTwoFactorAuthentication.php | 2 +- .../GenerateNewRecoveryCodes.php | 2 +- .../GenerateQrCodeAndSecretKey.php | 20 ++++---- .../TwoFactorAuth/ProcessRecoveryCode.php | 20 ++++---- .../TwoFactorAuth/VerifyTwoFactorCode.php | 11 ++--- .../Auth/AuthenticatedSessionController.php | 2 +- .../Auth/TwoFactorAuthChallengeController.php | 36 ++++++-------- .../Settings/TwoFactorAuthController.php | 39 +++++++-------- .../EnsureTwoFactorChallengeSession.php | 3 -- bootstrap/app.php | 2 +- routes/auth.php | 2 +- tests/Feature/Auth/TwoFactorAuthTest.php | 49 +++++++++---------- 13 files changed, 87 insertions(+), 106 deletions(-) diff --git a/app/Actions/TwoFactorAuth/CompleteTwoFactorAuthentication.php b/app/Actions/TwoFactorAuth/CompleteTwoFactorAuthentication.php index 1ea1f2fe..88750189 100644 --- a/app/Actions/TwoFactorAuth/CompleteTwoFactorAuthentication.php +++ b/app/Actions/TwoFactorAuth/CompleteTwoFactorAuthentication.php @@ -10,14 +10,13 @@ class CompleteTwoFactorAuthentication /** * Complete the two-factor authentication process. * - * @param mixed $user The user to authenticate - * @return void + * @param mixed $user The user to authenticate */ public function __invoke($user): void { // Get the remember preference from the session (default to false if not set) $remember = Session::get('login.remember', false); - + // Log the user in with the remember preference Auth::login($user, $remember); diff --git a/app/Actions/TwoFactorAuth/DisableTwoFactorAuthentication.php b/app/Actions/TwoFactorAuth/DisableTwoFactorAuthentication.php index 7146ba0f..54af7db5 100644 --- a/app/Actions/TwoFactorAuth/DisableTwoFactorAuthentication.php +++ b/app/Actions/TwoFactorAuth/DisableTwoFactorAuthentication.php @@ -23,4 +23,4 @@ public function __invoke($user) ])->save(); } } -} \ No newline at end of file +} diff --git a/app/Actions/TwoFactorAuth/GenerateNewRecoveryCodes.php b/app/Actions/TwoFactorAuth/GenerateNewRecoveryCodes.php index b0e66e2a..85fb6e8f 100644 --- a/app/Actions/TwoFactorAuth/GenerateNewRecoveryCodes.php +++ b/app/Actions/TwoFactorAuth/GenerateNewRecoveryCodes.php @@ -24,4 +24,4 @@ public function generate() { return Str::random(10).'-'.Str::random(10); } -} \ No newline at end of file +} diff --git a/app/Actions/TwoFactorAuth/GenerateQrCodeAndSecretKey.php b/app/Actions/TwoFactorAuth/GenerateQrCodeAndSecretKey.php index e0077409..10462702 100644 --- a/app/Actions/TwoFactorAuth/GenerateQrCodeAndSecretKey.php +++ b/app/Actions/TwoFactorAuth/GenerateQrCodeAndSecretKey.php @@ -2,11 +2,11 @@ namespace App\Actions\TwoFactorAuth; +use App\Models\User; use BaconQrCode\Renderer\Image\SvgImageBackEnd; use BaconQrCode\Renderer\ImageRenderer; use BaconQrCode\Renderer\RendererStyle\RendererStyle; use BaconQrCode\Writer; -use App\Models\User; use PragmaRX\Google2FA\Google2FA; class GenerateQrCodeAndSecretKey @@ -21,34 +21,34 @@ class GenerateQrCodeAndSecretKey public function __invoke($user): array { // Create a new Google2FA instance with explicit configuration - $google2fa = new Google2FA(); + $google2fa = new Google2FA; $google2fa->setOneTimePasswordLength(6); - + // Generate a standard 16-character secret key $secret_key = $google2fa->generateSecretKey(16); - + // Set company name from config $this->companyName = config('app.name', 'Laravel'); - + // Generate the QR code URL $g2faUrl = $google2fa->getQRCodeUrl( $this->companyName, $user->email, $secret_key ); - + // Create the QR code image $writer = new Writer( new ImageRenderer( new RendererStyle(400), - new SvgImageBackEnd() + new SvgImageBackEnd ) ); - + // Generate the QR code as a base64 encoded SVG $qrcode_image = base64_encode($writer->writeString($g2faUrl)); - + return [$qrcode_image, $secret_key]; } -} \ No newline at end of file +} diff --git a/app/Actions/TwoFactorAuth/ProcessRecoveryCode.php b/app/Actions/TwoFactorAuth/ProcessRecoveryCode.php index c222e621..3e422c97 100644 --- a/app/Actions/TwoFactorAuth/ProcessRecoveryCode.php +++ b/app/Actions/TwoFactorAuth/ProcessRecoveryCode.php @@ -7,28 +7,28 @@ class ProcessRecoveryCode /** * Verify a recovery code and remove it from the list if valid. * - * @param array $recoveryCodes The array of recovery codes - * @param string $submittedCode The code submitted by the user + * @param array $recoveryCodes The array of recovery codes + * @param string $submittedCode The code submitted by the user * @return array|false Returns the updated array of recovery codes if valid, or false if invalid */ public function __invoke(array $recoveryCodes, string $submittedCode) { // Clean the submitted code $submittedCode = trim($submittedCode); - + // If the user has entered multiple codes, only validate the first one - $submittedCode = explode(" ", $submittedCode)[0]; - + $submittedCode = explode(' ', $submittedCode)[0]; + // Check if the code is valid - if (!in_array($submittedCode, $recoveryCodes)) { + if (! in_array($submittedCode, $recoveryCodes)) { return false; } - + // Remove the used recovery code from the list - $updatedCodes = array_values(array_filter($recoveryCodes, function($code) use ($submittedCode) { - return !hash_equals($code, $submittedCode); + $updatedCodes = array_values(array_filter($recoveryCodes, function ($code) use ($submittedCode) { + return ! hash_equals($code, $submittedCode); })); - + return $updatedCodes; } } diff --git a/app/Actions/TwoFactorAuth/VerifyTwoFactorCode.php b/app/Actions/TwoFactorAuth/VerifyTwoFactorCode.php index bdb96957..37ab146c 100644 --- a/app/Actions/TwoFactorAuth/VerifyTwoFactorCode.php +++ b/app/Actions/TwoFactorAuth/VerifyTwoFactorCode.php @@ -9,20 +9,19 @@ class VerifyTwoFactorCode /** * Verify a two-factor authentication code. * - * @param string $secret The decrypted secret key - * @param string $code The code to verify - * @return bool + * @param string $secret The decrypted secret key + * @param string $code The code to verify */ public function __invoke(string $secret, string $code): bool { // Clean the code (remove spaces and non-numeric characters) $code = preg_replace('/[^0-9]/', '', $code); - + // Create a new Google2FA instance with explicit configuration - $google2fa = new Google2FA(); + $google2fa = new Google2FA; $google2fa->setWindow(8); // Allow for some time drift $google2fa->setOneTimePasswordLength(6); // Ensure 6-digit codes - + try { return $google2fa->verify($code, $secret); } catch (\Exception $e) { diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php index bc2c5eed..cc0a2539 100644 --- a/app/Http/Controllers/Auth/AuthenticatedSessionController.php +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -38,7 +38,7 @@ public function store(LoginRequest $request): RedirectResponse // Store the user ID and remember preference in the session $request->session()->put([ 'login.id' => $user->getKey(), - 'login.remember' => $request->boolean('remember') + 'login.remember' => $request->boolean('remember'), ]); return redirect()->route('two-factor.challenge'); diff --git a/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php b/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php index 0e0c9391..bc6be7dc 100644 --- a/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php +++ b/app/Http/Controllers/Auth/TwoFactorAuthChallengeController.php @@ -9,15 +9,14 @@ use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; -use Illuminate\Validation\ValidationException; use Illuminate\Support\Str; +use Illuminate\Validation\ValidationException; class TwoFactorAuthChallengeController extends Controller { /** * Attempt to authenticate a new session using the two factor authentication code. * - * @param \Illuminate\Http\Request $request * @return mixed */ public function store(Request $request) @@ -49,64 +48,61 @@ public function store(Request $request) /** * Authenticate using a one-time password (OTP). * - * @param \Illuminate\Http\Request $request - * @param \App\Models\User $user * @return \Illuminate\Http\Response */ protected function authenticateUsingCode(Request $request, User $user) { $secret = $user->two_factor_secret; $valid = app(VerifyTwoFactorCode::class)($secret, $request->code); - + if ($valid) { app(CompleteTwoFactorAuthentication::class)($user); RateLimiter::clear($this->throttleKey($user)); + return redirect()->intended(route('dashboard', absolute: false)); } - + RateLimiter::hit($this->throttleKey($user)); + return back()->withErrors(['code' => __('The provided two factor authentication code was invalid.')]); } /** * Authenticate using a recovery code. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\User $user * @return \Illuminate\Http\Response */ protected function authenticateUsingRecoveryCode(Request $request, User $user) { $recoveryCodes = $user->two_factor_recovery_codes; - + // Process the recovery code - this handles validation and removing the used code $updatedCodes = app(ProcessRecoveryCode::class)($recoveryCodes, $request->recovery_code); - + // If ProcessRecoveryCode returns false, the code was invalid if ($updatedCodes === false) { RateLimiter::hit($this->throttleKey($user)); + return back()->withErrors(['recovery_code' => __('The provided two factor authentication recovery code was invalid.')]); } - + // Update the user's recovery codes, removing the used code $user->two_factor_recovery_codes = $updatedCodes; $user->save(); - + // Complete the authentication process app(CompleteTwoFactorAuthentication::class)($user); - + // Clear rate limiter after successful authentication RateLimiter::clear($this->throttleKey($user)); - + // Redirect to the intended page return redirect()->intended(route('dashboard', absolute: false)); } - + /** * Ensure the 2FA challenge is not rate limited. * - * @param \App\Models\User $user - * @return void * * @throws \Illuminate\Validation\ValidationException */ @@ -127,13 +123,9 @@ protected function ensureIsNotRateLimited(User $user): void /** * Get the rate limiting throttle key for the given user. - * - * @param \App\Models\User $user - * @return string */ protected function throttleKey(User $user): string { - return Str::transliterate($user->id . '|2fa|' . request()->ip()); + return Str::transliterate($user->id.'|2fa|'.request()->ip()); } } - diff --git a/app/Http/Controllers/Settings/TwoFactorAuthController.php b/app/Http/Controllers/Settings/TwoFactorAuthController.php index fa030d2c..e53ae399 100644 --- a/app/Http/Controllers/Settings/TwoFactorAuthController.php +++ b/app/Http/Controllers/Settings/TwoFactorAuthController.php @@ -17,23 +17,22 @@ class TwoFactorAuthController extends Controller * Show the two factor auth settings page * route[GET] => 'settings/two-factor' * - * @param \Illuminate\Http\Request $request * @return \Inertia\Response */ public function show(Request $request) { $user = $request->user(); - $confirmed = !is_null($user->two_factor_confirmed_at); + $confirmed = ! is_null($user->two_factor_confirmed_at); // If 2fa is not confirmed, we want to clear out secret and recovery codes // This happens when a user enables 2fa and does not finish confirmation - if (!$confirmed) { + if (! $confirmed) { app(DisableTwoFactorAuthentication::class)($user); } return Inertia::render('settings/TwoFactor', [ 'confirmed' => $confirmed, - 'recoveryCodes' => !is_null($user->two_factor_secret) ? $user->two_factor_recovery_codes : [], + 'recoveryCodes' => ! is_null($user->two_factor_secret) ? $user->two_factor_recovery_codes : [], ]); } @@ -41,20 +40,19 @@ public function show(Request $request) * Enable two factor authentication * route[POST] => 'settings/two-factor' * - * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function enable(Request $request) { [$qrCode, $secret] = app(GenerateQrCodeAndSecretKey::class)($request->user()); - + $request->user()->forceFill([ - 'two_factor_secret' => $secret + 'two_factor_secret' => $secret, ])->save(); - + return response()->json([ 'qrCode' => $qrCode, - 'secret' => $secret + 'secret' => $secret, ]); } @@ -62,7 +60,6 @@ public function enable(Request $request) * Verify and confirm the user's two-factor authentication. * route[POST] => 'settings/two-factor/confirm' * - * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function confirm(Request $request) @@ -73,29 +70,29 @@ public function confirm(Request $request) // Get the secret key from the user's record $secret = $request->user()->two_factor_secret; - + // Verify the code $valid = app(VerifyTwoFactorCode::class)($secret, $request->code); if ($valid) { // Generate recovery codes when confirming 2FA $recoveryCodes = app(GenerateNewRecoveryCodes::class)($request->user()); - + // Update user with recovery codes and confirm 2FA $request->user()->forceFill([ 'two_factor_recovery_codes' => $recoveryCodes, - 'two_factor_confirmed_at' => now() + 'two_factor_confirmed_at' => now(), ])->save(); - + return response()->json([ 'status' => 'two-factor-authentication-confirmed', - 'recovery_codes' => $recoveryCodes + 'recovery_codes' => $recoveryCodes, ]); } return response()->json([ 'status' => 'error', - 'message' => 'The provided two-factor authentication code was invalid.' + 'message' => 'The provided two-factor authentication code was invalid.', ], 422); } @@ -103,19 +100,18 @@ public function confirm(Request $request) * Generate new recovery codes for the user. * route[POST] => 'settings/two-factor/recovery-codes' * - * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse */ public function regenerateRecoveryCodes(Request $request) { $codes = app(GenerateNewRecoveryCodes::class)($request->user()); - + $request->user()->forceFill([ - 'two_factor_recovery_codes' => $codes + 'two_factor_recovery_codes' => $codes, ])->save(); - + return response()->json([ - 'recovery_codes' => $codes + 'recovery_codes' => $codes, ]); } @@ -123,7 +119,6 @@ public function regenerateRecoveryCodes(Request $request) * Disable two factor authentication for the user. * route[DELETE] => 'settings/two-factor' * - * @param \Illuminate\Http\Request $request * @return void */ public function disable(Request $request) diff --git a/app/Http/Middleware/EnsureTwoFactorChallengeSession.php b/app/Http/Middleware/EnsureTwoFactorChallengeSession.php index 22940128..6989437d 100644 --- a/app/Http/Middleware/EnsureTwoFactorChallengeSession.php +++ b/app/Http/Middleware/EnsureTwoFactorChallengeSession.php @@ -5,15 +5,12 @@ use App\Models\User; use Closure; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; class EnsureTwoFactorChallengeSession { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) diff --git a/bootstrap/app.php b/bootstrap/app.php index c3c6777e..f315c389 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,7 +1,7 @@ group(function () { diff --git a/tests/Feature/Auth/TwoFactorAuthTest.php b/tests/Feature/Auth/TwoFactorAuthTest.php index 59a8691b..3de87695 100644 --- a/tests/Feature/Auth/TwoFactorAuthTest.php +++ b/tests/Feature/Auth/TwoFactorAuthTest.php @@ -2,13 +2,12 @@ namespace Tests\Feature\Auth; +use App\Actions\TwoFactorAuth\GenerateNewRecoveryCodes; +use App\Actions\TwoFactorAuth\GenerateQrCodeAndSecretKey; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; -use Tests\TestCase; -use App\Actions\TwoFactorAuth\GenerateQrCodeAndSecretKey; -use App\Actions\TwoFactorAuth\GenerateNewRecoveryCodes; use Illuminate\Support\Facades\Session; -use Illuminate\Support\Facades\Hash; +use Tests\TestCase; class TwoFactorAuthTest extends TestCase { @@ -38,13 +37,13 @@ public function test_can_enable_two_factor_authentication() // Simulate enabling 2FA via POST $response = $this->post('/settings/two-factor'); - + // Assert JSON response with expected structure $response->assertStatus(200) - ->assertJsonStructure([ - 'qrCode', - 'secret' - ]); + ->assertJsonStructure([ + 'qrCode', + 'secret', + ]); $user->refresh(); $this->assertNotNull($user->two_factor_secret); @@ -57,11 +56,11 @@ public function test_can_manually_enable_two_factor_authentication() $user = User::factory()->create(); // Generate QR code and secret key - $generateQrAndSecret = new GenerateQrCodeAndSecretKey(); + $generateQrAndSecret = new GenerateQrCodeAndSecretKey; [$qrCode, $secret] = $generateQrAndSecret($user); // Generate recovery codes - $generateCodes = new GenerateNewRecoveryCodes(); + $generateCodes = new GenerateNewRecoveryCodes; $recoveryCodes = $generateCodes($user); // Manually enable 2FA for the user @@ -91,11 +90,11 @@ public function test_user_with_two_factor_enabled_is_redirected_to_challenge_pag $user = User::factory()->create(); // Generate QR code and secret key - $generateQrAndSecret = new GenerateQrCodeAndSecretKey(); + $generateQrAndSecret = new GenerateQrCodeAndSecretKey; [$qrCode, $secret] = $generateQrAndSecret($user); // Generate recovery codes - $generateCodes = new GenerateNewRecoveryCodes(); + $generateCodes = new GenerateNewRecoveryCodes; $recoveryCodes = $generateCodes($user); // Manually enable 2FA for the user @@ -122,11 +121,11 @@ public function test_can_authenticate_with_recovery_code() $user = User::factory()->create(); // Generate QR code and secret key - $generateQrAndSecret = new GenerateQrCodeAndSecretKey(); + $generateQrAndSecret = new GenerateQrCodeAndSecretKey; [$qrCode, $secret] = $generateQrAndSecret($user); // Generate recovery codes - $generateCodes = new GenerateNewRecoveryCodes(); + $generateCodes = new GenerateNewRecoveryCodes; $recoveryCodes = $generateCodes($user); // Manually enable 2FA for the user @@ -162,7 +161,7 @@ public function test_unauthenticated_user_is_redirected_to_login_when_accessing_ { // Clear session to ensure we're testing as a guest Session::flush(); - + $response = $this->get('/two-factor-challenge'); $response->assertRedirect('/login'); } @@ -174,7 +173,7 @@ public function test_authenticated_user_without_pending_two_factor_challenge_is_ // Authenticate the user but don't set the login.id session // which would indicate a pending 2FA challenge Session::flush(); - + $response = $this->actingAs($user) ->get('/two-factor-challenge'); @@ -183,15 +182,15 @@ public function test_authenticated_user_without_pending_two_factor_challenge_is_ } public function test_user_with_two_factor_confirmed_can_access_dashboard() - { + { $user = User::factory()->create(); // Generate QR code and secret key - $generateQrAndSecret = new GenerateQrCodeAndSecretKey(); + $generateQrAndSecret = new GenerateQrCodeAndSecretKey; [$qrCode, $secret] = $generateQrAndSecret($user); // Generate recovery codes - $generateCodes = new GenerateNewRecoveryCodes(); + $generateCodes = new GenerateNewRecoveryCodes; $recoveryCodes = $generateCodes($user); // Manually enable 2FA for the user @@ -207,7 +206,7 @@ public function test_user_with_two_factor_confirmed_can_access_dashboard() // Attempt to access dashboard $response = $this->actingAs($user) ->get('/dashboard'); - + // In the test environment, the middleware might not be active // so we just verify we can access the dashboard (status 200) $response->assertStatus(200); @@ -218,11 +217,11 @@ public function test_can_disable_two_factor_authentication() $user = User::factory()->create(); // Generate QR code and secret key - $generateQrAndSecret = new GenerateQrCodeAndSecretKey(); + $generateQrAndSecret = new GenerateQrCodeAndSecretKey; [$qrCode, $secret] = $generateQrAndSecret($user); // Generate recovery codes - $generateCodes = new GenerateNewRecoveryCodes(); + $generateCodes = new GenerateNewRecoveryCodes; $recoveryCodes = $generateCodes($user); // Manually enable 2FA for the user @@ -234,7 +233,7 @@ public function test_can_disable_two_factor_authentication() // Test disabling 2FA $this->actingAs($user); - + // Simulate disabling 2FA via DELETE $response = $this->delete('/settings/two-factor'); $response->assertStatus(200); @@ -245,4 +244,4 @@ public function test_can_disable_two_factor_authentication() $this->assertNull($user->two_factor_recovery_codes); $this->assertNull($user->two_factor_confirmed_at); } -} \ No newline at end of file +}