Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Controllers/PushedAuthorizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,7 @@ protected function resolveParametersToPersist(
if ($requestObjectResult !== null) {
// Request Object (JAR) was used. Per RFC 9126, all authorization request parameters must appear
// as claims of the Request Object, so only use its (validated) payload.
/** @psalm-suppress MixedAssignment */
$parameters = $resultBag->getOrFail(RequestObjectRule::class)->getValue();
$parameters = is_array($parameters) ? $parameters : [];

/** @psalm-suppress MixedAssignment */
$clientIdClaim = $parameters[ParamsEnum::ClientId->value] ?? null;
Expand Down
6 changes: 0 additions & 6 deletions src/Server/AuthorizationServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,8 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): O
);

// state and redirectUri is used here, so we can return HTTP redirect error in case of invalid response_type.
/** @var ?string $state */
$state = $resultBag->getOrFail(StateRule::class)->getValue();
/** @var string $redirectUri */
$redirectUri = $resultBag->getOrFail(ClientRedirectUriRule::class)->getValue();
/** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
$responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();

foreach ($this->enabledGrantTypes as $grantType) {
Expand Down Expand Up @@ -192,11 +189,8 @@ public function validateLogoutRequest(ServerRequestInterface $request): LogoutRe
throw new BadRequest($reason);
}

/** @var \SimpleSAML\OpenID\Core\IdToken|null $idTokenHint */
$idTokenHint = $resultBag->getOrFail(IdTokenHintRule::class)->getValue();
/** @var string|null $postLogoutRedirectUri */
$postLogoutRedirectUri = $resultBag->getOrFail(PostLogoutRedirectUriRule::class)->getValue();
/** @var string|null $state */
$state = $resultBag->getOrFail(StateRule::class)->getValue();
/** @var string|null $uiLocales */
$uiLocales = $resultBag->getOrFail(UiLocalesRule::class)->getValue();
Expand Down
16 changes: 1 addition & 15 deletions src/Server/Grants/AuthCodeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -525,12 +525,10 @@ public function respondToAccessTokenRequest(
// it is predefined as the ClientRule result and authenticated against by ClientAuthenticationRule above.
$client = $authorizationClientEntity;

/** @var ?ResolvedClientAuthenticationMethod $resolvedClientAuthenticationMethod */
$resolvedClientAuthenticationMethod = $authorizationClientEntity->isGeneric() ?
null :
$resultBag->getOrFail(ClientAuthenticationRule::class)->getValue();

/** @var ?string $codeVerifier */
$codeVerifier = $resultBag->getOrFail(CodeVerifierRule::class)->getValue();

$utilizedClientAuthenticationParams = [];
Expand Down Expand Up @@ -777,13 +775,9 @@ public function validateAuthorizationRequestWithRequestRules(
// Since we have already validated redirect_uri, and we have state, make it available for other checkers.
$this->requestRulesManager->predefineResultBag($resultBag);

/** @var string $redirectUri */
$redirectUri = $resultBag->getOrFail(ClientRedirectUriRule::class)->getValue();
/** @var string|null $state */
$state = $resultBag->getOrFail(StateRule::class)->getValue();
/** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */
$client = $resultBag->getOrFail(ClientRule::class)->getValue();
/** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
$responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();

$this->loggerService->debug('AuthCodeGrant: Resolved data:', [
Expand All @@ -805,7 +799,6 @@ public function validateAuthorizationRequestWithRequestRules(

$this->loggerService->debug('AuthCodeGrant: executed rules.', ['rulesToExecute' => $rulesToExecute]);

/** @var \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes */
$scopes = $resultBag->getOrFail(ScopeRule::class)->getValue();

$this->loggerService->debug('AuthCodeGrant: Resolved scopes: ', ['scopes' => $scopes]);
Expand All @@ -821,13 +814,11 @@ public function validateAuthorizationRequestWithRequestRules(
$oAuth2AuthorizationRequest->setState($state);
}

/** @var ?string $codeChallenge */
$codeChallenge = $resultBag->getOrFail(CodeChallengeRule::class)->getValue();
if ($codeChallenge) {
$this->loggerService->debug('AuthCodeGrant: Code challenge: ', [
'codeChallenge' => $codeChallenge,
]);
/** @var string $codeChallengeMethod */
$codeChallengeMethod = $resultBag->getOrFail(CodeChallengeMethodRule::class)->getValue();

$oAuth2AuthorizationRequest->setCodeChallenge($codeChallenge);
Expand Down Expand Up @@ -879,7 +870,7 @@ public function validateAuthorizationRequestWithRequestRules(
$maxAge = $resultBag->get(MaxAgeRule::class);
$this->loggerService->debug('AuthCodeGrant: MaxAge: ', ['maxAge' => $maxAge]);
if (null !== $maxAge) {
$authorizationRequest->setAuthTime((int) $maxAge->getValue());
$authorizationRequest->setAuthTime($maxAge->getValue());
}

$requestClaims = $resultBag->get(RequestedClaimsRule::class);
Expand All @@ -892,7 +883,6 @@ public function validateAuthorizationRequestWithRequestRules(
}
}

/** @var array|null $acrValues */
$acrValues = $resultBag->getOrFail(AcrValuesRule::class)->getValue();
$this->loggerService->debug('AuthCodeGrant: ACR values: ', ['acrValues' => $acrValues]);
$authorizationRequest->setRequestedAcrValues($acrValues);
Expand All @@ -904,20 +894,17 @@ public function validateAuthorizationRequestWithRequestRules(
$this->loggerService->debug('AuthCodeGrant: FlowType: ', ['flowType' => $flowType]);
$authorizationRequest->setFlowType($flowType);

/** @var ?string $issuerState */
$issuerState = $resultBag->get(IssuerStateRule::class)?->getValue();
$this->loggerService->debug('AuthCodeGrant: Issuer state: ', ['issuerState' => $issuerState]);
$authorizationRequest->setIssuerState($issuerState);

/** @var ?array $authorizationDetails */
$authorizationDetails = $resultBag->get(AuthorizationDetailsRule::class)?->getValue();
$this->loggerService->debug(
'AuthCodeGrant: Authorization details: ',
['authorizationDetails' => $authorizationDetails],
);
$authorizationRequest->setAuthorizationDetails($authorizationDetails);

/** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
$responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();
$this->loggerService->debug(
'AuthCodeGrant: Response mode: ',
Expand Down Expand Up @@ -955,7 +942,6 @@ public function validateAuthorizationRequestWithRequestRules(
['genericClientId' => $client->getIdentifier()],
);
// The generic client was used. Make sure to store actually used client_id and redirect_uri params.
/** @var string $clientIdParam */
$clientIdParam = $resultBag->getOrFail(ClientIdRule::class)->getValue();
$this->loggerService->debug(
'AuthCodeGrant: Binding client_id param to request: ',
Expand Down
13 changes: 2 additions & 11 deletions src/Server/Grants/ImplicitGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,9 @@ public function validateAuthorizationRequestWithRequestRules(

$this->requestRulesManager->predefineResultBag($resultBag);

/** @var string $redirectUri */
$redirectUri = $resultBag->getOrFail(ClientRedirectUriRule::class)->getValue();
/** @var string|null $state */
$state = $resultBag->getOrFail(StateRule::class)->getValue();
/** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */
$client = $resultBag->getOrFail(ClientRule::class)->getValue();
/** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
$responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();

// Some rules need certain things available in order to work properly...
Expand All @@ -157,7 +153,6 @@ public function validateAuthorizationRequestWithRequestRules(
$this->allowedAuthorizationHttpMethods,
);

/** @var \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes */
$scopes = $resultBag->getOrFail(ScopeRule::class)->getValue();

$authorizationRequest = new AuthorizationRequest();
Expand All @@ -170,11 +165,11 @@ public function validateAuthorizationRequestWithRequestRules(
}

// nonce existence is validated using a rule, so we can get it from there.
$authorizationRequest->setNonce((string)$resultBag->getOrFail(RequiredNonceRule::class)->getValue());
$authorizationRequest->setNonce($resultBag->getOrFail(RequiredNonceRule::class)->getValue());

$maxAge = $resultBag->get(MaxAgeRule::class);
if (null !== $maxAge) {
$authorizationRequest->setAuthTime((int) $maxAge->getValue());
$authorizationRequest->setAuthTime($maxAge->getValue());
}

$requestClaims = $resultBag->get(RequestedClaimsRule::class);
Expand All @@ -185,19 +180,15 @@ public function validateAuthorizationRequestWithRequestRules(
$authorizationRequest->setClaims($requestClaimValues);
}
}
/** @var bool $addClaimsToIdToken */
$addClaimsToIdToken = ($resultBag->getOrFail(AddClaimsToIdTokenRule::class))->getValue();
$authorizationRequest->setAddClaimsToIdToken($addClaimsToIdToken);

/** @var string $responseType */
$responseType = ($resultBag->getOrFail(ResponseTypeRule::class))->getValue();
$authorizationRequest->setResponseType($responseType);

/** @var array|null $acrValues */
$acrValues = $resultBag->getOrFail(AcrValuesRule::class)->getValue();
$authorizationRequest->setRequestedAcrValues($acrValues);

/** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
$responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();
$authorizationRequest->setResponseMode($responseMode);

Expand Down
1 change: 0 additions & 1 deletion src/Server/Grants/PreAuthCodeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ public function respondToAccessTokenRequest(
$this->allowedTokenHttpMethods,
);

/** @var ?array $authorizationDetails */
$authorizationDetails = $resultBag->get(AuthorizationDetailsRule::class)?->getValue();

// Issue and persist new access token
Expand Down
13 changes: 11 additions & 2 deletions src/Server/RequestRules/Interfaces/RequestRuleInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
namespace SimpleSAML\Module\oidc\Server\RequestRules\Interfaces;

use Psr\Http\Message\ServerRequestInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Result;
use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;

/**
* The generic parameter T describes the type of value the rule yields into the result bag. It is
* bound by each concrete rule (via `@extends AbstractRule<...>`) and consumed by the ResultBag,
* which uses it to infer the value type when a result is fetched by its rule class-string.
*
* @template-covariant T
*/
interface RequestRuleInterface
{
/**
Expand All @@ -27,7 +35,8 @@ public function getKey(): string;
* @param ResponseModeInterface $responseMode Response mode to use for error responses
* @param HttpMethodsEnum[] $allowedServerRequestMethods Indicate allowed HTTP methods used for request
*
* @return \SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface|null Result of the specific check
* @return \SimpleSAML\Module\oidc\Server\RequestRules\Result<mixed>|null Result of the specific check
* (the concrete value type T is bound per rule and surfaced via the ResultBag accessors)
*
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException If check fails
*/
Expand All @@ -38,5 +47,5 @@ public function checkRule(
array $data = [],
ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface;
): ?Result;
}
39 changes: 34 additions & 5 deletions src/Server/RequestRules/Interfaces/ResultBagInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,56 @@

namespace SimpleSAML\Module\oidc\Server\RequestRules\Interfaces;

use SimpleSAML\Module\oidc\Server\RequestRules\Result;

interface ResultBagInterface
{
/**
* Add result to the result bag.
*
* @param \SimpleSAML\Module\oidc\Server\RequestRules\Result<mixed> $result
*/
public function add(ResultInterface $result): void;
public function add(Result $result): void;

/**
* Get specific result or null if it doesn't exist.
*
* The value type is inferred from the rule class-string passed as the key.
*
* @template T
* @param class-string<RequestRuleInterface<T>> $key
* @return \SimpleSAML\Module\oidc\Server\RequestRules\Result<T>|null
*/
public function get(string $key): ?Result;

/**
* Get specific result or fail if it doesn't exist.
*
* The value type is inferred from the rule class-string passed as the key.
*
* @template T
* @param class-string<RequestRuleInterface<T>> $key
* @return \SimpleSAML\Module\oidc\Server\RequestRules\Result<T>
* @throws \Throwable If result with specific key is not present.
*/
public function get(string $key): ?ResultInterface;
public function getOrFail(string $key): Result;

/**
* Get specific result or fail if it doesn't exits.
* Get the value of a specific result or fail if the result doesn't exist.
*
* Convenience accessor that skips the intermediate Result object. The value type is inferred
* from the rule class-string passed as the key.
*
* @template T
* @param class-string<RequestRuleInterface<T>> $key
* @return T
* @throws \Throwable If result with specific key is not present.
*/
public function getOrFail(string $key): ResultInterface;
public function getValueOrFail(string $key): mixed;

/**
* Get all results.
* @return \SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface[]
* @return array<string, \SimpleSAML\Module\oidc\Server\RequestRules\Result<mixed>>
*/
public function getAll(): array;

Expand Down
25 changes: 0 additions & 25 deletions src/Server/RequestRules/Interfaces/ResultInterface.php

This file was deleted.

3 changes: 1 addition & 2 deletions src/Server/RequestRules/RequestRulesManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Psr\Http\Message\ServerRequestInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\RequestRuleInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface;
use SimpleSAML\Module\oidc\Services\LoggerService;
Expand Down Expand Up @@ -84,7 +83,7 @@ public function check(
/**
* Predefine (add) the existing result, so it can be used by other checkers during check.
*/
public function predefineResult(ResultInterface $result): void
public function predefineResult(Result $result): void
{
$this->resultBag->add($result);
}
Expand Down
24 changes: 14 additions & 10 deletions src/Server/RequestRules/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@

namespace SimpleSAML\Module\oidc\Server\RequestRules;

use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;

class Result implements ResultInterface
/**
* Result of a single request rule check.
*
* The generic parameter T describes the type of the contained value. Each rule binds it via
* its `@extends AbstractRule<...>` annotation, which in turn lets the ResultBag infer the value
* type when a result is fetched by its rule class-string.
*
* @template-covariant T
*/
class Result
{
/**
* Result constructor.
* @param mixed|null $value
* @param T $value
*/
public function __construct(protected string $key, protected mixed $value = null)
{
Expand All @@ -21,13 +27,11 @@ public function getKey(): string
return $this->key;
}

/**
* @return T
*/
public function getValue(): mixed
{
return $this->value;
}

public function setValue(mixed $value): void
{
$this->value = $value;
}
}
Loading
Loading