diff --git a/src/Model/Traits/HasOtp.php b/src/Model/Traits/HasOtp.php index 8867c19..8113d64 100644 --- a/src/Model/Traits/HasOtp.php +++ b/src/Model/Traits/HasOtp.php @@ -99,6 +99,6 @@ public function verifyOtp(string $received): bool } $otp = OTPHP\Factory::loadFromProvisioningUri($this->otpUri); - return $otp->verify($received); + return $otp->verify($received, null, 29); } } diff --git a/tests/Model/Traits/HasOtpTest.php b/tests/Model/Traits/HasOtpTest.php index f892a68..a36d1d3 100644 --- a/tests/Model/Traits/HasOtpTest.php +++ b/tests/Model/Traits/HasOtpTest.php @@ -89,6 +89,12 @@ public function testVerifySecret(): void $otp = Factory::loadFromProvisioningUri($uri); self::assertInstanceOf(TOTPInterface::class, $otp); - self::assertTrue($this->user->verifyOtp($otp->now()), 'Correct OTP given'); + + // This is very time sensitive, and test might be flaky if the generated OTP is on the last + // millisecond of a second, and the verification happens on the first millisecond of the next second. + // To limit flakiness, we test with a slightly shorter time period than what is actually allowed. + self::assertTrue($this->user->verifyOtp($otp->at(time())), 'Correct OTP given'); + self::assertTrue($this->user->verifyOtp($otp->at(time() - 27)), 'Even accept correct past OTP, in case of mobile device clock sync failure'); + self::assertTrue($this->user->verifyOtp($otp->at(time() + 27)), 'Even accept correct future OTP, in case of mobile device clock sync failure'); } }