22
33namespace Pdsinterop \Solid \Auth \Utils ;
44
5+ use DateInterval ;
6+ use Exception ;
57use Jose \Component \Core \JWK ;
68use Jose \Component \Core \Util \ECKey ;
79use Jose \Component \Core \Util \RSAKey ;
810use Lcobucci \Clock \SystemClock ;
911use Lcobucci \JWT \Configuration ;
12+ use Lcobucci \JWT \Signer \Ecdsa \Sha256 ;
1013use Lcobucci \JWT \Signer \Key \InMemory ;
14+ use Lcobucci \JWT \Token \InvalidTokenStructure ;
1115use Lcobucci \JWT \Validation \Constraint \LooseValidAt ;
1216use Lcobucci \JWT \Validation \Constraint \SignedWith ;
17+ use Lcobucci \JWT \Validation \RequiredConstraintsViolated ;
18+ use Psr \Http \Message \ServerRequestInterface ;
1319
1420/**
1521 * This class contains code to fetch the WebId from a request
@@ -30,12 +36,12 @@ public function __construct(JtiValidator $jtiValidator)
3036 * that the request has a valid DPoP token that matches
3137 * the access token.
3238 *
33- * @param Psr\Http\Message\ ServerRequestInterface $request Server Request
39+ * @param ServerRequestInterface $request Server Request
3440 *
3541 * @return string the WebId, or "public" if no WebId is found
3642 *
37- * @throws \ Exception "Invalid token" when the DPoP token is invalid
38- * @throws \ Exception "Missng DPoP token" when the DPoP token is missing, but the Authorisation header in the request specifies it
43+ * @throws Exception "Invalid token" when the DPoP token is invalid
44+ * @throws Exception "Missing DPoP token" when the DPoP token is missing, but the Authorisation header in the request specifies it
3945 */
4046 public function getWebId ($ request ) {
4147 // @FIXME: What happens when HTTP_AUTHORIZATION is not set?
@@ -73,12 +79,12 @@ public function getWebId($request) {
7379 * Returns the "kid" from the "jwk" header in the DPoP token.
7480 * The DPoP token must be valid.
7581 *
76- * @param string $dpop The DPoP token
77- * @param Psr\Http\Message\ ServerRequestInterface $request Server Request
82+ * @param string $dpop The DPoP token
83+ * @param ServerRequestInterface $request Server Request
7884 *
7985 * @return string the "kid" from the "jwk" header in the DPoP token.
8086 *
81- * @throws Lcobucci\JWT\Validation\ RequiredConstraintsViolated
87+ * @throws RequiredConstraintsViolated
8288 */
8389 public function getDpopKey ($ dpop , $ request ) {
8490 $ this ->validateDpop ($ dpop , $ request );
@@ -89,7 +95,7 @@ public function getDpopKey($dpop, $request) {
8995 $ jwk = $ dpop ->headers ()->get ("jwk " );
9096
9197 if (isset ($ jwk ['kid ' ]) === false ) {
92- throw new \ Exception ('Key ID is missing from JWK header ' );
98+ throw new Exception ('Key ID is missing from JWK header ' );
9399 }
94100
95101 return $ jwk ['kid ' ];
@@ -101,15 +107,15 @@ private function validateJwtDpop($jwt, $dpopKey) {
101107 $ cnf = $ jwt ->claims ()->get ("cnf " );
102108
103109 if ($ cnf === null ) {
104- throw new \ Exception ('JWT Confirmation claim (cnf) is missing ' );
110+ throw new Exception ('JWT Confirmation claim (cnf) is missing ' );
105111 }
106112
107113 if (isset ($ cnf ['jkt ' ]) === false ) {
108- throw new \ Exception ('JWT Confirmation claim (cnf) is missing Thumbprint (jkt) ' );
114+ throw new Exception ('JWT Confirmation claim (cnf) is missing Thumbprint (jkt) ' );
109115 }
110116
111117 if ($ cnf ['jkt ' ] !== $ dpopKey ) {
112- throw new \ Exception ('JWT Confirmation claim (cnf) provided Thumbprint (jkt) does not match Key ID from JWK header ' );
118+ throw new Exception ('JWT Confirmation claim (cnf) provided Thumbprint (jkt) does not match Key ID from JWK header ' );
113119 }
114120
115121 //@FIXME: add check for "ath" claim in DPoP token, per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop#section-7
@@ -120,12 +126,12 @@ private function validateJwtDpop($jwt, $dpopKey) {
120126 * Validates that the DPOP token matches all requirements from
121127 * https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop#section-4.2
122128 *
123- * @param string $dpop The DPOP token
124- * @param Psr\Http\Message\ ServerRequestInterface $request Server Request
129+ * @param string $dpop The DPOP token
130+ * @param ServerRequestInterface $request Server Request
125131 *
126- * @return bool True if the DPOP token is valid, false otherwise
132+ * @return bool True if the DPOP token is valid, false otherwise
127133 *
128- * @throws Lcobucci\JWT\Validation\ RequiredConstraintsViolated
134+ * @throws RequiredConstraintsViolated
129135 */
130136 public function validateDpop ($ dpop , $ request ) {
131137 /*
@@ -160,48 +166,48 @@ public function validateDpop($dpop, $request) {
160166 // 2. all required claims are contained in the JWT,
161167 $ htm = $ dpop ->claims ()->get ("htm " ); // http method
162168 if (!$ htm ) {
163- throw new \ Exception ("missing htm " );
169+ throw new Exception ("missing htm " );
164170 }
165171 $ htu = $ dpop ->claims ()->get ("htu " ); // http uri
166172 if (!$ htu ) {
167- throw new \ Exception ("missing htu " );
173+ throw new Exception ("missing htu " );
168174 }
169175 $ typ = $ dpop ->headers ()->get ("typ " );
170176 if (!$ typ ) {
171- throw new \ Exception ("missing typ " );
177+ throw new Exception ("missing typ " );
172178 }
173179 $ alg = $ dpop ->headers ()->get ("alg " );
174180 if (!$ alg ) {
175- throw new \ Exception ("missing alg " );
181+ throw new Exception ("missing alg " );
176182 }
177183
178184 // 3. the "typ" field in the header has the value "dpop+jwt",
179185 if ($ typ != "dpop+jwt " ) {
180- throw new \ Exception ("typ is not dpop+jwt " );
186+ throw new Exception ("typ is not dpop+jwt " );
181187 }
182188
183189 // 4. the algorithm in the header of the JWT indicates an asymmetric
184190 // digital signature algorithm, is not "none", is supported by the
185191 // application, and is deemed secure,
186192 if ($ alg == "none " ) {
187- throw new \ Exception ("alg is none " );
193+ throw new Exception ("alg is none " );
188194 }
189195
190196 // 5. that the JWT is signed using the public key contained in the
191197 // "jwk" header of the JWT,
192198 $ jwk = $ dpop ->headers ()->get ("jwk " );
193- $ webTokenJwk = \ Jose \ Component \ Core \ JWK ::createFromJson (json_encode ($ jwk ));
199+ $ webTokenJwk = JWK ::createFromJson (json_encode ($ jwk ));
194200 switch ($ alg ) {
195201 case "RS256 " :
196- $ pem = \ Jose \ Component \ Core \ Util \ RSAKey::createFromJWK ($ webTokenJwk )->toPEM ();
202+ $ pem = RSAKey::createFromJWK ($ webTokenJwk )->toPEM ();
197203 $ signer = new \Lcobucci \JWT \Signer \Rsa \Sha256 ();
198204 break ;
199205 case "ES256 " :
200- $ pem = \ Jose \ Component \ Core \ Util \ ECKey::convertToPEM ($ webTokenJwk );
201- $ signer = \ Lcobucci \ JWT \ Signer \ Ecdsa \ Sha256::create ();
206+ $ pem = ECKey::convertToPEM ($ webTokenJwk );
207+ $ signer = Sha256::create ();
202208 break ;
203209 default :
204- throw new \ Exception ("unsupported algorithm " );
210+ throw new Exception ("unsupported algorithm " );
205211 break ;
206212 }
207213 $ key = InMemory::plainText ($ pem );
@@ -211,7 +217,7 @@ public function validateDpop($dpop, $request) {
211217 // 6. the "htm" claim matches the HTTP method value of the HTTP request
212218 // in which the JWT was received (case-insensitive),
213219 if (strtolower ($ htm ) != strtolower ($ request ->getMethod ())) {
214- throw new \ Exception ("htm http method is invalid " );
220+ throw new Exception ("htm http method is invalid " );
215221 }
216222
217223 // 7. the "htu" claims matches the HTTP URI value for the HTTP request
@@ -223,12 +229,12 @@ public function validateDpop($dpop, $request) {
223229 //error_log("REQUESTED HTU $htu");
224230 //error_log("REQUESTED PATH $requestedPath");
225231 if ($ htu != $ requestedPath ) {
226- throw new \ Exception ("htu does not match requested path " );
232+ throw new Exception ("htu does not match requested path " );
227233 }
228234
229235 // 8. the token was issued within an acceptable timeframe (see Section 9.1), and
230236
231- $ leeway = new \ DateInterval ("PT60S " ); // allow 60 seconds clock skew
237+ $ leeway = new DateInterval ("PT60S " ); // allow 60 seconds clock skew
232238 $ clock = SystemClock::fromUTC ();
233239 $ validationConstraints [] = new LooseValidAt ($ clock , $ leeway ); // It will use the current time to validate (iat, nbf and exp)
234240 if (!$ jwtConfig ->validator ()->validate ($ dpop , ...$ validationConstraints )) {
@@ -238,11 +244,11 @@ public function validateDpop($dpop, $request) {
238244 // 9. that, within a reasonable consideration of accuracy and resource utilization, a JWT with the same "jti" value has not been received previously (see Section 9.1).
239245 $ jti = $ dpop ->claims ()->get ("jti " );
240246 if ($ jti === null ) {
241- throw new \ Exception ("jti is missing " );
247+ throw new Exception ("jti is missing " );
242248 }
243249 $ isJtiValid = $ this ->jtiValidator ->validate ($ jti , (string ) $ request ->getUri ());
244250 if (! $ isJtiValid ) {
245- throw new \ Exception ("jti is invalid " );
251+ throw new Exception ("jti is invalid " );
246252 }
247253
248254 // 10. that, if used with an access token, it also contains the 'ath' claim, with a hash of the access token
@@ -255,13 +261,13 @@ private function getSubjectFromJwt($jwt) {
255261 $ jwtConfig = $ configuration = Configuration::forUnsecuredSigner ();
256262 try {
257263 $ jwt = $ jwtConfig ->parser ()->parse ($ jwt );
258- } catch (\ Exception $ e ) {
259- throw new \ Exception ("Invalid JWT token " , 409 , $ e );
264+ } catch (Exception $ e ) {
265+ throw new Exception ("Invalid JWT token " , 409 , $ e );
260266 }
261267
262268 $ sub = $ jwt ->claims ()->get ("sub " );
263269 if ($ sub === null ) {
264- throw new \ Exception ('Invalid token: Missing "SUB ' );
270+ throw new Exception ('Missing "SUB" ' );
265271 }
266272 return $ sub ;
267273 }
0 commit comments