Skip to content

Commit

Permalink
updating leeway to just use int (and update tests), updated documenta…
Browse files Browse the repository at this point in the history
…tion, fixed styling issue
  • Loading branch information
m777z committed Jul 14, 2018
1 parent 7c9d27e commit 85e72f3
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 249 deletions.
54 changes: 29 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ Just use the builder to create a new JWT/JWS tokens:
```php
use Lcobucci\JWT\Builder;

$time = time();
$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->canOnlyBeUsedBy('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt(time()) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter(time() + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt(time() + 3600) // Configures the expiration time of the token (nbf claim)
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (nbf claim)
->with('uid', 1) // Configures a new claim, called "uid"
->getToken(); // Retrieves the generated token

Expand Down Expand Up @@ -69,7 +70,7 @@ echo $token->getClaim('uid'); // will print "1"

### Validating

We can easily validate if the token is valid (using the previous token as example):
We can easily validate if the token is valid (using the previous token and time as example):

```php
use Lcobucci\JWT\ValidationData;
Expand All @@ -81,32 +82,33 @@ $data->setId('4f1g23a12aa');

var_dump($token->validate($data)); // false, because token cannot be used before now() + 60

$data->setCurrentTime(time() + 61); // changing the validation time to future
$data->setCurrentTime($time + 61); // changing the validation time to future

var_dump($token->validate($data)); // true, because current time is between "nbf" and "exp" claims

$data->setCurrentTime(time() + 4000); // changing the validation time to future
$data->setCurrentTime($time + 4000); // changing the validation time to future

var_dump($token->validate($data)); // false, because token is expired since current time is greater than exp

// We can also use leeway to deal with clock skew.
// It uses the current time to validate, except that iat will add 5 seconds, nbf will add 10, and exp will subtract 20
$dataWithLeeway = new ValidationData(null, ['iat' => 5, 'nbf' => 14, 'exp' => -20]);
// We can also use the $leeway parameter to deal with clock skew (see notes below)
// If token's claimed time is invalid but the difference between that and the validation time is less than $leeway,
// then token is still considered valid
$dataWithLeeway = new ValidationData($time, 20);
$dataWithLeeway->setIssuer('http://example.com');
$dataWithLeeway->setAudience('http://example.org');
$dataWithLeeway->setId('4f1g23a12aa');

var_dump($token->validate($dataWithLeeway)); // false, because token can't be used before now() + 60 and it's now() + 14
var_dump($token->validate($dataWithLeeway)); // false, because token can't be used before now() + 60, not within leeway

$dataWithLeeway->setCurrentTime(time() + 51); // changing the validation time to future
$dataWithLeeway->setCurrentTime($time + 51); // changing the validation time to future

var_dump($token->validate($dataWithLeeway)); // true, because current time plus leeway is between "nbf" and "exp" claims

$dataWithLeeway->setCurrentTime(time() + 3610); // changing the validation time to future but within leeway
$dataWithLeeway->setCurrentTime($time + 3610); // changing the validation time to future but within leeway

var_dump($token->validate($dataWithLeeway)); // true, because current time + (-20 seconds leeway) is less than exp
var_dump($token->validate($dataWithLeeway)); // true, because current time - 20 seconds leeway is less than exp

$dataWithLeeway->setCurrentTime(time() + 4000); // changing the validation time to future outside of leeway
$dataWithLeeway->setCurrentTime($time + 4000); // changing the validation time to future outside of leeway

