2626use SimpleSAML \Module \oidc \Server \ResponseModes \QueryResponseMode ;
2727use SimpleSAML \Module \oidc \Server \ResponseModes \ResponseModeInterface ;
2828use SimpleSAML \Module \oidc \Services \LoggerService ;
29- use SimpleSAML \Module \oidc \Utils \JwksResolver ;
3029use SimpleSAML \Module \oidc \Utils \RequestParamsResolver ;
3130use SimpleSAML \OpenID \Codebooks \HttpMethodsEnum ;
3231use SimpleSAML \OpenID \Codebooks \ParamsEnum ;
33- use SimpleSAML \OpenID \Core \RequestObject as ConnectRequestObject ;
34- use SimpleSAML \OpenID \Jar \RequestObject as JarRequestObject ;
3532
3633/**
37- * Handle the request_uri authorization request parameter:
38- * - Pushed Authorization Request URIs (RFC 9126, urn form): validate existence, expiration, one-time use
39- * (consume on validation) and client binding,
40- * - https Request URIs (RFC 9101 / OpenID Connect Core, Request Object by reference): validate that the
41- * Request URI is registered for the client, and validate the fetched Request Object (signature, client
42- * binding), differentiating between OpenID Connect and plain OAuth 2.0 (JAR) requests,
43- * - enforce Pushed Authorization Request usage if required by server or client policy.
44- *
45- * Note that the actual resolution of params from the request_uri value is done in RequestParamsResolver, so
46- * that resolved params are transparently available to all other rules .
34+ * Gatekeeper for the request_uri authorization request parameter. It does not parse, fetch or verify the
35+ * Request Object itself (that is the job of the RequestParamsResolver and the RequestObjectRule); it only
36+ * enforces request_uri usage policy:
37+ * - request and request_uri must not be used together (RFC 9101),
38+ * - client_id is required when using request_uri,
39+ * - Pushed Authorization Request URIs (RFC 9126, urn form): existence, expiration, one-time use (consume on
40+ * validation) and client binding,
41+ * - https Request URIs (Request Object by reference): the OP must support the request_uri parameter, and the
42+ * Request Object must be resolvable (registration / federation policy is enforced in RequestParamsResolver),
43+ * - Pushed Authorization Request usage if required by server or client policy .
4744 *
4845 * @see \SimpleSAML\Module\oidc\Utils\RequestParamsResolver
46+ * @see \SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule
4947 */
5048class RequestUriRule extends AbstractRule
5149{
5250 public function __construct (
5351 RequestParamsResolver $ requestParamsResolver ,
5452 Helpers $ helpers ,
5553 protected PushedAuthorizationRequestRepository $ pushedAuthorizationRequestRepository ,
56- protected JwksResolver $ jwksResolver ,
5754 protected ModuleConfig $ moduleConfig ,
5855 ) {
5956 parent ::__construct ($ requestParamsResolver , $ helpers );
@@ -76,9 +73,8 @@ public function checkRule(
7673 ): ?ResultInterface {
7774 $ loggerService ->debug ('RequestUriRule::checkRule ' );
7875
79- // Note: we are intentionally working with raw request params here
80- // (not the merged view which includes params resolved from the
81- // request_uri itself).
76+ // Note: we are intentionally working with raw request params here (not the merged view which includes
77+ // params resolved from the request_uri itself).
8278 $ requestUri = $ this ->requestParamsResolver ->getFromRequestBasedOnAllowedMethods (
8379 ParamsEnum::RequestUri->value ,
8480 $ request ,
@@ -140,9 +136,7 @@ public function checkRule(
140136 if (str_starts_with (strtolower ($ requestUri ), 'https:// ' )) {
141137 return $ this ->checkHttpsRequestUri (
142138 $ requestUri ,
143- $ client ,
144139 $ request ,
145- $ currentResultBag ,
146140 $ isParRequired ,
147141 $ allowedServerRequestMethods ,
148142 );
@@ -224,9 +218,7 @@ protected function checkPushedAuthorizationRequestUri(
224218 */
225219 protected function checkHttpsRequestUri (
226220 string $ requestUri ,
227- ClientEntityInterface $ client ,
228221 ServerRequestInterface $ request ,
229- ResultBagInterface $ currentResultBag ,
230222 bool $ isParRequired ,
231223 array $ allowedServerRequestMethods ,
232224 ): ResultInterface {
@@ -237,98 +229,26 @@ protected function checkHttpsRequestUri(
237229 );
238230 }
239231
240- if (!in_array ( $ requestUri , $ client -> getRequestUris (), true )) {
232+ if (!$ this -> moduleConfig -> getRequestUriParameterSupported ( )) {
241233 throw OidcServerException::invalidRequest (
242234 ParamsEnum::RequestUri->value ,
243- 'The request_uri is not registered for this client . ' ,
235+ 'Passing the request object by reference (request_uri) is not supported . ' ,
244236 );
245237 }
246238
247- // Make sure the request_uri resolution ran (it is memoized in
248- // RequestParamsResolver, so this is inexpensive if other rules already
249- // triggered it), then grab the resolved Request Object Bag.
250- $ this ->requestParamsResolver ->getAllBasedOnAllowedMethods ($ request , $ allowedServerRequestMethods );
251-
252- $ requestObjectBag = $ this ->requestParamsResolver ->getResolvedRequestUriBag ($ requestUri );
239+ // Make sure the Request Object behind the request_uri can actually be resolved (fetched and parsed,
240+ // and allowed by registration / federation policy in RequestParamsResolver). The signature and other
241+ // request object validations are then done by the RequestObjectRule (or by ClientRule for the
242+ // federation case).
243+ $ requestObjectBag = $ this ->requestParamsResolver ->getRequestObjectBag ($ request , $ allowedServerRequestMethods );
253244 if ($ requestObjectBag === null ) {
254245 throw OidcServerException::invalidRequest (
255246 ParamsEnum::RequestUri->value ,
256- 'Could not fetch or parse the Request Object from request_uri. ' ,
257- );
258- }
259-
260- if (!$ this ->isOidcAuthorizationRequest ($ request , $ allowedServerRequestMethods )) {
261- // This is a plain OAuth 2.0 authorization request, so JAR
262- // (RFC 9101) rules apply: the Request Object must be a signed
263- // JWT containing the Client ID claim.
264- $ requestObject = $ requestObjectBag ->get (JarRequestObject::class);
265- if (!$ requestObject instanceof JarRequestObject) {
266- throw OidcServerException::invalidRequest (
267- ParamsEnum::RequestUri->value ,
268- 'Request object is not a valid JAR Request Object (note that it must be signed). ' ,
269- );
270- }
271-
272- $ this ->verifySignature ($ requestObject , $ client );
273- } else {
274- // This is an OpenID Connect authorization request, so OpenID Connect Core rules apply: the
275- // Request Object can be unsigned (unless signature is required by policy).
276- $ requestObject = $ requestObjectBag ->get (ConnectRequestObject::class);
277- if (!$ requestObject instanceof ConnectRequestObject) {
278- throw OidcServerException::invalidRequest (
279- ParamsEnum::RequestUri->value ,
280- 'Request object is not a valid Request Object. ' ,
281- );
282- }
283-
284- if ($ requestObject ->isProtected ()) {
285- $ this ->verifySignature ($ requestObject , $ client );
286- } elseif (
287- $ this ->moduleConfig ->getRequireSignedRequestObject () ||
288- $ client ->getRequireSignedRequestObject ()
289- ) {
290- throw OidcServerException::invalidRequest (
291- ParamsEnum::RequestUri->value ,
292- 'Request object must be signed (alg: none is not allowed). ' ,
293- );
294- }
295- }
296-
297- $ payload = $ requestObject ->getPayload ();
298-
299- /** @psalm-suppress MixedAssignment */
300- $ clientIdClaim = $ payload [ParamsEnum::ClientId->value ] ?? null ;
301- if ($ clientIdClaim !== $ client ->getIdentifier ()) {
302- throw OidcServerException::invalidRequest (
303- ParamsEnum::RequestUri->value ,
304- 'Client ID claim in request object does not match the client_id parameter. ' ,
247+ 'The request_uri could not be resolved (it may not be allowed for this client, or the fetch ' .
248+ 'failed). ' ,
305249 );
306250 }
307251
308- // Mark the Request Object as resolved (and validated), so that RequestObjectRule does not need to
309- // run again for it.
310- $ currentResultBag ->add (new Result (RequestObjectRule::class, $ payload ));
311-
312252 return new Result ($ this ->getKey (), $ requestUri );
313253 }
314-
315- /**
316- * @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
317- */
318- protected function verifySignature (
319- ConnectRequestObject |JarRequestObject $ requestObject ,
320- ClientEntityInterface $ client ,
321- ): void {
322- ($ jwks = $ this ->jwksResolver ->forClient ($ client )) || throw OidcServerException::accessDenied (
323- 'can not validate request object, client JWKS not available ' ,
324- );
325-
326- try {
327- $ requestObject ->verifyWithKeySet ($ jwks );
328- } catch (\Throwable $ exception ) {
329- throw OidcServerException::accessDenied (
330- 'request object validation failed: ' . $ exception ->getMessage (),
331- );
332- }
333- }
334254}
0 commit comments