diff --git a/src/Actions/EnableTwoFactorAuthentication.php b/src/Actions/EnableTwoFactorAuthentication.php index cab10ad6..d8487694 100644 --- a/src/Actions/EnableTwoFactorAuthentication.php +++ b/src/Actions/EnableTwoFactorAuthentication.php @@ -31,17 +31,20 @@ public function __construct(TwoFactorAuthenticationProvider $provider) * Enable two factor authentication for the user. * * @param mixed $user + * @param bool $force * @return void */ - public function __invoke($user) + public function __invoke($user, $force = false) { - $user->forceFill([ - 'two_factor_secret' => encrypt($this->provider->generateSecretKey()), - 'two_factor_recovery_codes' => encrypt(json_encode(Collection::times(8, function () { - return RecoveryCode::generate(); - })->all())), - ])->save(); + if (empty($user->two_factor_secret) || $force === true) { + $user->forceFill([ + 'two_factor_secret' => encrypt($this->provider->generateSecretKey()), + 'two_factor_recovery_codes' => encrypt(json_encode(Collection::times(8, function () { + return RecoveryCode::generate(); + })->all())), + ])->save(); - TwoFactorAuthenticationEnabled::dispatch($user); + TwoFactorAuthenticationEnabled::dispatch($user); + } } } diff --git a/src/Http/Controllers/TwoFactorAuthenticationController.php b/src/Http/Controllers/TwoFactorAuthenticationController.php index a4754e96..3743cc1e 100644 --- a/src/Http/Controllers/TwoFactorAuthenticationController.php +++ b/src/Http/Controllers/TwoFactorAuthenticationController.php @@ -20,7 +20,7 @@ class TwoFactorAuthenticationController extends Controller */ public function store(Request $request, EnableTwoFactorAuthentication $enable) { - $enable($request->user()); + $enable($request->user(), $request->boolean('force', false)); return app(TwoFactorEnabledResponse::class); } diff --git a/tests/TwoFactorAuthenticationControllerTest.php b/tests/TwoFactorAuthenticationControllerTest.php index 32aa9d62..cb49776e 100644 --- a/tests/TwoFactorAuthenticationControllerTest.php +++ b/tests/TwoFactorAuthenticationControllerTest.php @@ -46,6 +46,88 @@ public function test_two_factor_authentication_can_be_enabled() $this->assertNotNull($user->twoFactorQrCodeSvg()); } + #[ResetRefreshDatabaseState] + public function test_calling_two_factor_authentication_endpoint_will_not_overwrite_without_force_parameter() + { + Event::fake(); + + $user = TestTwoFactorAuthenticationUser::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => bcrypt('secret'), + ]); + + $response = $this->withoutExceptionHandling()->actingAs($user)->postJson( + '/user/two-factor-authentication' + ); + + $response->assertStatus(200); + + Event::assertDispatched(TwoFactorAuthenticationEnabled::class); + + $user = $user->fresh(); + + $old_value = $user->two_factor_secret; + + $response = $this->withoutExceptionHandling()->actingAs($user)->postJson( + '/user/two-factor-authentication' + ); + + $response->assertStatus(200); + + $this->assertNotNull($user->two_factor_secret); + $this->assertNotNull($user->two_factor_recovery_codes); + $this->assertEquals($old_value, $user->fresh()->two_factor_secret); + $this->assertNull($user->two_factor_confirmed_at); + $this->assertIsArray(json_decode(decrypt($user->two_factor_recovery_codes), true)); + $this->assertNotNull($user->twoFactorQrCodeSvg()); + } + + #[ResetRefreshDatabaseState] + public function test_calling_two_factor_authentication_endpoint_will_overwrite_with_force_parameter() + { + Event::fake(); + + $user = TestTwoFactorAuthenticationUser::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => bcrypt('secret'), + ]); + + $response = $this->withoutExceptionHandling()->actingAs($user)->postJson( + '/user/two-factor-authentication', + [ + 'force' => true, + ] + ); + + $response->assertStatus(200); + + Event::assertDispatched(TwoFactorAuthenticationEnabled::class); + + $user = $user->fresh(); + + $old_value = $user->two_factor_secret; + + $response = $this->withoutExceptionHandling()->actingAs($user)->postJson( + '/user/two-factor-authentication', + [ + 'force' => true, + ] + ); + + $response->assertStatus(200); + + $user = $user->fresh(); + + $this->assertNotNull($user->two_factor_secret); + $this->assertNotNull($user->two_factor_recovery_codes); + $this->assertNotEquals($old_value, $user->fresh()->two_factor_secret); + $this->assertNull($user->two_factor_confirmed_at); + $this->assertIsArray(json_decode(decrypt($user->two_factor_recovery_codes), true)); + $this->assertNotNull($user->twoFactorQrCodeSvg()); + } + public function test_two_factor_authentication_secret_key_can_be_retrieved() { Event::fake();