Added support for validating token issuer and audience
* Use requireIssuer() and requireAudience() on the JwtValidator to make sure that the token is for what you expect it to be for. * A setAll() method has been added to property bag, applying but not overriding values. * Added tests for JwtValidator.
This commit is contained in:
parent
9d85e2ccef
commit
7753853e58
@ -79,7 +79,10 @@ Parsing is done by passing the raw token as the 2nd parameter
|
|||||||
|
|
||||||
$validator = new JwtValidator();
|
$validator = new JwtValidator();
|
||||||
// Require that some claim exists
|
// Require that some claim exists
|
||||||
$validator->addRequiredClaim("some/required/Claim");
|
$validator
|
||||||
|
->requireIssuer("api.issuer.tld")
|
||||||
|
->requireAudience(["api.issuer.tld", "foo.issuer.tld"])
|
||||||
|
->addRequiredClaim("some/required/Claim");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Pass a JwtToken to validateToken()...
|
// Pass a JwtToken to validateToken()...
|
||||||
|
@ -45,6 +45,14 @@ class PropertyBag
|
|||||||
$this->props[$prop] = $value;
|
$this->props[$prop] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setAll(array $props)
|
||||||
|
{
|
||||||
|
$this->props = array_merge(
|
||||||
|
$this->props,
|
||||||
|
$props
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -11,6 +11,10 @@ class JwtValidator
|
|||||||
|
|
||||||
private $requireClaims = [];
|
private $requireClaims = [];
|
||||||
|
|
||||||
|
private $requireIssuer = [];
|
||||||
|
|
||||||
|
private $requireAudience = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->requireHeaders = [
|
$this->requireHeaders = [
|
||||||
@ -27,6 +31,16 @@ class JwtValidator
|
|||||||
$this->requireClaims[] = $name;
|
$this->requireClaims[] = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requireIssuer($issuer)
|
||||||
|
{
|
||||||
|
$this->requireIssuer = (array)$issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requireAudience($audience)
|
||||||
|
{
|
||||||
|
$this->requireAudience = (array)$audience;
|
||||||
|
}
|
||||||
|
|
||||||
public function validateToken(JwtToken $token)
|
public function validateToken(JwtToken $token)
|
||||||
{
|
{
|
||||||
if (!$token->isValid()) {
|
if (!$token->isValid()) {
|
||||||
@ -41,6 +55,20 @@ class JwtValidator
|
|||||||
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->requireAudience) {
|
||||||
|
$hasAudience = $token->header->has("aud");
|
||||||
|
if ((!$hasAudience)
|
||||||
|
|| (!in_array($token->header->get("aud"), $this->requireAudience)))
|
||||||
|
throw new JwtTokenException("Invalid audience");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use NoccyLabs\SimpleJwt\Key\JwtPlaintextKey;
|
|||||||
class JwtValidatorTest extends \PhpUnit\Framework\TestCase
|
class JwtValidatorTest extends \PhpUnit\Framework\TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testValidKeysShouldPassWithDefaultConfiguration()
|
public function testValidTokensShouldPassWithDefaultConfiguration()
|
||||||
{
|
{
|
||||||
$key = new JwtPlaintextKey("key");
|
$key = new JwtPlaintextKey("key");
|
||||||
$token = new JwtToken($key);
|
$token = new JwtToken($key);
|
||||||
@ -18,7 +18,7 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase
|
|||||||
$this->assertEquals(true, $valid);
|
$this->assertEquals(true, $valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExpiredKeysShouldFailWithException()
|
public function testExpiredTokensShouldFailWithException()
|
||||||
{
|
{
|
||||||
$key = new JwtPlaintextKey("key");
|
$key = new JwtPlaintextKey("key");
|
||||||
$token = new JwtToken($key);
|
$token = new JwtToken($key);
|
||||||
@ -31,4 +31,56 @@ class JwtValidatorTest extends \PhpUnit\Framework\TestCase
|
|||||||
$valid = $validator->validateToken($token);
|
$valid = $validator->validateToken($token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider tokenGenerator
|
||||||
|
*/
|
||||||
|
public function testPinningIssuer($issuer,$audience,$key,$token)
|
||||||
|
{
|
||||||
|
$goodIssuer = "a-dom.tld";
|
||||||
|
$jwtKey = new JwtPlaintextKey($key);
|
||||||
|
$jwtToken = new JwtToken($jwtKey, $token);
|
||||||
|
|
||||||
|
$validator = new JwtValidator();
|
||||||
|
$validator->requireIssuer($goodIssuer);
|
||||||
|
if ($goodIssuer != $issuer) {
|
||||||
|
$this->expectException(JwtTokenException::class);
|
||||||
|
}
|
||||||
|
$valid = $validator->validateToken($jwtToken);
|
||||||
|
if ($goodIssuer == $issuer) {
|
||||||
|
$this->assertTrue($valid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// public function testPinningAudience()
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
$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);
|
||||||
|
return [
|
||||||
|
$iss,
|
||||||
|
$aud,
|
||||||
|
$key,
|
||||||
|
$token(['iss'=>$iss, 'aud'=>$aud], $claims, $key),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
$row("a-dom.tld", "a-dom.tld", []),
|
||||||
|
$row("b-dom.tld", "a-dom.tld", []),
|
||||||
|
$row("b-dom.tld", "b-dom.tld", []),
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user