Skip to content

Commit 6d98904

Browse files
committed
added docblocks in DPop.php
made getWebId catch constraints violated exceptions and throw invalid token only then, other exceptions indicate coding errors, so shouldn't be caught added a check that a dpop token is available, if the authorization header specifies it
1 parent bb725c4 commit 6d98904

2 files changed

Lines changed: 50 additions & 13 deletions

File tree

src/Utils/DPop.php

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,38 @@
1515
use Jose\Component\Core\Util\ECKey;
1616
use Jose\Component\Core\Util\RSAKey;
1717

18+
/**
19+
* This class contains code to fetch the WebId from a request
20+
* It also verifies that the request has a valid DPoP token
21+
* that matches the access token
22+
*/
1823
class DPop {
24+
25+
/**
26+
* This method fetches the WebId from a request and verifies
27+
* that the request has a valid DPoP token that matches
28+
* the access token.
29+
* @param Psr\Http\Message\ServerRequestInterface $request Server Request
30+
* @return string the WebId, or "public" if no WebId is found
31+
* @throws \Exception "Invalid token" when the DPoP token is invalid
32+
* @throws \Exception "Missng DPoP token" when the DPoP token is missing, but the Authorisation header in the request specifies it
33+
*/
1934
public function getWebId($request) {
2035
$auth = explode(" ", $request->getServerParams()['HTTP_AUTHORIZATION']);
2136
$jwt = $auth[1] ?? false;
2237

2338
if (strtolower($auth[0]) == "dpop") {
2439
$dpop = $request->getServerParams()['HTTP_DPOP'];
40+
//@FIXME: check that there is just one DPoP token in the request
2541
if ($dpop) {
2642
$dpopKey = $this->getDpopKey($dpop, $request);
27-
if (!$this->validateJwtDpop($jwt, $dpopKey)) {
28-
throw new \Exception("Invalid token");
43+
try {
44+
$this->validateJwtDpop($jwt, $dpopKey);
45+
} catch (Lcobucci\JWT\Validation\RequiredConstraintsViolated $e) {
46+
throw new \Exception("Invalid token", $e);
2947
}
48+
} else {
49+
throw new \Exception("Missing DPoP token");
3050
}
3151
}
3252

@@ -39,16 +59,21 @@ public function getWebId($request) {
3959
return $webId;
4060
}
4161

62+
/**
63+
* Returns the "kid" from the "jwk" header in the DPoP token.
64+
* The DPoP token must be valid.
65+
* @param string $dpop The DPoP token
66+
* @param Psr\Http\Message\ServerRequestInterface $request Server Request
67+
* @return string the "kid" from the "jwk" header in the DPoP token.
68+
* @throws Lcobucci\JWT\Validation\RequiredConstraintsViolated
69+
*/
4270
public function getDpopKey($dpop, $request) {
43-
//error_log("11");
4471
$this->validateDpop($dpop, $request);
45-
//error_log("22");
4672

4773
// 1. the string value is a well-formed JWT,
4874
$jwtConfig = $configuration = Configuration::forUnsecuredSigner();
4975
$dpop = $jwtConfig->parser()->parse($dpop);
50-
$jwk = $dpop->headers()->get("jwk");
51-
//error_log(print_r($jwk, true));
76+
$jwk = $dpop->headers()->get("jwk");
5277

5378
return $jwk['kid'];
5479
}
@@ -59,13 +84,10 @@ private function validateJwtDpop($jwt, $dpopKey) {
5984
$cnf = $jwt->claims()->get("cnf");
6085

6186
if ($cnf['jkt'] == $dpopKey) {
62-
//error_log("dpopKey matches");
6387
return true;
6488
}
65-
//error_log("dpopKey mismatch");
66-
//error_log(print_r($cnf, true));
67-
//error_log($dpopKey);
6889

90+
//@FIXME: add check for "ath" claim in DPoP token, per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop#section-7
6991
return false;
7092
}
7193

tests/unit/Utils/DPOPTest.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
77

88
/**
9-
* @covers DPop
9+
* @coversDefaultClass \Pdsinterop\Solid\Auth\Utils\DPop
10+
* @covers ::__construct
11+
* @covers ::<!public>
12+
* @uses \Pdsinterop\Solid\Auth\Utils\Base64Url
1013
*/
1114
class DPOPTest extends TestCase
1215
{
1316

1417
private $dpop;
1518
private $url;
1619
private $serverRequest;
17-
20+
1821
protected function sign($dpop, $privateKey=null)
1922
{
20-
$keyPath = dirname(__DIR__) . '/../fixtures/keys';
23+
$keyPath = __DIR__ . '/../../fixtures/keys';
2124
if (!$privateKey) {
2225
$privateKey = file_get_contents($keyPath . '/private.key');
2326
}
@@ -100,6 +103,9 @@ private function getWrongKey() {
100103
return $pubkey;
101104
}
102105

106+
/**
107+
* @covers ::validateDpop
108+
*/
103109
public function testWrongTyp(): void
104110
{
105111
$this->dpop['header']['typ'] = 'jwt';
@@ -112,6 +118,9 @@ public function testWrongTyp(): void
112118
$result = $dpop->validateDpop($token['token'], $this->serverRequest);
113119
}
114120

121+
/**
122+
* @covers ::validateDpop
123+
*/
115124
public function testAlgNone(): void
116125
{
117126
$this->dpop['header']['alg'] = 'none';
@@ -123,6 +132,9 @@ public function testAlgNone(): void
123132
$result = $dpop->validateDpop($token['token'], $this->serverRequest);
124133
}
125134

135+
/**
136+
* @covers ::validateDpop
137+
*/
126138
public function testWrongKey(): void
127139
{
128140
$theWrongKey = $this->getWrongKey();
@@ -143,6 +155,9 @@ public function testWrongKey(): void
143155
$this->assertFalse($result);
144156
}
145157

158+
/**
159+
* @covers ::validateDpop
160+
*/
146161
public function testCorrectToken(): void
147162
{
148163
$token = $this->sign($this->dpop);

0 commit comments

Comments
 (0)