Skip to content

Commit 27e379e

Browse files
authored
Mandatory members (#91)
* Support for mandatory header parameters and claims
1 parent 6c702ab commit 27e379e

File tree

6 files changed

+168
-5
lines changed

6 files changed

+168
-5
lines changed

src/Component/Checker/ClaimCheckerManager.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,17 @@ public function getCheckers(): array
7979
* This method returns an array with all checked claims.
8080
* It is up to the implementor to decide use the claims that have not been checked.
8181
*
82-
* @param array $claims
82+
* @param array $claims
83+
* @param string[] $mandatoryClaims
8384
*
8485
* @throws InvalidClaimException
86+
* @throws MissingMandatoryClaimException
8587
*
8688
* @return array
8789
*/
88-
public function check(array $claims): array
90+
public function check(array $claims, array $mandatoryClaims = []): array
8991
{
92+
$this->checkMandatoryClaims($mandatoryClaims, $claims);
9093
$checkedClaims = [];
9194
foreach ($this->checkers as $claim => $checker) {
9295
if (array_key_exists($claim, $claims)) {
@@ -97,4 +100,22 @@ public function check(array $claims): array
97100

98101
return $checkedClaims;
99102
}
103+
104+
/**
105+
* @param string[] $mandatoryClaims
106+
* @param array $claims
107+
*
108+
* @throws MissingMandatoryClaimException
109+
*/
110+
private function checkMandatoryClaims(array $mandatoryClaims, array $claims)
111+
{
112+
if (empty($mandatoryClaims)) {
113+
return;
114+
}
115+
$diff = array_keys(array_diff_key(array_flip($mandatoryClaims), $claims));
116+
117+
if (!empty($diff)) {
118+
throw new MissingMandatoryClaimException(sprintf('The following claims are mandatory: %s.', implode(', ', $diff)), $diff);
119+
}
120+
}
100121
}

src/Component/Checker/HeaderCheckerManager.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,22 @@ private function add(HeaderChecker $checker): self
9999
* All header parameters are checked against the header parameter checkers.
100100
* If one fails, the InvalidHeaderException is thrown.
101101
*
102-
* @param JWT $jwt
103-
* @param int $index
102+
* @param JWT $jwt
103+
* @param int $index
104+
* @param string[] $mandatoryHeaderParameters
104105
*
105106
* @throws InvalidHeaderException
107+
* @throws MissingMandatoryHeaderParameterException
106108
*/
107-
public function check(JWT $jwt, int $index)
109+
public function check(JWT $jwt, int $index, array $mandatoryHeaderParameters = [])
108110
{
109111
foreach ($this->tokenTypes as $tokenType) {
110112
if ($tokenType->supports($jwt)) {
111113
$protected = [];
112114
$unprotected = [];
113115
$tokenType->retrieveTokenHeaders($jwt, $index, $protected, $unprotected);
114116
$this->checkDuplicatedHeaderParameters($protected, $unprotected);
117+
$this->checkMandatoryHeaderParameters($mandatoryHeaderParameters, $protected, $unprotected);
115118
$this->checkHeaders($protected, $unprotected);
116119

117120
return;
@@ -133,6 +136,25 @@ private function checkDuplicatedHeaderParameters(array $header1, array $header2)
133136
}
134137
}
135138

139+
/**
140+
* @param string[] $mandatoryHeaderParameters
141+
* @param array $protected
142+
* @param array $unprotected
143+
*
144+
* @throws MissingMandatoryHeaderParameterException
145+
*/
146+
private function checkMandatoryHeaderParameters(array $mandatoryHeaderParameters, array $protected, array $unprotected)
147+
{
148+
if (empty($mandatoryHeaderParameters)) {
149+
return;
150+
}
151+
$diff = array_keys(array_diff_key(array_flip($mandatoryHeaderParameters), array_merge($protected, $unprotected)));
152+
153+
if (!empty($diff)) {
154+
throw new MissingMandatoryHeaderParameterException(sprintf('The following header parameters are mandatory: %s.', implode(', ', $diff)), $diff);
155+
}
156+
}
157+
136158
/**
137159
* @param array $protected
138160
* @param array $header
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* The MIT License (MIT)
7+
*
8+
* Copyright (c) 2014-2018 Spomky-Labs
9+
*
10+
* This software may be modified and distributed under the terms
11+
* of the MIT license. See the LICENSE file for details.
12+
*/
13+
14+
namespace Jose\Component\Checker;
15+
16+
class MissingMandatoryClaimException extends \Exception
17+
{
18+
/**
19+
* @var string[]
20+
*/
21+
private $claims;
22+
23+
/**
24+
* MissingMandatoryClaimException constructor.
25+
*
26+
* @param string $message
27+
* @param string[] $claims
28+
*/
29+
public function __construct(string $message, array $claims)
30+
{
31+
parent::__construct($message);
32+
33+
$this->claims = $claims;
34+
}
35+
36+
/**
37+
* @return string[]
38+
*/
39+
public function getClaims(): array
40+
{
41+
return $this->claims;
42+
}
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* The MIT License (MIT)
7+
*
8+
* Copyright (c) 2014-2018 Spomky-Labs
9+
*
10+
* This software may be modified and distributed under the terms
11+
* of the MIT license. See the LICENSE file for details.
12+
*/
13+
14+
namespace Jose\Component\Checker;
15+
16+
class MissingMandatoryHeaderParameterException extends \Exception
17+
{
18+
/**
19+
* @var string[]
20+
*/
21+
private $parameters;
22+
23+
/**
24+
* MissingMandatoryHeaderParameterException constructor.
25+
*
26+
* @param string $message
27+
* @param string[] $parameters
28+
*/
29+
public function __construct(string $message, array $parameters)
30+
{
31+
parent::__construct($message);
32+
33+
$this->parameters = $parameters;
34+
}
35+
36+
/**
37+
* @return string[]
38+
*/
39+
public function getParameters(): array
40+
{
41+
return $this->parameters;
42+
}
43+
}

src/Component/Checker/Tests/ClaimCheckerManagerFactoryTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,25 @@ public function iCanCheckValidPayloadClaims()
7272
self::assertEquals($expected, $result);
7373
}
7474

75+
/**
76+
* @test
77+
* @expectedException \Jose\Component\Checker\MissingMandatoryClaimException
78+
* @expectedExceptionMessage The following claims are mandatory: bar.
79+
*/
80+
public function theMandatoryClaimsAreNotSet()
81+
{
82+
$payload = [
83+
'exp' => time() + 3600,
84+
'iat' => time() - 1000,
85+
'nbf' => time() - 100,
86+
'foo' => 'bar',
87+
];
88+
$expected = $payload;
89+
unset($expected['foo']);
90+
$manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']);
91+
$manager->check($payload, ['exp', 'foo', 'bar']);
92+
}
93+
7594
/**
7695
* @var ClaimCheckerManagerFactory|null
7796
*/

src/Component/Checker/Tests/HeaderCheckerManagerFactoryTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ public function theHeaderContainsUnknownParametersAndIsSuccessfullyChecked()
125125
self::assertTrue(true);
126126
}
127127

128+
/**
129+
* @test
130+
* @expectedException \Jose\Component\Checker\MissingMandatoryHeaderParameterException
131+
* @expectedExceptionMessage The following header parameters are mandatory: mandatory.
132+
*/
133+
public function theHeaderDoesNotContainSomeMandatoryParameters()
134+
{
135+
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
136+
$payload = [];
137+
$protected = ['aud' => 'Audience', 'iss' => 'Another Service'];
138+
$unprotected = ['foo' => 'bar'];
139+
$token = Token::create(json_encode($payload), $protected, $unprotected);
140+
$headerCheckerManager->check($token, 0, ['aud', 'iss', 'mandatory']);
141+
}
142+
128143
/**
129144
* @test
130145
* @expectedException \InvalidArgumentException

0 commit comments

Comments
 (0)