var_dump($token->validate($dataWithLeeway)); // false, because token is expired since current time is greater than exp
```
Expand All @@ -118,11 +120,11 @@ var_dump($token->validate($dataWithLeeway)); // false, because token is expired
configured in ```ValidationData``` they will be ignored by ```Token::validate()```.
- ```exp```, ```nbf``` and ```iat``` claims are configured by default in ```ValidationData::__construct()```
with the current UNIX time (```time()```).
- The values for the keys in the optional ```$leeway``` array of ```ValidationData``` will add that number of seconds
to the time that would otherwise be used for validation of the given claim. If using leeway, you will generally want to
pass a positive value for ```iat``` and ```nbf``` claims (pretending that we are further in the future) and a negative
value for ```exp``` claims (pretending that we are further in the past). Keys other than ```iat```, ```nbf```, and
```exp``` in the ```$leeway``` array will be ignored.
- The optional ```$leeway``` parameter of ```ValidationData``` will cause us to use that number of seconds of leeway
when validating the time-based claims, pretending we are further in the future for the "Issued At" (```iat```) and "Not
Before" (```nbf```) claims and pretending we are further in the past for the "Expiration Time" (```exp```) claim. This
allows for situations where the clock of the issuing server has a different time than the clock of the verifying server,
as mentioned in [section 4.1 of RFC 7519](https://tools.ietf.org/html/rfc7519#section-4.1).

## Token signature

Expand All @@ -145,13 +147,14 @@ use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;

$signer = new Sha256();
$time = time();

$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->canOnlyBeUsedBy('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt(time()) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter(time() + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt(time() + 3600) // Configures the expiration time of the token (nbf claim)
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (nbf claim)
->with('uid', 1) // Configures a new claim, called "uid"
->sign($signer, 'testing') // creates a signature using "testing" as key
->getToken(); // Retrieves the generated token
Expand All @@ -172,13 +175,14 @@ use Lcobucci\JWT\Signer\Rsa\Sha256; // you can use Lcobucci\JWT\Signer\Ecdsa\Sha

$signer = new Sha256();
$privateKey = new Key('file://{path to your private key}');
$time = time();

$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->canOnlyBeUsedBy('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt(time()) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter(time() + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt(time() + 3600) // Configures the expiration time of the token (exp claim)
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
->with('uid', 1) // Configures a new claim, called "uid"
->sign($signer, $privateKey) // creates a signature using your private key
->getToken(); // Retrieves the generated token
Expand Down
33 changes: 16 additions & 17 deletions src/ValidationData.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,22 @@ class ValidationData
*/
private $items;

private $timeFields = ['iat', 'nbf', 'exp'];

private $leeways = [];
/**
* The leeway (in seconds) to use when validating time claims
* @var int
*/
private $leeway;

/**
* Initializes the object
*
* @param int $currentTime
* @param array $leeways
* @param int $leeway
*/
public function __construct($currentTime = null, $leeways = [])
public function __construct($currentTime = null, $leeway=0)
{
$currentTime = $currentTime ?: time();
$this->leeways = array_intersect_key($leeways, array_flip($this->timeFields));
$this->leeway = (int) $leeway;

$this->items = [
'jti' => null,
Expand All @@ -55,7 +57,7 @@ public function __construct($currentTime = null, $leeways = [])
*/
public function setId($id)
{
$this->items['jti'] = (string)$id;
$this->items['jti'] = (string) $id;
}

/**
Expand All @@ -65,7 +67,7 @@ public function setId($id)
*/
public function setIssuer($issuer)
{
$this->items['iss'] = (string)$issuer;
$this->items['iss'] = (string) $issuer;
}

/**
Expand All @@ -75,7 +77,7 @@ public function setIssuer($issuer)
*/
public function setAudience($audience)
{
$this->items['aud'] = (string)$audience;
$this->items['aud'] = (string) $audience;
}

/**
Expand All @@ -85,7 +87,7 @@ public function setAudience($audience)
*/
public function setSubject($subject)
{
$this->items['sub'] = (string)$subject;
$this->items['sub'] = (string) $subject;
}

/**
Expand All @@ -95,13 +97,10 @@ public function setSubject($subject)
*/
public function setCurrentTime($currentTime)
{
foreach ($this->timeFields as $field) {
if (isset($this->leeways[$field]) && is_int($this->leeways[$field])) {
$this->items[$field] = (int)$currentTime + $this->leeways[$field];
} else {
$this->items[$field] = (int)$currentTime;
}
}
$currentTime = (int) $currentTime;
$this->items['iat'] = $currentTime + $this->leeway;
$this->items['nbf'] = $currentTime + $this->leeway;
$this->items['exp'] = $currentTime - $this->leeway;
}

/**
Expand Down
92 changes: 46 additions & 46 deletions test/functional/UnsignedTokenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ class UnsignedTokenTest extends \PHPUnit_Framework_TestCase
*/
public function builderCanGenerateAToken()
{
$user = (object)['name' => 'testing', 'email' => '[email protected]'];
$user = (object) ['name' => 'testing', 'email' => '[email protected]'];

$token = (new Builder())->setId(1)
->setAudience('http://client.abc.com')
->setIssuer('http://api.abc.com')
->setExpiration(self::CURRENT_TIME + 3000)
->set('user', $user)
->getToken();
->setAudience('http://client.abc.com')
->setIssuer('http://api.abc.com')
->setExpiration(self::CURRENT_TIME + 3000)
->set('user', $user)
->getToken();

$this->assertAttributeEquals(null, 'signature', $token);
$this->assertEquals('http://client.abc.com', $token->getClaim('aud'));
Expand All @@ -54,17 +54,17 @@ public function builderCanGenerateAToken()
*
* @depends builderCanGenerateAToken
*
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
*/
public function parserCanReadAToken(Token $generated)
{
$read = (new Parser())->parse((string)$generated);
$read = (new Parser())->parse((string) $generated);

$this->assertEquals($generated, $read);
$this->assertEquals('testing', $read->getClaim('user')->name);
Expand All @@ -75,16 +75,16 @@ public function parserCanReadAToken(Token $generated)
*
* @depends builderCanGenerateAToken
*
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
*/
public function tokenValidationShouldReturnWhenEverythingIsFine(Token $generated)
{
Expand All @@ -100,18 +100,18 @@ public function tokenValidationShouldReturnWhenEverythingIsFine(Token $generated
*
* @dataProvider invalidValidationData
*
* @depends builderCanGenerateAToken
* @depends builderCanGenerateAToken
*
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
*/
public function tokenValidationShouldReturnFalseWhenExpectedDataDontMatch(ValidationData $data, Token $generated)
{
Expand All @@ -123,20 +123,20 @@ public function tokenValidationShouldReturnFalseWhenExpectedDataDontMatch(Valida
*
* @depends builderCanGenerateAToken
*
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
* @covers Lcobucci\JWT\Builder
* @covers Lcobucci\JWT\Parser
* @covers Lcobucci\JWT\Token
* @covers Lcobucci\JWT\ValidationData
* @covers Lcobucci\JWT\Claim\Factory
* @covers Lcobucci\JWT\Claim\Basic
* @covers Lcobucci\JWT\Claim\EqualsTo
* @covers Lcobucci\JWT\Claim\GreaterOrEqualsTo
* @covers Lcobucci\JWT\Parsing\Encoder
* @covers Lcobucci\JWT\Parsing\Decoder
*/
public function tokenValidationShouldReturnTrueWhenExpectedDataMatchBecauseOfLeeway(Token $generated)
{
$notExpiredDueToLeeway = new ValidationData(self::CURRENT_TIME + 3020, ['exp' => -50]);
$notExpiredDueToLeeway = new ValidationData(self::CURRENT_TIME + 3020, 50);
$notExpiredDueToLeeway->setAudience('http://client.abc.com');
$notExpiredDueToLeeway->setIssuer('http://api.abc.com');
$this->assertTrue($generated->validate($notExpiredDueToLeeway));
Expand Down
Loading

0 comments on commit 85e72f3

Please sign in to comment.