Skip to content

Commit

Permalink
Add leeway to ValidAt constraint
Browse files Browse the repository at this point in the history
To address clock skew issues.

More info:

- #191
- #248
  • Loading branch information
lcobucci committed Oct 14, 2018
1 parent 008a9e7 commit 222811d
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 5 deletions.
31 changes: 26 additions & 5 deletions src/Validation/Constraint/ValidAt.php
Expand Up @@ -3,7 +3,9 @@


namespace Lcobucci\JWT\Validation\Constraint; namespace Lcobucci\JWT\Validation\Constraint;


use DateInterval;
use DateTimeInterface; use DateTimeInterface;
use InvalidArgumentException;
use Lcobucci\Clock\Clock; use Lcobucci\Clock\Clock;
use Lcobucci\JWT\Token; use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\Constraint;
Expand All @@ -16,9 +18,28 @@ final class ValidAt implements Constraint
*/ */
private $clock; private $clock;


public function __construct(Clock $clock) /**
* @var DateInterval
*/
private $leeway;

public function __construct(Clock $clock, ?DateInterval $leeway = null)
{
$this->clock = $clock;
$this->leeway = $this->guardLeeway($leeway);
}

private function guardLeeway(?DateInterval $leeway): DateInterval
{ {
$this->clock = $clock; if ($leeway === null) {
return new DateInterval('PT0S');
}

if ($leeway->invert === 1) {
throw new InvalidArgumentException('Leeway cannot be negative');
}

return $leeway;
} }


/** /**
Expand All @@ -28,9 +49,9 @@ public function assert(Token $token): void
{ {
$now = $this->clock->now(); $now = $this->clock->now();


$this->assertIssueTime($token, $now); $this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now); $this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now); $this->assertExpiration($token, $now->sub($this->leeway));
} }


/** /**
Expand Down
51 changes: 51 additions & 0 deletions test/unit/Validation/Constraint/ValidAtTest.php
Expand Up @@ -3,6 +3,7 @@


namespace Lcobucci\JWT\Validation\Constraint; namespace Lcobucci\JWT\Validation\Constraint;


use DateInterval;
use DateTimeImmutable; use DateTimeImmutable;
use Lcobucci\Clock\Clock; use Lcobucci\Clock\Clock;
use Lcobucci\Clock\FrozenClock; use Lcobucci\Clock\FrozenClock;
Expand All @@ -23,12 +24,29 @@ public function createDependencies(): void
$this->clock = new FrozenClock(new DateTimeImmutable()); $this->clock = new FrozenClock(new DateTimeImmutable());
} }


/**
* @test
*
* @expectedException \InvalidArgumentException
*
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
*/
public function constructShouldRaiseExceptionOnNegativeLeeway(): void
{
$leeway = new DateInterval('PT30S');
$leeway->invert = 1;

new ValidAt($this->clock, $leeway);
}

/** /**
* @test * @test
* *
* @expectedException \Lcobucci\JWT\Validation\ConstraintViolation * @expectedException \Lcobucci\JWT\Validation\ConstraintViolation
* *
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
Expand Down Expand Up @@ -58,6 +76,7 @@ public function assertShouldRaiseExceptionWhenTokenIsExpired(): void
* @expectedException \Lcobucci\JWT\Validation\ConstraintViolation * @expectedException \Lcobucci\JWT\Validation\ConstraintViolation
* *
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
Expand Down Expand Up @@ -87,6 +106,7 @@ public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void
* @expectedException \Lcobucci\JWT\Validation\ConstraintViolation * @expectedException \Lcobucci\JWT\Validation\ConstraintViolation
* *
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
Expand Down Expand Up @@ -114,6 +134,36 @@ public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void
* @doesNotPerformAssertions * @doesNotPerformAssertions
* *
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertMinimumTime
*
* @uses \Lcobucci\JWT\Token\DataSet
* @uses \Lcobucci\JWT\Token\Plain
* @uses \Lcobucci\JWT\Token\Signature
*/
public function assertShouldNotRaiseExceptionWhenLeewayIsUsed(): void
{
$now = $this->clock->now();

$claims = [
RegisteredClaims::ISSUED_AT => $now->modify('+5 seconds'),
RegisteredClaims::NOT_BEFORE => $now->modify('+5 seconds'),
RegisteredClaims::EXPIRATION_TIME => $now->modify('-5 seconds'),
];

$constraint = new ValidAt($this->clock, new DateInterval('PT5S'));
$constraint->assert($this->buildToken($claims));
}

/**
* @test
* @doesNotPerformAssertions
*
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
Expand Down Expand Up @@ -154,6 +204,7 @@ public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment():
* @doesNotPerformAssertions * @doesNotPerformAssertions
* *
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::guardLeeway
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assert
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertExpiration
* @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt::assertIssueTime
Expand Down

0 comments on commit 222811d

Please sign in to comment.