Skip to content

RecoveryCodeReplaced dispatched twice when two-factor.login receive a valid recovery code #592

Open
@joke2k

Description

@joke2k

Fortify Version

1.25.3

Laravel Version

10.48.27

PHP Version

8.1

Database Driver & Version

No response

Description

The TwoFactorAuthenticatedSessionController dispatches a RecoveryCodeReplaced event

public function store(TwoFactorLoginRequest $request)
{
$user = $request->challengedUser();
if ($code = $request->validRecoveryCode()) {
$user->replaceRecoveryCode($code);
event(new RecoveryCodeReplaced($user, $code));

but User::replaceRecoveryCode already dispatches the exact same event

public function replaceRecoveryCode($code)
{
$this->forceFill([
'two_factor_recovery_codes' => encrypt(str_replace(
$code,
RecoveryCode::generate(),
decrypt($this->two_factor_recovery_codes)
)),
])->save();
RecoveryCodeReplaced::dispatch($this, $code);

I’m not sure where it would make the most sense to keep the dispatch, but I think it’s reasonable to avoid triggering it twice.

Steps To Reproduce

public function test_two_factor_challenge_with_recovery_code_dispatches_RecoveryCodeReplaced_twice()
    {
        Event::fake();

        $user = UserWithTwoFactor::forceCreate([
            'name' => 'Taylor Otwell',
            'email' => '[email protected]',
            'password' => bcrypt('secret'),
            'two_factor_recovery_codes' => encrypt(json_encode(['invalid-code', 'valid-code'])),
        ]);

        $response = $this->withSession([
            'login.id' => $user->id,
            'login.remember' => false,
        ])->withoutExceptionHandling()->post('/two-factor-challenge', [
            'recovery_code' => 'valid-code',
        ]);

        Event::assertDispatchedTimes(RecoveryCodeReplaced::class, 2);
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions