Skip to content

Commit edaa4be

Browse files
committed
add validator for 'bearer' token with id_token
1 parent bb63483 commit edaa4be

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

src/Utils/Bearer.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Pdsinterop\Solid\Auth\Utils;
4+
5+
use DateInterval;
6+
use Exception;
7+
use Jose\Component\Core\JWK;
8+
use Jose\Component\Core\Util\ECKey;
9+
use Jose\Component\Core\Util\RSAKey;
10+
use Lcobucci\Clock\SystemClock;
11+
use Lcobucci\JWT\Configuration;
12+
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
13+
use Lcobucci\JWT\Signer\Key\InMemory;
14+
use Lcobucci\JWT\Token\InvalidTokenStructure;
15+
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
16+
use Lcobucci\JWT\Validation\Constraint\SignedWith;
17+
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
18+
use Pdsinterop\Solid\Auth\Exception\AuthorizationHeaderException;
19+
use Pdsinterop\Solid\Auth\Exception\InvalidTokenException;
20+
use Psr\Http\Message\ServerRequestInterface;
21+
22+
/**
23+
* This class contains code to fetch the WebId from a request
24+
* that is make in legacy mode (bearer token with pop)
25+
*/
26+
class Bearer {
27+
28+
private JtiValidator $jtiValidator;
29+
30+
public function __construct(JtiValidator $jtiValidator)
31+
{
32+
$this->jtiValidator = $jtiValidator;
33+
}
34+
35+
/**
36+
* This method fetches the WebId from a request and verifies
37+
* that the request has a valid pop token that matches
38+
* the access token.
39+
*
40+
* @param ServerRequestInterface $request Server Request
41+
*
42+
* @return string the WebId, or "public" if no WebId is found
43+
*
44+
* @throws Exception "Invalid token" when the pop token is invalid
45+
*/
46+
public function getWebId($request) {
47+
$serverParams = $request->getServerParams();
48+
49+
if (empty($serverParams['HTTP_AUTHORIZATION'])) {
50+
$webId = "public";
51+
} else {
52+
$this->validateRequestHeaders($serverParams);
53+
54+
[, $jwt] = explode(" ", $serverParams['HTTP_AUTHORIZATION'], 2);
55+
56+
try {
57+
$this->validateJwt($jwt, $request);
58+
} catch (RequiredConstraintsViolated $e) {
59+
throw new InvalidTokenException($e->getMessage(), 0, $e);
60+
}
61+
$idToken = $this->getIdTokenFromJwt($jwt);
62+
63+
try {
64+
$this->validateIdToken($idToken, $request);
65+
} catch (RequiredConstraintsViolated $e) {
66+
throw new InvalidTokenException($e->getMessage(), 0, $e);
67+
}
68+
$webId = $this->getSubjectFromIdToken($idToken);
69+
}
70+
71+
return $webId;
72+
}
73+
74+
/**
75+
* @param string $jwt JWT access token, raw
76+
* @param ServerRequestInterface $request Server Request
77+
* @return bool
78+
*
79+
* FIXME: Add more validations to the token;
80+
*/
81+
public function validateJwt($jwt, $request) {
82+
$jwtConfig = Configuration::forUnsecuredSigner();
83+
$jwtConfig->parser()->parse($jwt);
84+
return true;
85+
}
86+
87+
/**
88+
* validates that the provided OIDC ID Token
89+
* @param string $token The OIDS ID Token (raw)
90+
* @param ServerRequestInterface $request Server Request
91+
* @return bool True if the id token is valid
92+
* @throws InvalidTokenException when the tokens is not valid
93+
*
94+
* FIXME: Add more validations to the token;
95+
*/
96+
public function validateIdToken($token, $request) {
97+
$jwtConfig = Configuration::forUnsecuredSigner();
98+
$jwtConfig->parser()->parse($jwt);
99+
return true;
100+
}
101+
102+
private function getIdTokenFromJwt($jwt) {
103+
$jwtConfig = Configuration::forUnsecuredSigner();
104+
try {
105+
$jwt = $jwtConfig->parser()->parse($jwt);
106+
} catch(Exception $e) {
107+
throw new InvalidTokenException("Invalid JWT token", 409, $e);
108+
}
109+
110+
$idToken = $jwt->claims()->get("id_token");
111+
if ($idToken === null) {
112+
throw new InvalidTokenException('Missing "id_token"');
113+
}
114+
return $idToken;
115+
}
116+
117+
private function getSubjectFromIdToken($idToken) {
118+
$jwtConfig = Configuration::forUnsecuredSigner();
119+
try {
120+
$jwt = $jwtConfig->parser()->parse($idToken);
121+
} catch(Exception $e) {
122+
throw new InvalidTokenException("Invalid ID token", 409, $e);
123+
}
124+
125+
$sub = $jwt->claims()->get("sub");
126+
if ($sub === null) {
127+
throw new InvalidTokenException('Missing "sub"');
128+
}
129+
return $sub;
130+
}
131+
132+
private function validateRequestHeaders($serverParams) {
133+
if (str_contains($serverParams['HTTP_AUTHORIZATION'], ' ') === false) {
134+
throw new AuthorizationHeaderException("Authorization Header does not contain parameters");
135+
}
136+
137+
if (str_starts_with(strtolower($serverParams['HTTP_AUTHORIZATION']), 'bearer') === false) {
138+
throw new AuthorizationHeaderException('Only "bearer" authorization scheme is supported');
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)