aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/spomky-labs/otphp/src/TOTP.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/spomky-labs/otphp/src/TOTP.php')
-rw-r--r--vendor/spomky-labs/otphp/src/TOTP.php58
1 files changed, 47 insertions, 11 deletions
diff --git a/vendor/spomky-labs/otphp/src/TOTP.php b/vendor/spomky-labs/otphp/src/TOTP.php
index 8a1cfeba1..035e04f95 100644
--- a/vendor/spomky-labs/otphp/src/TOTP.php
+++ b/vendor/spomky-labs/otphp/src/TOTP.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace OTPHP;
use InvalidArgumentException;
+use Psr\Clock\ClockInterface;
use function assert;
use function is_int;
@@ -13,16 +14,34 @@ use function is_int;
*/
final class TOTP extends OTP implements TOTPInterface
{
+ private readonly ClockInterface $clock;
+
+ public function __construct(string $secret, ?ClockInterface $clock = null)
+ {
+ parent::__construct($secret);
+ if ($clock === null) {
+ trigger_deprecation(
+ 'spomky-labs/otphp',
+ '11.3.0',
+ 'The parameter "$clock" will become mandatory in 12.0.0. Please set a valid PSR Clock implementation instead of "null".'
+ );
+ $clock = new InternalClock();
+ }
+
+ $this->clock = $clock;
+ }
+
public static function create(
null|string $secret = null,
int $period = self::DEFAULT_PERIOD,
string $digest = self::DEFAULT_DIGEST,
int $digits = self::DEFAULT_DIGITS,
- int $epoch = self::DEFAULT_EPOCH
+ int $epoch = self::DEFAULT_EPOCH,
+ ?ClockInterface $clock = null
): self {
$totp = $secret !== null
- ? self::createFromSecret($secret)
- : self::generate()
+ ? self::createFromSecret($secret, $clock)
+ : self::generate($clock)
;
$totp->setPeriod($period);
$totp->setDigest($digest);
@@ -32,9 +51,9 @@ final class TOTP extends OTP implements TOTPInterface
return $totp;
}
- public static function createFromSecret(string $secret): self
+ public static function createFromSecret(string $secret, ?ClockInterface $clock = null): self
{
- $totp = new self($secret);
+ $totp = new self($secret, $clock);
$totp->setPeriod(self::DEFAULT_PERIOD);
$totp->setDigest(self::DEFAULT_DIGEST);
$totp->setDigits(self::DEFAULT_DIGITS);
@@ -43,9 +62,9 @@ final class TOTP extends OTP implements TOTPInterface
return $totp;
}
- public static function generate(): self
+ public static function generate(?ClockInterface $clock = null): self
{
- return self::createFromSecret(self::generateSecret());
+ return self::createFromSecret(self::generateSecret(), $clock);
}
public function getPeriod(): int
@@ -68,9 +87,14 @@ final class TOTP extends OTP implements TOTPInterface
{
$period = $this->getPeriod();
- return $period - (time() % $this->getPeriod());
+ return $period - ($this->clock->now()->getTimestamp() % $this->getPeriod());
}
+ /**
+ * The OTP at the specified input.
+ *
+ * @param 0|positive-int $input
+ */
public function at(int $input): string
{
return $this->generateOTP($this->timecode($input));
@@ -78,16 +102,24 @@ final class TOTP extends OTP implements TOTPInterface
public function now(): string
{
- return $this->at(time());
+ $timestamp = $this->clock->now()
+ ->getTimestamp();
+ assert($timestamp >= 0, 'The timestamp must return a positive integer.');
+
+ return $this->at($timestamp);
}
/**
* If no timestamp is provided, the OTP is verified at the actual timestamp. When used, the leeway parameter will
* allow time drift. The passed value is in seconds.
+ *
+ * @param 0|positive-int $timestamp
+ * @param null|0|positive-int $leeway
*/
public function verify(string $otp, null|int $timestamp = null, null|int $leeway = null): bool
{
- $timestamp ??= time();
+ $timestamp ??= $this->clock->now()
+ ->getTimestamp();
$timestamp >= 0 || throw new InvalidArgumentException('Timestamp must be at least 0.');
if ($leeway === null) {
@@ -98,8 +130,12 @@ final class TOTP extends OTP implements TOTPInterface
$leeway < $this->getPeriod() || throw new InvalidArgumentException(
'The leeway must be lower than the TOTP period'
);
+ $timestampMinusLeeway = $timestamp - $leeway;
+ $timestampMinusLeeway >= 0 || throw new InvalidArgumentException(
+ 'The timestamp must be greater than or equal to the leeway.'
+ );
- return $this->compareOTP($this->at($timestamp - $leeway), $otp)
+ return $this->compareOTP($this->at($timestampMinusLeeway), $otp)
|| $this->compareOTP($this->at($timestamp), $otp)
|| $this->compareOTP($this->at($timestamp + $leeway), $otp);
}