Skip to content

Commit cefaeb3

Browse files
authored
Merge pull request #706 from yassinrais/4.0.x
Fix usage of non JSON numeric values for time fractions (without having precision issues)
2 parents 1dc8675 + 9a961f4 commit cefaeb3

File tree

6 files changed

+77
-13
lines changed

6 files changed

+77
-13
lines changed

src/Encoding/MicrosecondBasedDateConversion.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,13 @@ public function formatClaims(array $claims): array
2525
return $claims;
2626
}
2727

28-
/** @return int|string */
28+
/** @return int|float */
2929
private function convertDate(DateTimeImmutable $date)
3030
{
31-
$seconds = $date->format('U');
32-
$microseconds = $date->format('u');
33-
34-
if ((int) $microseconds === 0) {
35-
return (int) $seconds;
31+
if ($date->format('u') === '000000') {
32+
return (int) $date->format('U');
3633
}
3734

38-
return $seconds . '.' . $microseconds;
35+
return (float) $date->format('U.u');
3936
}
4037
}

src/Token/Parser.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212
use function count;
1313
use function explode;
1414
use function is_array;
15+
use function is_string;
16+
use function json_encode;
1517
use function strpos;
1618

19+
use const JSON_THROW_ON_ERROR;
20+
1721
final class Parser implements ParserInterface
1822
{
1923
private Decoder $decoder;
@@ -105,7 +109,9 @@ private function parseClaims(string $data): array
105109
continue;
106110
}
107111

108-
$claims[$claim] = $this->convertDate((string) $claims[$claim]);
112+
$date = $claims[$claim];
113+
114+
$claims[$claim] = $this->convertDate(is_string($date) ? $date : json_encode($date, JSON_THROW_ON_ERROR));
109115
}
110116

111117
return $claims;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\JWT\FunctionalTests;
5+
6+
use DateTimeImmutable;
7+
use Lcobucci\JWT\Configuration;
8+
use Lcobucci\JWT\Token\Plain;
9+
use PHPUnit\Framework\TestCase;
10+
11+
/**
12+
* @covers \Lcobucci\JWT\Configuration
13+
* @covers \Lcobucci\JWT\Encoding\JoseEncoder
14+
* @covers \Lcobucci\JWT\Encoding\ChainedFormatter
15+
* @covers \Lcobucci\JWT\Encoding\MicrosecondBasedDateConversion
16+
* @covers \Lcobucci\JWT\Encoding\UnifyAudience
17+
* @covers \Lcobucci\JWT\Token\Builder
18+
* @covers \Lcobucci\JWT\Token\Parser
19+
* @covers \Lcobucci\JWT\Token\Plain
20+
* @covers \Lcobucci\JWT\Token\DataSet
21+
* @covers \Lcobucci\JWT\Token\Signature
22+
* @covers \Lcobucci\JWT\Signer\Key\InMemory
23+
* @covers \Lcobucci\JWT\Signer\None
24+
* @covers \Lcobucci\JWT\Validation\Validator
25+
* @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated
26+
* @covers \Lcobucci\JWT\Validation\Constraint\SignedWith
27+
*/
28+
final class TimeFractionPrecisionTest extends TestCase
29+
{
30+
/**
31+
* @test
32+
* @dataProvider datesWithPotentialRoundingIssues
33+
*/
34+
public function timeFractionsPrecisionsAreRespected(string $timeFraction): void
35+
{
36+
$config = Configuration::forUnsecuredSigner();
37+
38+
$issuedAt = DateTimeImmutable::createFromFormat('U.u', $timeFraction);
39+
40+
self::assertInstanceOf(DateTimeImmutable::class, $issuedAt);
41+
self::assertSame($timeFraction, $issuedAt->format('U.u'));
42+
43+
$token = $config->builder()
44+
->issuedAt($issuedAt)
45+
->getToken($config->signer(), $config->signingKey());
46+
47+
$parsedToken = $config->parser()->parse($token->toString());
48+
49+
self::assertInstanceOf(Plain::class, $parsedToken);
50+
self::assertSame($timeFraction, $parsedToken->claims()->get('iat')->format('U.u'));
51+
}
52+
53+
/** @return iterable<string[]> */
54+
public function datesWithPotentialRoundingIssues(): iterable
55+
{
56+
yield ['1613938511.017448'];
57+
yield ['1613938511.023691'];
58+
yield ['1613938511.018045'];
59+
yield ['1616074725.008455'];
60+
}
61+
}

test/unit/Encoding/ChainedFormatterTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public function formatClaimsShouldApplyAllConfiguredFormatters(): void
3434
$formatted = $formatter->formatClaims($claims);
3535

3636
self::assertSame('test', $formatted[RegisteredClaims::AUDIENCE]);
37-
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
37+
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
3838
}
3939
}

test/unit/Encoding/MicrosecondBasedDateConversionTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ public function dateClaimsHaveMicrosecondsOrSeconds(): void
3636
$formatted = $formatter->formatClaims($claims);
3737

3838
self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]);
39-
self::assertSame('1487285080.000123', $formatted[RegisteredClaims::NOT_BEFORE]);
40-
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
39+
self::assertSame(1487285080.000123, $formatted[RegisteredClaims::NOT_BEFORE]);
40+
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
4141
self::assertSame('test', $formatted['testing']); // this should remain untouched
4242
}
4343

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

6464
self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]);
65-
self::assertSame('1487285080.123456', $formatted[RegisteredClaims::EXPIRATION_TIME]);
65+
self::assertSame(1487285080.123456, $formatted[RegisteredClaims::EXPIRATION_TIME]);
6666
self::assertSame('test', $formatted['testing']); // this should remain untouched
6767
}
6868
}

test/unit/Token/ParserTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ public function parseMustConvertDateClaimsToObjects(): void
453453
{
454454
$data = [
455455
RegisteredClaims::ISSUED_AT => 1486930663,
456-
RegisteredClaims::EXPIRATION_TIME => '1486930757.023055',
456+
RegisteredClaims::EXPIRATION_TIME => 1486930757.023055,
457457
];
458458

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

0 commit comments

Comments
 (0)