5 Commits
0.1.0 ... 0.2.0

Author SHA1 Message Date
822b796d40 phpstan fixes 2023-04-09 14:12:48 +02:00
b9c690cb6e Fixed readme 2023-04-09 02:46:48 +02:00
fabf160346 Useless merge 2023-04-09 02:44:23 +02:00
953e831d84 Fixed capitalization, tests 2023-04-09 02:40:21 +02:00
5a4a2845e4 Update 'README.md'
Fixed code blocks
2022-12-29 01:18:13 +00:00
25 changed files with 260 additions and 200 deletions

View File

@ -1,4 +1,4 @@
# SimpleJwt # SimpleJWT
This is a simple library for generating (signing) and verifying JWT tokens. It 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 is by no means an advanced library. If you just need to sign and refresh tokens
@ -27,69 +27,77 @@ Install using composer:
## Usage ## Usage
You need a key for both generating and parsing tokens. Create a `JwtDerivedKey` You need a key for both generating and parsing tokens. Create a `JWTDerivedKey`
or a `JwtPlaintextKey` and pass it to the `JwtToken` constructor: or a `JWTPlaintextKey` and pass it to the `JWTToken` constructor:
use NoccyLabs\SimpleJwt\Key\{JwtDerivedKey,JwtPlaintextKey} ```php
use NoccyLabs\SimpleJWT\Key\{JWTDerivedKey,JWTPlaintextKey}
// Derive a key using secret and salt... // Derive a key using secret and salt...
$key = new JwtDerivedKey("secret", "salt"); $key = new JWTDerivedKey("secret", "salt");
// ...or use a prepared plaintext key // ...or use a prepared plaintext key
$key = new JwtPlaintextKey("This Should Be Binary Data.."); $key = new JWTPlaintextKey("This Should Be Binary Data..");
```
`JWTDerivedKey` uses hash_pbkdf2.
### Generating tokens ### Generating tokens
```php
use NoccyLabs\SimpleJWT\JWTToken;
use NoccyLabs\SimpleJwt\JwtToken; $tok = new JWTToken($key);
$tok->setExpiry("1h");
$tok = new JwtToken($key); $tok->claims->add("some/claim/MaxItems", 8);
$tok->setExpiry("1h");
$tok->claims->add("some/claim/MaxItems", 8);
$str = $tok->getSignedToken();
$str = $tok->getSignedToken();
```
### Parsing tokens ### Parsing tokens
Parsing is done by passing the raw token as the 2nd parameter Parsing is done by passing the raw token as the 2nd parameter
use NoccyLabs\SimpleJwt\JwtToken; ```php
use NoccyLabs\SimpleJWT\JWTToken;
$str = "...received token..."; $str = "...received token...";
$tok = new JwtToken($key, $str); $tok = new JWTToken($key, $str);
if (!$tok->isValid()) { if (!$tok->isValid()) {
// This check works, but using the validator might be better // This check works, but using the validator might be better
} }
// Using ->has() follwed by ->get() is one way // Using ->has() follwed by ->get() is one way
if ($tok->claims->has("some/claim/MaxItems")) { if ($tok->claims->has("some/claim/MaxItems")) {
// The claim exists, we can get the value (if any) // The claim exists, we can get the value (if any)
$val = $tok->claims->get("some/claim/MaxItems"); $val = $tok->claims->get("some/claim/MaxItems");
} }
// You can also use valueOf() to return a default value if needed // You can also use valueOf() to return a default value if needed
$val = $tok->claims->valueOf("some/claim/MaxItems", 64); $val = $tok->claims->valueOf("some/claim/MaxItems", 64);
```
### Validating tokens ### Validating tokens
use NoccyLabs\SimpleJwt\Validator\JwtValidator; ```php
use NoccyLabs\SimpleJWT\Validator\JWTValidator;
$validator = new JwtValidator(); $validator = new JWTValidator();
// Require that some claim exists // Require that some claim exists
$validator $validator
->requireIssuer("api.issuer.tld") ->requireIssuer("api.issuer.tld")
->requireAudience(["api.issuer.tld", "foo.issuer.tld"]) ->requireAudience(["api.issuer.tld", "foo.issuer.tld"])
->addRequiredClaim("some/required/Claim"); ->addRequiredClaim("some/required/Claim");
try { try {
// Pass a JwtToken to validateToken()... // Pass a JWTToken to validateToken()...
$valid = $validator->validateToken($tok); $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); $valid = $validator->validate($key, $tokenstr);
} }
catch (JwtValidatorException $e) { catch (JWTValidatorException $e) {
// validation failed // validation failed
} }
```

