SymfonyCasts / reset-password-bundle

Need a killer reset password feature for your Symfony? Us too!
https://symfonycasts.com
MIT License
468 stars 65 forks source link

Token verification may fail for DateTime instances with milliseconds resolution #259

Open spetters opened 1 year ago

spetters commented 1 year ago

In our project we had issues with wrong password reset requests. We were able to track this down to a rounding issue with the expiresAt field for the ResetPasswordRequest.

Setup: Symfony 6.2.6 PHP 8.1.13 Doctrine MySQL

For the hashed token, the unix timestamp of the expiresAt is used and stored. Also expiresAt is stored as DateTimeImmutable. With Doctrine entries with milliseconds will get rounded and stored with an one second higher value in the database.

During verification based on the expiresAt value, the hashes then do not match and the user is confronted with an error.

Our solution is to strip the milliseconds while creating a ResetPasswordRequest:

...
class ResetPasswordRequest implements ResetPasswordRequestInterface {

    use ResetPasswordRequestTrait;

    ...

    public function __construct(User $user, \DateTimeInterface $expiresAt, string $selector, string $hashedToken) {
        $this->user = $user;

        // strip milliseconds
        $mutable = \DateTime::createFromInterface($expiresAt);
        $mutable->setTime(
            (int) $expiresAt->format('G'),
            (int) $expiresAt->format('i'),
            (int) $expiresAt->format('s')
        );

        $this->initialize($mutable, $selector, $hashedToken);
    }
...

This could be added to the maker script.