Skip to content

Commit

Permalink
Merge pull request #706 from yassinrais/4.0.x
Browse files Browse the repository at this point in the history
Fix usage of non JSON numeric values for time fractions (without having precision issues)
  • Loading branch information
lcobucci authored Mar 19, 2021
2 parents 1dc8675 + 9a961f4 commit cefaeb3
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 13 deletions.
11 changes: 4 additions & 7 deletions src/Encoding/MicrosecondBasedDateConversion.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,13 @@ public function formatClaims(array $claims): array
return $claims;
}

/** @return int|string */
/** @return int|float */
private function convertDate(DateTimeImmutable $date)
{
$seconds = $date->format('U');
$microseconds = $date->format('u');

if ((int) $microseconds === 0) {
return (int) $seconds;
if ($date->format('u') === '000000') {
return (int) $date->format('U');
}

return $seconds . '.' . $microseconds;
return (float) $date->format('U.u');
}
}
8 changes: 7 additions & 1 deletion src/Token/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
use function count;
use function explode;
use function is_array;
use function is_string;
use function json_encode;
use function strpos;

use const JSON_THROW_ON_ERROR;

final class Parser implements ParserInterface
{
private Decoder $decoder;
Expand Down Expand Up @@ -105,7 +109,9 @@ private function parseClaims(string $data): array
continue;
}

$claims[$claim] = $this->convertDate((string) $claims[$claim]);
$date = $claims[$claim];

$claims[$claim] = $this->convertDate(is_string($date) ? $date : json_encode($date, JSON_THROW_ON_ERROR));
}

return $claims;
Expand Down
61 changes: 61 additions & 0 deletions test/functional/TimeFractionPrecisionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\FunctionalTests;

use DateTimeImmutable;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Token\Plain;
use PHPUnit\Framework\TestCase;

/**
* @covers \Lcobucci\JWT\Configuration
* @covers \Lcobucci\JWT\Encoding\JoseEncoder
* @covers \Lcobucci\JWT\Encoding\ChainedFormatter
* @covers \Lcobucci\JWT\Encoding\MicrosecondBasedDateConversion
* @covers \Lcobucci\JWT\Encoding\UnifyAudience
* @covers \Lcobucci\JWT\Token\Builder
* @covers \Lcobucci\JWT\Token\Parser
* @covers \Lcobucci\JWT\Token\Plain
* @covers \Lcobucci\JWT\Token\DataSet
* @covers \Lcobucci\JWT\Token\Signature
* @covers \Lcobucci\JWT\Signer\Key\InMemory
* @covers \Lcobucci\JWT\Signer\None
* @covers \Lcobucci\JWT\Validation\Validator
* @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated
* @covers \Lcobucci\JWT\Validation\Constraint\SignedWith
*/
final class TimeFractionPrecisionTest extends TestCase
{
/**
* @test
* @dataProvider datesWithPotentialRoundingIssues
*/
public function timeFractionsPrecisionsAreRespected(string $timeFraction): void
{
$config = Configuration::forUnsecuredSigner();

$issuedAt = DateTimeImmutable::createFromFormat('U.u', $timeFraction);

self::assertInstanceOf(DateTimeImmutable::class, $issuedAt);
self::assertSame($timeFraction, $issuedAt->format('U.u'));

$token = $config->builder()
->issuedAt($issuedAt)
->getToken($config->signer(), $config->signingKey());

$parsedToken = $config->parser()->parse($token->toString());

self::assertInstanceOf(Plain::class, $parsedToken);
self::assertSame($timeFraction, $parsedToken->claims()->get('iat')->format('U.u'));
}

/** @return iterable<string[]> */
public function datesWithPotentialRoundingIssues(): iterable
{
yield ['1613938511.017448'];
yield ['1613938511.023691'];
yield ['1613938511.018045'];
yield ['1616074725.008455'];
}
}
2 changes: 1 addition & 1 deletion test/unit/Encoding/ChainedFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ public function formatClaimsShouldApplyAllConfiguredFormatters(): void
$formatted = $formatter->formatClaims($claims);

self::assertSame('test', $formatted[RegisteredClaims::AUDIENCE]);
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
}
}
6 changes: 3 additions & 3 deletions test/unit/Encoding/MicrosecondBasedDateConversionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public function dateClaimsHaveMicrosecondsOrSeconds(): void
$formatted = $formatter->formatClaims($claims);

self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]);
self::assertSame('1487285080.000123', $formatted[RegisteredClaims::NOT_BEFORE]);
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
self::assertSame(1487285080.000123, $formatted[RegisteredClaims::NOT_BEFORE]);
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
self::assertSame('test', $formatted['testing']); // this should remain untouched
}

Expand All @@ -62,7 +62,7 @@ public function notAllDateClaimsNeedToBeConfigured(): void
$formatted = $formatter->formatClaims($claims);

self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]);
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
self::assertSame('test', $formatted['testing']); // this should remain untouched
}
}
2 changes: 1 addition & 1 deletion test/unit/Token/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ public function parseMustConvertDateClaimsToObjects(): void
{
$data = [
RegisteredClaims::ISSUED_AT => 1486930663,
RegisteredClaims::EXPIRATION_TIME => '1486930757.023055',
RegisteredClaims::EXPIRATION_TIME => 1486930757.023055,
];

$this->decoder->expects(self::exactly(2))
Expand Down

0 comments on commit cefaeb3

Please sign in to comment.