diff --git a/src/JWT.php b/src/JWT.php index 37a9e0e6..e8fc901f 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -152,30 +152,46 @@ public static function decode( // Check the nbf if it is defined. This is the time that the // token can actually be used. If it's not yet that time, abort. - if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) { - $ex = new BeforeValidException( - 'Cannot handle token with nbf prior to ' . \date(DateTime::ATOM, (int) floor($payload->nbf)) - ); - $ex->setPayload($payload); - throw $ex; + if (isset($payload->nbf)) { + if (!\is_int($payload->nbf)) { + throw new UnexpectedValueException('The property nbf must be of type integer.'); + } + if (floor($payload->nbf) > ($timestamp + static::$leeway)) { + $ex = new BeforeValidException( + 'Cannot handle token with nbf prior to ' . \date(DateTime::ATOM, (int) floor($payload->nbf)) + ); + $ex->setPayload($payload); + throw $ex; + } } // Check that this token has been created before 'now'. This prevents // using tokens that have been created for later use (and haven't // correctly used the nbf claim). - if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) { - $ex = new BeforeValidException( - 'Cannot handle token with iat prior to ' . \date(DateTime::ATOM, (int) floor($payload->iat)) - ); - $ex->setPayload($payload); - throw $ex; + if (isset($payload->iat)) { + if (!\is_int($payload->iat)) { + throw new UnexpectedValueException('The property iat must be of type integer.'); + } + if (floor($payload->iat) > ($timestamp + static::$leeway)) { + $ex = new BeforeValidException( + 'Cannot handle token with iat prior to ' . \date(DateTime::ATOM, (int) floor($payload->iat)) + ); + $ex->setPayload($payload); + throw $ex; + } } // Check if this token has expired. - if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { - $ex = new ExpiredException('Expired token'); - $ex->setPayload($payload); - throw $ex; + if (isset($payload->exp)) { + if (!\is_int($payload->exp)) { + throw new UnexpectedValueException('The property exp must be of type integer.'); + } + + if (($timestamp - static::$leeway) >= $payload->exp) { + $ex = new ExpiredException('Expired token'); + $ex->setPayload($payload); + throw $ex; + } } return $payload; @@ -211,6 +227,15 @@ public static function encode( if ($keyId !== null) { $header['kid'] = $keyId; } + if (isset($payload['nbf']) && !\is_int($payload['nbf'])) { + throw new UnexpectedValueException('The property nbf must be an integer containing a unix timestamp.'); + } + if (isset($payload['iat']) && !\is_int($payload['iat'])) { + throw new UnexpectedValueException('The property nbf must be an integer containing a unix timestamp.'); + } + if (isset($payload['exp']) && !\is_int($payload['exp'])) { + throw new UnexpectedValueException('The property exp must be an integer containing a unix timestamp.'); + } $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 d09d43e3..4ca53f6a 100644 --- a/tests/JWTTest.php +++ b/tests/JWTTest.php @@ -546,4 +546,58 @@ public function testAdditionalHeaderOverrides() $this->assertEquals('my_key_id', $headers->kid, 'key param not overridden'); $this->assertEquals('HS256', $headers->alg, 'alg param not overridden'); } + + public function testEncodeExpectsIntegerIat() + { + $this->expectException(UnexpectedValueException::class); + $payload = [ + 'iat' => 'abc', + ]; + JWT::encode($payload, 'my_key', 'HS256'); + } + + public function testDecodeExpectsIntegerIat() + { + $this->expectException(UnexpectedValueException::class); + JWT::decode( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzM3IiwibmFtZSI6IkpvaG4gU25vdyIsImlhdCI6InRlc3QifQ.B8cbURVQAPay3-Ep0DAm1Ji2rhij-hxfNA5PIDarf5o', + new Key('secret', 'HS256') + ); + } + + public function testEncodeExpectsIntegerNbf() + { + $this->expectException(UnexpectedValueException::class); + $payload = [ + 'nbf' => 'abc', + ]; + JWT::encode($payload, 'my_key', 'HS256'); + } + + public function testDecodeExpectsIntegerNbf() + { + $this->expectException(UnexpectedValueException::class); + JWT::decode( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzM3IiwibmFtZSI6IkpvaG4gU25vdyIsIm5iZiI6InRlc3QifQ.9KdFz3ktQoPO5QFG3E7J86PEuw5Vmx0VPrUKszP7DDs', + new Key('secret', 'HS256') + ); + } + + public function testEncodeExpectsIntegerExp() + { + $this->expectException(UnexpectedValueException::class); + $payload = [ + 'exp' => 'abc', + ]; + JWT::encode($payload, 'my_key', 'HS256'); + } + + public function testDecodeExpectsIntegerExp() + { + $this->expectException(UnexpectedValueException::class); + JWT::decode( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzM3IiwibmFtZSI6IkpvaG4gU25vdyIsImV4cCI6InRlc3QifQ.LXevvGvchI3PTBZo9jZ5-4d0OvONVU-_8Tbg_22-PTo', + new Key('secret', 'HS256') + ); + } }