View File

@ -11,7 +11,15 @@
], ],
"autoload": { "autoload": {
"psr-4": { "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"
} }
} }

12
phpstan.neon Normal file
View File

@ -0,0 +1,12 @@
parameters:
level: 5
excludePaths:
- doc
- vendor
- tests
# Paths to include in the analysis
paths:
- src

View File

@ -1,22 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" bootstrap="vendor/autoload.php" executionOrder="depends,defects" beStrictAboutOutputDuringTests="true" cacheDirectory=".phpunit.cache" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="true">
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd" <coverage>
bootstrap="vendor/autoload.php" <include>
executionOrder="depends,defects" <directory suffix=".php">src</directory>
forceCoversAnnotation="true" </include>
beStrictAboutCoversAnnotation="true" </coverage>
beStrictAboutOutputDuringTests="true" <testsuites>
beStrictAboutTodoAnnotatedTests="true" <testsuite name="default">
verbose="true"> <directory suffix="Test.php">tests</directory>
<testsuites> </testsuite>
<testsuite name="default"> </testsuites>
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit> </phpunit>

View File

@ -1,6 +1,6 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Collection; namespace NoccyLabs\SimpleJWT\Collection;
use ArrayAccess; use ArrayAccess;
use Countable; use Countable;
@ -21,8 +21,8 @@ class PropertyBag
/** /**
* Add a property value, fails if the property exists * Add a property value, fails if the property exists
* *
* @param string Property name * @param string $prop Property name
* @param mixed Value * @param mixed $value Value
* @throws PropertyException if the property already exists * @throws PropertyException if the property already exists
*/ */
public function add(string $prop, $value) public function add(string $prop, $value)
@ -37,14 +37,19 @@ class PropertyBag
* Set a property value, create the property if it doesn't * Set a property value, create the property if it doesn't
* exist. * exist.
* *
* @param string Property name * @param string $prop Property name
* @param mixed Value * @param mixed $value Value
*/ */
public function set(string $prop, $value) public function set(string $prop, $value)
{ {
$this->props[$prop] = $value; $this->props[$prop] = $value;
} }
/**
* Apply properties without removing anything.
*
* @param array $props The properties to apply
*/
public function setAll(array $props) public function setAll(array $props)
{ {
$this->props = array_merge( $this->props = array_merge(
@ -57,7 +62,7 @@ class PropertyBag
* Get the value of a property, fails if the property does not exist. * Get the value of a property, fails if the property does not exist.
* Use the value() method to get with a default value * Use the value() method to get with a default value
* *
* @param string Property name * @param string $prop Property name
* @return mixed * @return mixed
* @throws PropertyException if the property does not exist * @throws PropertyException if the property does not exist
*/ */
@ -92,8 +97,8 @@ class PropertyBag
/** /**
* Get the value of the property, or use the provided default value. * Get the value of the property, or use the provided default value.
* *
* @param string Property name * @param string $prop Property name
* @param mixed Default value * @param mixed|null $default Default value
* @return mixed * @return mixed
*/ */
public function valueOf(string $prop, $default=null) public function valueOf(string $prop, $default=null)
@ -105,6 +110,8 @@ class PropertyBag
/** /**
* Remove a property * Remove a property
*
* @param string $prop Property name
*/ */
public function delete(string $prop) public function delete(string $prop)
{ {
@ -113,6 +120,8 @@ class PropertyBag
/** /**
* Check if a property is present * Check if a property is present
*
* @param string $prop Property name
*/ */
public function has(string $prop): bool public function has(string $prop): bool
{ {
@ -121,6 +130,8 @@ class PropertyBag
/** /**
* Check if all the provided properties are present * Check if all the provided properties are present
*
* @param array $props Property names
*/ */
public function hasAll(array $props) public function hasAll(array $props)
{ {

View File

@ -1,6 +1,6 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Collection; namespace NoccyLabs\SimpleJWT\Collection;
class PropertyException extends \RuntimeException class PropertyException extends \RuntimeException

View File

@ -1,19 +1,19 @@
<?php <?php
namespace NoccyLabs\SimpleJwt; namespace NoccyLabs\SimpleJWT;
use NoccyLabs\SimpleJwt\Collection\PropertyBag; use NoccyLabs\SimpleJWT\Collection\PropertyBag;
use NoccyLabs\SimpleJwt\Key\KeyInterface; use NoccyLabs\SimpleJWT\Key\KeyInterface;
/** /**
* *
* *
* *
* *
* @property-read header PropertyBag * @property-read PropertyBag $header
* @property-read claim PropertyBag * @property-read PropertyBag $claims
*/ */
class JwtToken class JWTToken
{ {
/** @var PropertyBag */ /** @var PropertyBag */
private $header; private $header;
@ -30,7 +30,8 @@ class JwtToken
* Constructor * Constructor
* *
* *
* @param KeyInterface The key used to sign the token * @param KeyInterface $key The key used to sign the token
* @param string|null $token Token data
*/ */
public function __construct(KeyInterface $key, ?string $token=null) public function __construct(KeyInterface $key, ?string $token=null)
{ {
@ -54,13 +55,13 @@ class JwtToken
$this->generated = false; $this->generated = false;
[ $header, $payload, $signature ] = explode(".", trim($token), 3); [ $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) { if ($signature == $hash) {
$this->valid = true; $this->valid = true;
} }
$this->header = new PropertyBag(json_decode(JwtUtil::decode($header), true)); $this->header = new PropertyBag(json_decode(JWTUtil::decode($header), true));
$this->claims = new PropertyBag(json_decode(JwtUtil::decode($payload), true)); $this->claims = new PropertyBag(json_decode(JWTUtil::decode($payload), true));
if ($this->header->has('exp')) { if ($this->header->has('exp')) {
$exp = intval($this->header->get('exp')); $exp = intval($this->header->get('exp'));
@ -127,6 +128,8 @@ class JwtToken
case 'm': case 'm':
$fact = 60; $fact = 60;
break; break;
default:
throw new \InvalidArgumentException();
} }
$this->header->set('exp', time() + (intval($match[1]) * $fact)); $this->header->set('exp', time() + (intval($match[1]) * $fact));
} else { } else {
@ -136,9 +139,9 @@ class JwtToken
public function getSignedToken(): string public function getSignedToken(): string
{ {
$header = JwtUtil::encode($this->header->getJson()); $header = JWTUtil::encode($this->header->getJson());
$payload = JwtUtil::encode($this->claims->getJson()); $payload = JWTUtil::encode($this->claims->getJson());
$hash = JwtUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); $hash = JWTUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true));
return $header.".".$payload.".".$hash; return $header.".".$payload.".".$hash;
} }

View File

@ -1,9 +1,9 @@
<?php <?php
namespace NoccyLabs\SimpleJwt; namespace NoccyLabs\SimpleJWT;
class JwtUtil class JWTUtil
{ {
public static function encode($data) { public static function encode($data) {
return rtrim(str_replace(['+', '/'], ['-', '_'], base64_encode($data)), "="); return rtrim(str_replace(['+', '/'], ['-', '_'], base64_encode($data)), "=");

View File

@ -1,8 +1,8 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Key; namespace NoccyLabs\SimpleJWT\Key;
class JwtDerivedKey implements KeyInterface class JWTDerivedKey implements KeyInterface
{ {
private $key; private $key;

View File

@ -1,8 +1,8 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Key; namespace NoccyLabs\SimpleJWT\Key;
class JwtPlaintextKey implements KeyInterface class JWTPlaintextKey implements KeyInterface
{ {
private $key; private $key;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Key; namespace NoccyLabs\SimpleJWT\Key;
interface KeyInterface interface KeyInterface
{ {

View File

@ -0,0 +1,9 @@
<?php
namespace NoccyLabs\SimpleJWT\Validator;
class JWTClaimException extends JWTValidatorException
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace NoccyLabs\SimpleJWT\Validator;
class JWTHeaderException extends JWTValidatorException
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace NoccyLabs\SimpleJWT\Validator;
class JWTTokenException extends JWTValidatorException
{
}

View File

@ -1,11 +1,11 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Validator; namespace NoccyLabs\SimpleJWT\Validator;
use NoccyLabs\SimpleJwt\JwtToken; use NoccyLabs\SimpleJWT\JWTToken;
use NoccyLabs\SimpleJwt\Key\KeyInterface; use NoccyLabs\SimpleJWT\Key\KeyInterface;
class JwtValidator class JWTValidator
{ {
private $requireHeaders = []; private $requireHeaders = [];
@ -41,32 +41,32 @@ class JwtValidator
$this->requireAudience = (array)$audience; $this->requireAudience = (array)$audience;
} }
public function validateToken(JwtToken $token) public function validateToken(JWTToken $token)
{ {
if (!$token->isValid()) { 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)) { 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)) { 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) { if ($this->requireIssuer) {
$hasIssuer = $token->header->has("iss"); $hasIssuer = $token->header->has("iss");
if ((!$hasIssuer) if ((!$hasIssuer)
|| (!in_array($token->header->get("iss"), $this->requireIssuer))) || (!in_array($token->header->get("iss"), $this->requireIssuer)))
throw new JwtTokenException("Invalid issuer"); throw new JWTTokenException("Invalid issuer");
} }
if ($this->requireAudience) { if ($this->requireAudience) {
$hasAudience = $token->header->has("aud"); $hasAudience = $token->header->has("aud");
if ((!$hasAudience) if ((!$hasAudience)
|| (!in_array($token->header->get("aud"), $this->requireAudience))) || (!in_array($token->header->get("aud"), $this->requireAudience)))
throw new JwtTokenException("Invalid audience"); throw new JWTTokenException("Invalid audience");
} }
return true; return true;
@ -74,7 +74,7 @@ class JwtValidator
public function validate(KeyInterface $key, string $raw) public function validate(KeyInterface $key, string $raw)
{ {
$token = new JwtToken($key, $raw); $token = new JWTToken($key, $raw);
if ($this->validateToken($token)) { if ($this->validateToken($token)) {
return $token; return $token;
} }

View File

@ -0,0 +1,8 @@
<?php
namespace NoccyLabs\SimpleJWT\Validator;
class JWTValidatorException extends \RuntimeException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace NoccyLabs\SimpleJwt\Validator;
class JwtClaimException extends JwtValidatorException
{
}

View File

@ -1,8 +0,0 @@
<?php
namespace NoccyLabs\SimpleJwt\Validator;
class JwtHeaderException extends JwtValidatorException
{
}

View File

@ -1,8 +0,0 @@
<?php
namespace NoccyLabs\SimpleJwt\Validator;
class JwtTokenException extends JwtValidatorException
{
}

View File

@ -1,8 +0,0 @@
<?php
namespace NoccyLabs\SimpleJwt\Validator;
class JwtValidatorException extends \RuntimeException
{
}

View File

@ -1,17 +1,20 @@
<?php <?php
namespace NoccyLabs\SimpleJwt; namespace NoccyLabs\SimpleJWT;
use NoccyLabs\SimpleJwt\Key\JwtPlaintextKey; use NoccyLabs\SimpleJWT\Key\JWTPlaintextKey;
class JwtTokenTest extends \PhpUnit\Framework\TestCase class JWTTokenTest extends \PHPUnit\Framework\TestCase
{ {
/**
* @covers
*/
public function testGeneratingTokens() public function testGeneratingTokens()
{ {
$key = new JwtPlaintextKey("test"); $key = new JWTPlaintextKey("test");
$tok = new JwtToken($key); $tok = new JWTToken($key);
$tok->addClaim("foo", true); $tok->addClaim("foo", true);
$token = $tok->getSignedToken(); $token = $tok->getSignedToken();
@ -20,16 +23,19 @@ class JwtTokenTest extends \PhpUnit\Framework\TestCase
$this->assertTrue($tok->isGenerated()); $this->assertTrue($tok->isGenerated());
} }
/**
* @covers
*/
public function testParsingTokens() public function testParsingTokens()
{ {
$key = new JwtPlaintextKey("test"); $key = new JWTPlaintextKey("test");
$tok = new JwtToken($key); $tok = new JWTToken($key);
$tok->addClaim("foo", true); $tok->addClaim("foo", true);
$token = $tok->getSignedToken(); $token = $tok->getSignedToken();
$parsed = new JwtToken($key, $token); $parsed = new JWTToken($key, $token);
$this->assertTrue($parsed->isValid()); $this->assertTrue($parsed->isValid());
$this->assertFalse($parsed->isGenerated()); $this->assertFalse($parsed->isGenerated());

View File

@ -1,16 +1,19 @@
<?php <?php
namespace NoccyLabs\SimpleJwt; namespace NoccyLabs\SimpleJWT;
class JwtUtilTest extends \PhpUnit\Framework\TestCase class JWTUtilTest extends \PHPUnit\Framework\TestCase
{ {
/**
* @covers
*/
public function testTheEncodingShouldBeSymmetric() public function testTheEncodingShouldBeSymmetric()
{ {
$v1a = "HelloWorld"; $v1a = "HelloWorld";
$v1b = JwtUtil::encode($v1a); $v1b = JWTUtil::encode($v1a);
$v1c = JwtUtil::decode($v1b); $v1c = JWTUtil::decode($v1b);
$this->assertEquals($v1a, $v1c); $this->assertEquals($v1a, $v1c);
$this->assertNotEquals($v1a, $v1b); $this->assertNotEquals($v1a, $v1b);

View File

@ -1,34 +1,40 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Key; namespace NoccyLabs\SimpleJWT\Key;
class JwtDerivedKeyTest extends \PhpUnit\Framework\TestCase class JWTDerivedKeyTest extends \PHPUnit\Framework\TestCase
{ {
/**
* @covers
*/
public function testTheDerivedKeysShouldBeConsistent() public function testTheDerivedKeysShouldBeConsistent()
{ {
$key1a = new JwtDerivedKey("foo", "foosalt"); $key1a = new JWTDerivedKey("foo", "foosalt");
$key1b = new JwtDerivedKey("foo", "foosalt"); $key1b = new JWTDerivedKey("foo", "foosalt");
$this->assertNotNull($key1a); $this->assertNotNull($key1a);
$this->assertEquals($key1a->getBinaryKey(), $key1b->getBinaryKey()); $this->assertEquals($key1a->getBinaryKey(), $key1b->getBinaryKey());
$key2a = new JwtDerivedKey("bar", "foosalt"); $key2a = new JWTDerivedKey("bar", "foosalt");
$key2b = new JwtDerivedKey("bar", "barsalt"); $key2b = new JWTDerivedKey("bar", "barsalt");
$key2c = new JwtDerivedKey("bar", "barsalt"); $key2c = new JWTDerivedKey("bar", "barsalt");
$this->assertNotNull($key2a); $this->assertNotNull($key2a);
$this->assertNotEquals($key2a->getBinaryKey(), $key2b->getBinaryKey()); $this->assertNotEquals($key2a->getBinaryKey(), $key2b->getBinaryKey());
$this->assertEquals($key2b->getBinaryKey(), $key2c->getBinaryKey()); $this->assertEquals($key2b->getBinaryKey(), $key2c->getBinaryKey());
} }
/**
* @covers
*/
public function testTheDerivedKeysShouldBeUnique() public function testTheDerivedKeysShouldBeUnique()
{ {
$keys = []; $keys = [];
$keys[] = (new JwtDerivedKey("foo", "foosalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("foo", "foosalt"))->getBinaryKey();
$keys[] = (new JwtDerivedKey("foo", "barsalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("foo", "barsalt"))->getBinaryKey();
$keys[] = (new JwtDerivedKey("foo", "bazsalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("foo", "bazsalt"))->getBinaryKey();
$keys[] = (new JwtDerivedKey("bar", "foosalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("bar", "foosalt"))->getBinaryKey();
$keys[] = (new JwtDerivedKey("bar", "barsalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("bar", "barsalt"))->getBinaryKey();
$keys[] = (new JwtDerivedKey("bar", "bazsalt"))->getBinaryKey(); $keys[] = (new JWTDerivedKey("bar", "bazsalt"))->getBinaryKey();
$unique = array_unique($keys); $unique = array_unique($keys);
$this->assertEquals(count($keys), count($unique)); $this->assertEquals(count($keys), count($unique));

View File

@ -1,16 +1,19 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Key; namespace NoccyLabs\SimpleJWT\Key;
class JwtPlaintextKeyTest extends \PhpUnit\Framework\TestCase class JWTPlaintextKeyTest extends \PHPUnit\Framework\TestCase
{ {
/**
* @covers
*/
public function testThePlaintextKeyShouldBeReturned() public function testThePlaintextKeyShouldBeReturned()
{ {
$key = new JwtPlaintextKey("foo"); $key = new JWTPlaintextKey("foo");
$this->assertEquals("foo", $key->getBinaryKey()); $this->assertEquals("foo", $key->getBinaryKey());
$key = new JwtPlaintextKey("bar"); $key = new JWTPlaintextKey("bar");
$this->assertEquals("bar", $key->getBinaryKey()); $this->assertEquals("bar", $key->getBinaryKey());
} }
} }

View File

@ -1,49 +1,56 @@
<?php <?php
namespace NoccyLabs\SimpleJwt\Validator; namespace NoccyLabs\SimpleJWT\Validator;
use NoccyLabs\SimpleJwt\JwtToken; use NoccyLabs\SimpleJWT\JWTToken;
use NoccyLabs\SimpleJwt\Key\JwtPlaintextKey; use NoccyLabs\SimpleJWT\Key\JWTPlaintextKey;
class JwtValidatorTest extends \PhpUnit\Framework\TestCase class JWTValidatorTest extends \PHPUnit\Framework\TestCase
{ {
/**
* @covers
*/
public function testValidTokensShouldPassWithDefaultConfiguration() public function testValidTokensShouldPassWithDefaultConfiguration()
{ {
$key = new JwtPlaintextKey("key"); $key = new JWTPlaintextKey("key");
$token = new JwtToken($key); $token = new JWTToken($key);
$validator = new JwtValidator(); $validator = new JWTValidator();
$valid = $validator->validateToken($token); $valid = $validator->validateToken($token);
$this->assertEquals(true, $valid); $this->assertEquals(true, $valid);
} }
/**
* @covers
*/
public function testExpiredTokensShouldFailWithException() public function testExpiredTokensShouldFailWithException()
{ {
$key = new JwtPlaintextKey("key"); $key = new JWTPlaintextKey("key");
$token = new JwtToken($key); $token = new JWTToken($key);
$token->header->set("exp", 0); $token->header->set("exp", 0);
$token = new JwtToken($key, $token->getSignedToken()); $token = new JWTToken($key, $token->getSignedToken());
$validator = new JwtValidator(); $validator = new JWTValidator();
$this->expectException(JwtTokenException::class); $this->expectException(JWTTokenException::class);
$valid = $validator->validateToken($token); $valid = $validator->validateToken($token);
} }
/** /**
* @covers
* @dataProvider tokenGenerator * @dataProvider tokenGenerator
*/ */
public function testPinningIssuer($issuer,$audience,$key,$token) public function testPinningIssuer($issuer,$audience,$key,$token)
{ {
$goodIssuer = "a-dom.tld"; $goodIssuer = "a-dom.tld";
$jwtKey = new JwtPlaintextKey($key); $jwtKey = new JWTPlaintextKey($key);
$jwtToken = new JwtToken($jwtKey, $token); $jwtToken = new JWTToken($jwtKey, $token);
$validator = new JwtValidator(); $validator = new JWTValidator();
$validator->requireIssuer($goodIssuer); $validator->requireIssuer($goodIssuer);
if ($goodIssuer != $issuer) { if ($goodIssuer != $issuer) {
$this->expectException(JwtTokenException::class); $this->expectException(JWTTokenException::class);
} }
$valid = $validator->validateToken($jwtToken); $valid = $validator->validateToken($jwtToken);
if ($goodIssuer == $issuer) { if ($goodIssuer == $issuer) {
@ -52,18 +59,19 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase
} }
/** /**
* @covers
* @dataProvider tokenGenerator * @dataProvider tokenGenerator
*/ */
public function testPinningAudience($issuer,$audience,$key,$token) public function testPinningAudience($issuer,$audience,$key,$token)
{ {
$goodAudience = [ "a-dom.tld", "app.a-dom.tld" ]; $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ];
$jwtKey = new JwtPlaintextKey($key); $jwtKey = new JWTPlaintextKey($key);
$jwtToken = new JwtToken($jwtKey, $token); $jwtToken = new JWTToken($jwtKey, $token);
$validator = new JwtValidator(); $validator = new JWTValidator();
$validator->requireAudience($goodAudience); $validator->requireAudience($goodAudience);
if (!in_array($audience, $goodAudience)) { if (!in_array($audience, $goodAudience)) {
$this->expectException(JwtTokenException::class); $this->expectException(JWTTokenException::class);
} }
$valid = $validator->validateToken($jwtToken); $valid = $validator->validateToken($jwtToken);
if (in_array($audience, $goodAudience)) { if (in_array($audience, $goodAudience)) {
@ -72,20 +80,21 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase
} }
/** /**
* @covers
* @dataProvider tokenGenerator * @dataProvider tokenGenerator
*/ */
public function testPinningBoth($issuer,$audience,$key,$token) public function testPinningBoth($issuer,$audience,$key,$token)
{ {
$goodIssuer = "a-dom.tld"; $goodIssuer = "a-dom.tld";
$goodAudience = [ "a-dom.tld", "app.a-dom.tld" ]; $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ];
$jwtKey = new JwtPlaintextKey($key); $jwtKey = new JWTPlaintextKey($key);
$jwtToken = new JwtToken($jwtKey, $token); $jwtToken = new JWTToken($jwtKey, $token);
$validator = new JwtValidator(); $validator = new JWTValidator();
$validator->requireIssuer($goodIssuer); $validator->requireIssuer($goodIssuer);
$validator->requireAudience($goodAudience); $validator->requireAudience($goodAudience);
if (($goodIssuer != $issuer) || (!in_array($audience, $goodAudience))) { if (($goodIssuer != $issuer) || (!in_array($audience, $goodAudience))) {
$this->expectException(JwtTokenException::class); $this->expectException(JWTTokenException::class);
} }
$valid = $validator->validateToken($jwtToken); $valid = $validator->validateToken($jwtToken);
if (($goodIssuer == $issuer) && (in_array($audience, $goodAudience))) { 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 () { $keyrand = function () {
return substr(sha1(microtime(true).rand(0,65535)), 5, 10); return substr(sha1(microtime(true).rand(0,65535)), 5, 10);
}; };
$token = function ($head,$claims,$key) { $token = function ($head,$claims,$key) {
$jwtKey = new JwtPlaintextKey($key); $jwtKey = new JWTPlaintextKey($key);
$tok = new JwtToken($jwtKey); $tok = new JWTToken($jwtKey);
$tok->header->setAll($head); $tok->header->setAll($head);
$tok->claims->setAll($claims); $tok->claims->setAll($claims);
return $tok->getSignedToken(); return $tok->getSignedToken();
}; };
$row = function ($iss, $aud, array $claims) use ($keyrand, $token) { $row = function ($iss, $aud, array $claims) use ($keyrand, $token) {
$key = $keyrand(); $key = $keyrand();
$jwtKey = new JwtPlaintextKey($key); $jwtKey = new JWTPlaintextKey($key);
return [ return [
$iss, $iss,
$aud, $aud,