diff --git a/src/JWT.php b/src/JWT.php index 0d2e47c9..706aef58 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -224,6 +224,15 @@ public static function encode( if ($keyId !== null) { $header['kid'] = $keyId; } + if (isset($payload['nbf']) && !\is_numeric($payload['nbf'])) { + throw new UnexpectedValueException('Payload nbf must be a number'); + } + if (isset($payload['iat']) && !\is_numeric($payload['iat'])) { + throw new UnexpectedValueException('Payload iat must be a number'); + } + if (isset($payload['exp']) && !\is_numeric($payload['exp'])) { + throw new UnexpectedValueException('Payload exp must be a number'); + } $segments = []; $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header)); $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload)); diff --git a/tests/JWTTest.php b/tests/JWTTest.php index 6832655f..60b7ae48 100644 --- a/tests/JWTTest.php +++ b/tests/JWTTest.php @@ -609,6 +609,45 @@ public function testDecodeExpectsIntegerExp() JWT::decode($payload, $this->hmacKey); } + public function testEncodeThrowsWhenIatIsNotNumeric(): void + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Payload iat must be a number'); + + JWT::encode(['iat' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256'); + } + + public function testEncodeThrowsWhenNbfIsNotNumeric(): void + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Payload nbf must be a number'); + + JWT::encode(['nbf' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256'); + } + + public function testEncodeThrowsWhenExpIsNotNumeric(): void + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Payload exp must be a number'); + + JWT::encode(['exp' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256'); + } + + public function testEncodeAcceptsNumericStringTimestamps(): void + { + $payload = [ + 'message' => 'abc', + 'iat' => (string) time(), + 'exp' => (string) (time() + 20), + 'nbf' => (string) (time() - 20), + ]; + + $encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256'); + $decoded = JWT::decode($encoded, $this->hmacKey); + + $this->assertSame('abc', $decoded->message); + } + public function testRsaKeyLengthValidationThrowsException(): void { $this->expectException(DomainException::class);