From 953e831d841872c14779100769833da032fdd500 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Sun, 9 Apr 2023 02:40:21 +0200 Subject: [PATCH] Fixed capitalization, tests --- README.md | 30 ++++----- composer.json | 12 +++- phpstan.neon | 12 ++++ phpunit.xml | 31 ++++----- src/Collection/PropertyBag.php | 2 +- src/Collection/PropertyException.php | 2 +- src/{JwtToken.php => JWTToken.php} | 20 +++--- src/{JwtUtil.php => JWTUtil.php} | 4 +- .../{JwtDerivedKey.php => JWTDerivedKey.php} | 4 +- ...wtPlaintextKey.php => JWTPlaintextKey.php} | 4 +- src/Key/KeyInterface.php | 2 +- src/Validator/JWTClaimException.php | 9 +++ src/Validator/JWTHeaderException.php | 8 +++ src/Validator/JWTTokenException.php | 8 +++ .../{JwtValidator.php => JWTValidator.php} | 22 +++---- src/Validator/JWTValidatorException.php | 8 +++ src/Validator/JwtClaimException.php | 9 --- src/Validator/JwtHeaderException.php | 8 --- src/Validator/JwtTokenException.php | 8 --- src/Validator/JwtValidatorException.php | 8 --- tests/JwtTokenTest.php | 24 ++++--- tests/JwtUtilTest.php | 13 ++-- tests/Key/JwtDerivedKeyTest.php | 34 ++++++---- tests/Key/JwtPlaintextKeyTest.php | 13 ++-- tests/Validator/JwtValidatorTest.php | 65 +++++++++++-------- 25 files changed, 199 insertions(+), 161 deletions(-) create mode 100644 phpstan.neon rename src/{JwtToken.php => JWTToken.php} (86%) rename src/{JwtUtil.php => JWTUtil.php} (85%) rename src/Key/{JwtDerivedKey.php => JWTDerivedKey.php} (78%) rename src/Key/{JwtPlaintextKey.php => JWTPlaintextKey.php} (71%) create mode 100644 src/Validator/JWTClaimException.php create mode 100644 src/Validator/JWTHeaderException.php create mode 100644 src/Validator/JWTTokenException.php rename src/Validator/{JwtValidator.php => JWTValidator.php} (73%) create mode 100644 src/Validator/JWTValidatorException.php delete mode 100644 src/Validator/JwtClaimException.php delete mode 100644 src/Validator/JwtHeaderException.php delete mode 100644 src/Validator/JwtTokenException.php delete mode 100644 src/Validator/JwtValidatorException.php diff --git a/README.md b/README.md index 34df3d2..962daa7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SimpleJwt +# SimpleJWT This is a simple library for generating (signing) and verifying JWT tokens. It is by no means an advanced library. If you just need to sign and refresh tokens @@ -27,23 +27,23 @@ Install using composer: ## Usage -You need a key for both generating and parsing tokens. Create a `JwtDerivedKey` -or a `JwtPlaintextKey` and pass it to the `JwtToken` constructor: +You need a key for both generating and parsing tokens. Create a `JWTDerivedKey` +or a `JWTPlaintextKey` and pass it to the `JWTToken` constructor: - use NoccyLabs\SimpleJwt\Key\{JwtDerivedKey,JwtPlaintextKey} + use NoccyLabs\SimpleJWT\Key\{JWTDerivedKey,JWTPlaintextKey} // Derive a key using secret and salt... - $key = new JwtDerivedKey("secret", "salt"); + $key = new JWTDerivedKey("secret", "salt"); // ...or use a prepared plaintext key - $key = new JwtPlaintextKey("This Should Be Binary Data.."); + $key = new JWTPlaintextKey("This Should Be Binary Data.."); ### Generating tokens - use NoccyLabs\SimpleJwt\JwtToken; + use NoccyLabs\SimpleJWT\JWTToken; - $tok = new JwtToken($key); + $tok = new JWTToken($key); $tok->setExpiry("1h"); $tok->claims->add("some/claim/MaxItems", 8); @@ -54,11 +54,11 @@ or a `JwtPlaintextKey` and pass it to the `JwtToken` constructor: Parsing is done by passing the raw token as the 2nd parameter - use NoccyLabs\SimpleJwt\JwtToken; + use NoccyLabs\SimpleJWT\JWTToken; $str = "...received token..."; - $tok = new JwtToken($key, $str); + $tok = new JWTToken($key, $str); if (!$tok->isValid()) { // This check works, but using the validator might be better @@ -75,9 +75,9 @@ Parsing is done by passing the raw token as the 2nd parameter ### Validating tokens - use NoccyLabs\SimpleJwt\Validator\JwtValidator; + use NoccyLabs\SimpleJWT\Validator\JWTValidator; - $validator = new JwtValidator(); + $validator = new JWTValidator(); // Require that some claim exists $validator ->requireIssuer("api.issuer.tld") @@ -85,11 +85,11 @@ Parsing is done by passing the raw token as the 2nd parameter ->addRequiredClaim("some/required/Claim"); try { - // Pass a JwtToken to validateToken()... + // Pass a JWTToken to validateToken()... $valid = $validator->validateToken($tok); - // ...or pass a JwtKeyInterface and the raw string to validate() + // ...or pass a JWTKeyInterface and the raw string to validate() $valid = $validator->validate($key, $tokenstr); } - catch (JwtValidatorException $e) { + catch (JWTValidatorException $e) { // validation failed } diff --git a/composer.json b/composer.json index a0b6a75..65af6be 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,15 @@ ], "autoload": { "psr-4": { - "NoccyLabs\\SimpleJwt\\": "src/" + "NoccyLabs\\SimpleJWT\\": "src/" } + }, + "require": { + "php": "^7.4|^8.0", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "phpstan/phpstan": "^1.10" } -} \ No newline at end of file +} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..e68bade --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,12 @@ +parameters: + level: 5 + + excludePaths: + - doc + - vendor + - tests + + # Paths to include in the analysis + paths: + - src + diff --git a/phpunit.xml b/phpunit.xml index 206ad44..bc38789 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,22 +1,13 @@ - - - - tests - - - - - - src - - + + + + src + + + + + tests + + diff --git a/src/Collection/PropertyBag.php b/src/Collection/PropertyBag.php index a6116b0..5f9a854 100644 --- a/src/Collection/PropertyBag.php +++ b/src/Collection/PropertyBag.php @@ -1,6 +1,6 @@ generated = false; [ $header, $payload, $signature ] = explode(".", trim($token), 3); - $hash = JwtUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); + $hash = JWTUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); if ($signature == $hash) { $this->valid = true; } - $this->header = new PropertyBag(json_decode(JwtUtil::decode($header), true)); - $this->claims = new PropertyBag(json_decode(JwtUtil::decode($payload), true)); + $this->header = new PropertyBag(json_decode(JWTUtil::decode($header), true)); + $this->claims = new PropertyBag(json_decode(JWTUtil::decode($payload), true)); if ($this->header->has('exp')) { $exp = intval($this->header->get('exp')); @@ -136,9 +136,9 @@ class JwtToken public function getSignedToken(): string { - $header = JwtUtil::encode($this->header->getJson()); - $payload = JwtUtil::encode($this->claims->getJson()); - $hash = JwtUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); + $header = JWTUtil::encode($this->header->getJson()); + $payload = JWTUtil::encode($this->claims->getJson()); + $hash = JWTUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); return $header.".".$payload.".".$hash; } diff --git a/src/JwtUtil.php b/src/JWTUtil.php similarity index 85% rename from src/JwtUtil.php rename to src/JWTUtil.php index d51faf6..c5130a8 100644 --- a/src/JwtUtil.php +++ b/src/JWTUtil.php @@ -1,9 +1,9 @@ requireAudience = (array)$audience; } - public function validateToken(JwtToken $token) + public function validateToken(JWTToken $token) { if (!$token->isValid()) { - throw new JwtTokenException("The token is not valid"); + throw new JWTTokenException("The token is not valid"); } if (!$token->header->hasAll($this->requireHeaders)) { - throw new JwtHeaderException("The token is missing one or more required headers"); + throw new JWTHeaderException("The token is missing one or more required headers"); } if (!$token->claims->hasAll($this->requireClaims)) { - throw new JwtHeaderException("The token is missing one or more required claims"); + throw new JWTHeaderException("The token is missing one or more required claims"); } if ($this->requireIssuer) { $hasIssuer = $token->header->has("iss"); if ((!$hasIssuer) || (!in_array($token->header->get("iss"), $this->requireIssuer))) - throw new JwtTokenException("Invalid issuer"); + throw new JWTTokenException("Invalid issuer"); } if ($this->requireAudience) { $hasAudience = $token->header->has("aud"); if ((!$hasAudience) || (!in_array($token->header->get("aud"), $this->requireAudience))) - throw new JwtTokenException("Invalid audience"); + throw new JWTTokenException("Invalid audience"); } return true; @@ -74,7 +74,7 @@ class JwtValidator public function validate(KeyInterface $key, string $raw) { - $token = new JwtToken($key, $raw); + $token = new JWTToken($key, $raw); if ($this->validateToken($token)) { return $token; } diff --git a/src/Validator/JWTValidatorException.php b/src/Validator/JWTValidatorException.php new file mode 100644 index 0000000..d4b2056 --- /dev/null +++ b/src/Validator/JWTValidatorException.php @@ -0,0 +1,8 @@ +addClaim("foo", true); $token = $tok->getSignedToken(); @@ -20,19 +23,22 @@ class JwtTokenTest extends \PhpUnit\Framework\TestCase $this->assertTrue($tok->isGenerated()); } + /** + * @covers + */ public function testParsingTokens() { - $key = new JwtPlaintextKey("test"); + $key = new JWTPlaintextKey("test"); - $tok = new JwtToken($key); + $tok = new JWTToken($key); $tok->addClaim("foo", true); $token = $tok->getSignedToken(); - $parsed = new JwtToken($key, $token); + $parsed = new JWTToken($key, $token); $this->assertTrue($parsed->isValid()); $this->assertFalse($parsed->isGenerated()); } -} \ No newline at end of file +} diff --git a/tests/JwtUtilTest.php b/tests/JwtUtilTest.php index ce5c554..aa44618 100644 --- a/tests/JwtUtilTest.php +++ b/tests/JwtUtilTest.php @@ -1,18 +1,21 @@ assertEquals($v1a, $v1c); $this->assertNotEquals($v1a, $v1b); } -} \ No newline at end of file +} diff --git a/tests/Key/JwtDerivedKeyTest.php b/tests/Key/JwtDerivedKeyTest.php index e0c99ad..d69b0d4 100644 --- a/tests/Key/JwtDerivedKeyTest.php +++ b/tests/Key/JwtDerivedKeyTest.php @@ -1,37 +1,43 @@ assertNotNull($key1a); $this->assertEquals($key1a->getBinaryKey(), $key1b->getBinaryKey()); - $key2a = new JwtDerivedKey("bar", "foosalt"); - $key2b = new JwtDerivedKey("bar", "barsalt"); - $key2c = new JwtDerivedKey("bar", "barsalt"); + $key2a = new JWTDerivedKey("bar", "foosalt"); + $key2b = new JWTDerivedKey("bar", "barsalt"); + $key2c = new JWTDerivedKey("bar", "barsalt"); $this->assertNotNull($key2a); $this->assertNotEquals($key2a->getBinaryKey(), $key2b->getBinaryKey()); $this->assertEquals($key2b->getBinaryKey(), $key2c->getBinaryKey()); } + /** + * @covers + */ public function testTheDerivedKeysShouldBeUnique() { $keys = []; - $keys[] = (new JwtDerivedKey("foo", "foosalt"))->getBinaryKey(); - $keys[] = (new JwtDerivedKey("foo", "barsalt"))->getBinaryKey(); - $keys[] = (new JwtDerivedKey("foo", "bazsalt"))->getBinaryKey(); - $keys[] = (new JwtDerivedKey("bar", "foosalt"))->getBinaryKey(); - $keys[] = (new JwtDerivedKey("bar", "barsalt"))->getBinaryKey(); - $keys[] = (new JwtDerivedKey("bar", "bazsalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("foo", "foosalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("foo", "barsalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("foo", "bazsalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("bar", "foosalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("bar", "barsalt"))->getBinaryKey(); + $keys[] = (new JWTDerivedKey("bar", "bazsalt"))->getBinaryKey(); $unique = array_unique($keys); $this->assertEquals(count($keys), count($unique)); } -} \ No newline at end of file +} diff --git a/tests/Key/JwtPlaintextKeyTest.php b/tests/Key/JwtPlaintextKeyTest.php index 0157c48..7c44d29 100644 --- a/tests/Key/JwtPlaintextKeyTest.php +++ b/tests/Key/JwtPlaintextKeyTest.php @@ -1,16 +1,19 @@ assertEquals("foo", $key->getBinaryKey()); - $key = new JwtPlaintextKey("bar"); + $key = new JWTPlaintextKey("bar"); $this->assertEquals("bar", $key->getBinaryKey()); } -} \ No newline at end of file +} diff --git a/tests/Validator/JwtValidatorTest.php b/tests/Validator/JwtValidatorTest.php index 9def9c7..ca0d002 100644 --- a/tests/Validator/JwtValidatorTest.php +++ b/tests/Validator/JwtValidatorTest.php @@ -1,49 +1,56 @@ validateToken($token); $this->assertEquals(true, $valid); } + /** + * @covers + */ public function testExpiredTokensShouldFailWithException() { - $key = new JwtPlaintextKey("key"); - $token = new JwtToken($key); + $key = new JWTPlaintextKey("key"); + $token = new JWTToken($key); $token->header->set("exp", 0); - $token = new JwtToken($key, $token->getSignedToken()); + $token = new JWTToken($key, $token->getSignedToken()); - $validator = new JwtValidator(); - $this->expectException(JwtTokenException::class); + $validator = new JWTValidator(); + $this->expectException(JWTTokenException::class); $valid = $validator->validateToken($token); } /** + * @covers * @dataProvider tokenGenerator */ public function testPinningIssuer($issuer,$audience,$key,$token) { $goodIssuer = "a-dom.tld"; - $jwtKey = new JwtPlaintextKey($key); - $jwtToken = new JwtToken($jwtKey, $token); + $jwtKey = new JWTPlaintextKey($key); + $jwtToken = new JWTToken($jwtKey, $token); - $validator = new JwtValidator(); + $validator = new JWTValidator(); $validator->requireIssuer($goodIssuer); if ($goodIssuer != $issuer) { - $this->expectException(JwtTokenException::class); + $this->expectException(JWTTokenException::class); } $valid = $validator->validateToken($jwtToken); if ($goodIssuer == $issuer) { @@ -52,18 +59,19 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase } /** + * @covers * @dataProvider tokenGenerator */ public function testPinningAudience($issuer,$audience,$key,$token) { $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ]; - $jwtKey = new JwtPlaintextKey($key); - $jwtToken = new JwtToken($jwtKey, $token); + $jwtKey = new JWTPlaintextKey($key); + $jwtToken = new JWTToken($jwtKey, $token); - $validator = new JwtValidator(); + $validator = new JWTValidator(); $validator->requireAudience($goodAudience); if (!in_array($audience, $goodAudience)) { - $this->expectException(JwtTokenException::class); + $this->expectException(JWTTokenException::class); } $valid = $validator->validateToken($jwtToken); if (in_array($audience, $goodAudience)) { @@ -72,20 +80,21 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase } /** + * @covers * @dataProvider tokenGenerator */ public function testPinningBoth($issuer,$audience,$key,$token) { $goodIssuer = "a-dom.tld"; $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ]; - $jwtKey = new JwtPlaintextKey($key); - $jwtToken = new JwtToken($jwtKey, $token); + $jwtKey = new JWTPlaintextKey($key); + $jwtToken = new JWTToken($jwtKey, $token); - $validator = new JwtValidator(); + $validator = new JWTValidator(); $validator->requireIssuer($goodIssuer); $validator->requireAudience($goodAudience); if (($goodIssuer != $issuer) || (!in_array($audience, $goodAudience))) { - $this->expectException(JwtTokenException::class); + $this->expectException(JWTTokenException::class); } $valid = $validator->validateToken($jwtToken); if (($goodIssuer == $issuer) && (in_array($audience, $goodAudience))) { @@ -93,21 +102,21 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase } } - public function tokenGenerator() + public static function tokenGenerator() { $keyrand = function () { return substr(sha1(microtime(true).rand(0,65535)), 5, 10); }; $token = function ($head,$claims,$key) { - $jwtKey = new JwtPlaintextKey($key); - $tok = new JwtToken($jwtKey); + $jwtKey = new JWTPlaintextKey($key); + $tok = new JWTToken($jwtKey); $tok->header->setAll($head); $tok->claims->setAll($claims); return $tok->getSignedToken(); }; $row = function ($iss, $aud, array $claims) use ($keyrand, $token) { $key = $keyrand(); - $jwtKey = new JwtPlaintextKey($key); + $jwtKey = new JWTPlaintextKey($key); return [ $iss, $aud,