diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 79156c9e..9e6c6c1f 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -255,10 +255,16 @@ jobs:
./conformance-suite/scripts/run-test-plan.py --expected-failures-file ./main/conformance-tests/implicit-warnings.json --expected-skips-file ./main/conformance-tests/implicit-skips.json "oidcc-implicit-certification-test-plan[server_metadata=discovery][client_registration=static_client]" ./main/conformance-tests/conformance-implicit-ci.json
- name: Run RP logout
run: |
- ./conformance-suite/scripts/run-test-plan.py "oidcc-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" ./main/conformance-tests/conformance-rp-initiated-logout-ci.json
+ ./conformance-suite/scripts/run-test-plan.py "oidcc-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" ./main/conformance-tests/conformance-rp-initiated-logout-ci.json
- name: Run RP backchannel
run: |
- ./conformance-suite/scripts/run-test-plan.py "oidcc-backchannel-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" ./main/conformance-tests/conformance-back-channel-logout-ci.json
+ ./conformance-suite/scripts/run-test-plan.py "oidcc-backchannel-rp-initiated-logout-certification-test-plan[response_type=code][client_registration=static_client]" ./main/conformance-tests/conformance-back-channel-logout-ci.json
+ - name: Run form_post basic tests
+ run: |
+ ./conformance-suite/scripts/run-test-plan.py "oidcc-formpost-basic-certification-test-plan[server_metadata=discovery][client_registration=static_client]" ./main/conformance-tests/conformance-basic-ci.json
+ - name: Run form_post implicit tests
+ run: |
+ ./conformance-suite/scripts/run-test-plan.py "oidcc-formpost-implicit-certification-test-plan[server_metadata=discovery][client_registration=static_client]" ./main/conformance-tests/conformance-implicit-ci.json
- name: Stop SSP
working-directory: ./main
run: |
diff --git a/composer.json b/composer.json
index d73a5c28..91ed0ae0 100644
--- a/composer.json
+++ b/composer.json
@@ -31,7 +31,7 @@
"psr/container": "^2.0",
"psr/log": "^3",
"simplesamlphp/composer-module-installer": "^1.3",
- "simplesamlphp/openid": "~0.2.0",
+ "simplesamlphp/openid": "~0.2.3",
"spomky-labs/base64url": "^2.0",
"symfony/expression-language": "^7.4",
"symfony/psr-http-message-bridge": "^7.4",
diff --git a/locales/en/LC_MESSAGES/oidc.po b/locales/en/LC_MESSAGES/oidc.po
index 4d626774..7b1f343d 100644
--- a/locales/en/LC_MESSAGES/oidc.po
+++ b/locales/en/LC_MESSAGES/oidc.po
@@ -564,3 +564,15 @@ msgstr ""
msgid "enabled"
msgstr ""
+
+msgid "Submitting..."
+msgstr ""
+
+msgid "Please wait while we redirect you..."
+msgstr ""
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
diff --git a/locales/es/LC_MESSAGES/oidc.po b/locales/es/LC_MESSAGES/oidc.po
index 7e1a5690..4f0b2f6d 100644
--- a/locales/es/LC_MESSAGES/oidc.po
+++ b/locales/es/LC_MESSAGES/oidc.po
@@ -564,3 +564,15 @@ msgstr ""
msgid "enabled"
msgstr ""
+
+msgid "Submitting..."
+msgstr "Enviando..."
+
+msgid "Please wait while we redirect you..."
+msgstr "Por favor, espere mientras le redirigimos..."
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr "Si no es redirigido automáticamente, haga clic en el botón de abajo para continuar."
+
+msgid "Continue"
+msgstr "Continuar"
diff --git a/locales/fr/LC_MESSAGES/oidc.po b/locales/fr/LC_MESSAGES/oidc.po
index c7d98d9f..8cfd34d5 100644
--- a/locales/fr/LC_MESSAGES/oidc.po
+++ b/locales/fr/LC_MESSAGES/oidc.po
@@ -564,3 +564,16 @@ msgstr ""
msgid "enabled"
msgstr ""
+
+msgid "Submitting..."
+msgstr "Soumission en cours..."
+
+msgid "Please wait while we redirect you..."
+msgstr "Veuillez patienter pendant que nous vous redirigeons..."
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr "Si vous n'êtes pas redirigé automatiquement, cliquez sur le bouton ci-dessous pour continuer."
+
+msgid "Continue"
+msgstr "Continuer"
+
diff --git a/locales/hr/LC_MESSAGES/oidc.po b/locales/hr/LC_MESSAGES/oidc.po
index 7c306d1d..54f87369 100644
--- a/locales/hr/LC_MESSAGES/oidc.po
+++ b/locales/hr/LC_MESSAGES/oidc.po
@@ -612,3 +612,15 @@ msgstr "onemogućeno"
msgid "enabled"
msgstr "omogućeno"
+
+msgid "Submitting..."
+msgstr "Slanje u tijeku..."
+
+msgid "Please wait while we redirect you..."
+msgstr "Molimo pričekajte dok vas preusmjerimo..."
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr "Ako niste automatski preusmjereni, kliknite na gumb ispod za nastavak."
+
+msgid "Continue"
+msgstr "Nastavi"
diff --git a/locales/it/LC_MESSAGES/oidc.po b/locales/it/LC_MESSAGES/oidc.po
index 7151442e..f8e51dd8 100644
--- a/locales/it/LC_MESSAGES/oidc.po
+++ b/locales/it/LC_MESSAGES/oidc.po
@@ -564,3 +564,16 @@ msgstr ""
msgid "enabled"
msgstr ""
+
+msgid "Submitting..."
+msgstr "Invio in corso..."
+
+msgid "Please wait while we redirect you..."
+msgstr "Attendere prego durante il reindirizzamento..."
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr "Se non vieni reindirizzato automaticamente, fai clic sul pulsante qui sotto per continuare."
+
+msgid "Continue"
+msgstr "Continua"
+
diff --git a/locales/nl/LC_MESSAGES/oidc.po b/locales/nl/LC_MESSAGES/oidc.po
index 37378dc2..433f634b 100644
--- a/locales/nl/LC_MESSAGES/oidc.po
+++ b/locales/nl/LC_MESSAGES/oidc.po
@@ -518,3 +518,15 @@ msgstr "uitgeschakeld"
msgid "enabled"
msgstr "ingeschakeld"
+
+msgid "Submitting..."
+msgstr "Verzenden..."
+
+msgid "Please wait while we redirect you..."
+msgstr "Wacht alstublieft terwijl u wordt doorgestuurd..."
+
+msgid "If you are not redirected automatically, click the button below to continue."
+msgstr "Als u niet automatisch wordt doorgestuurd, klik dan op de onderstaande knop om door te gaan."
+
+msgid "Continue"
+msgstr "Doorgaan"
diff --git a/public/assets/js/src/formpost.js b/public/assets/js/src/formpost.js
new file mode 100644
index 00000000..26fe7f6c
--- /dev/null
+++ b/public/assets/js/src/formpost.js
@@ -0,0 +1 @@
+document.getElementById('formpost').submit();
\ No newline at end of file
diff --git a/routing/services/services.yml b/routing/services/services.yml
index 5b8f7c6c..ffcad634 100644
--- a/routing/services/services.yml
+++ b/routing/services/services.yml
@@ -67,6 +67,12 @@ services:
SimpleSAML\Module\oidc\Server\ResponseTypes\TokenResponse:
factory: ['@SimpleSAML\Module\oidc\Factories\TokenResponseFactory', 'build']
+ SimpleSAML\Module\oidc\Server\ResponseModes\:
+ resource: '../../src/Server/ResponseModes/*'
+
+ SimpleSAML\Configuration:
+ factory: ['SimpleSAML\Configuration', 'getInstance']
+
oidc.key.private:
class: League\OAuth2\Server\CryptKey
factory: ['@SimpleSAML\Module\oidc\Factories\CryptKeyFactory', 'buildPrivateKey']
diff --git a/src/Controllers/Admin/ClientController.php b/src/Controllers/Admin/ClientController.php
index 7f7ff7ef..3251e661 100644
--- a/src/Controllers/Admin/ClientController.php
+++ b/src/Controllers/Admin/ClientController.php
@@ -239,6 +239,7 @@ public function edit(Request $request): Response
$clientData = $originalClient->toArray();
$clientData['allowed_origin'] = $clientAllowedOrigins;
+ $clientData[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] = $originalClient->getAllowedResponseModes();
// Handle extra metadata
@@ -318,7 +319,6 @@ protected function buildClientEntityFromFormData(
?string $owner = null,
bool $isGeneric = false,
): ClientEntityInterface {
- /** @var array $data */
$data = $form->getValues('array');
if (
@@ -358,6 +358,10 @@ protected function buildClientEntityFromFormData(
ClaimsEnum::IdTokenSignedResponseAlg->value => $idTokenSignedResponseAlg,
];
+ $allowedResponseModes = is_array($data[ClientEntity::KEY_ALLOWED_RESPONSE_MODES]) ?
+ $data[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] : [];
+ $extraMetadata[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] = $allowedResponseModes;
+
return $this->clientEntityFactory->fromData(
$identifier,
$secret,
diff --git a/src/Entities/ClientEntity.php b/src/Entities/ClientEntity.php
index a0ffb92b..d6de1b6d 100644
--- a/src/Entities/ClientEntity.php
+++ b/src/Entities/ClientEntity.php
@@ -23,6 +23,7 @@
use SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface;
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
use SimpleSAML\OpenID\Codebooks\ClientRegistrationTypesEnum;
+use SimpleSAML\OpenID\Codebooks\ResponseModesEnum;
class ClientEntity implements ClientEntityInterface
{
@@ -54,6 +55,7 @@ class ClientEntity implements ClientEntityInterface
public const string KEY_EXPIRES_AT = 'expires_at';
public const string KEY_IS_GENERIC = 'is_generic';
public const string KEY_EXTRA_METADATA = 'extra_metadata';
+ public const string KEY_ALLOWED_RESPONSE_MODES = 'allowed_response_modes';
private string $secret;
@@ -388,4 +390,20 @@ public function getIdTokenSignedResponseAlg(): ?string
return $idTokenSignedResponseAlg;
}
+
+ public function getAllowedResponseModes(): array
+ {
+ /** @psalm-suppress MixedAssignment */
+ $allowedResponseModes = $this->extraMetadata[self::KEY_ALLOWED_RESPONSE_MODES] ?? null;
+
+ if (is_array($allowedResponseModes)) {
+ return $allowedResponseModes;
+ }
+
+ return [
+ ResponseModesEnum::Query->value,
+ ResponseModesEnum::Fragment->value,
+ ResponseModesEnum::FormPost->value,
+ ];
+ }
}
diff --git a/src/Entities/Interfaces/ClientEntityInterface.php b/src/Entities/Interfaces/ClientEntityInterface.php
index dea9ff66..47bc6f15 100644
--- a/src/Entities/Interfaces/ClientEntityInterface.php
+++ b/src/Entities/Interfaces/ClientEntityInterface.php
@@ -82,4 +82,5 @@ public function isGeneric(): bool;
public function getExtraMetadata(): array;
public function getIdTokenSignedResponseAlg(): ?string;
+ public function getAllowedResponseModes(): array;
}
diff --git a/src/Factories/Grant/ImplicitGrantFactory.php b/src/Factories/Grant/ImplicitGrantFactory.php
index a22791d8..47d5ec6b 100644
--- a/src/Factories/Grant/ImplicitGrantFactory.php
+++ b/src/Factories/Grant/ImplicitGrantFactory.php
@@ -43,7 +43,6 @@ public function build(): ImplicitGrant
$this->accessTokenRepository,
$this->requestRulesManager,
$this->requestParamsResolver,
- '#',
$this->accessTokenEntityFactory,
);
}
diff --git a/src/Factories/RequestRulesManagerFactory.php b/src/Factories/RequestRulesManagerFactory.php
index 9b77e7c0..952485d4 100644
--- a/src/Factories/RequestRulesManagerFactory.php
+++ b/src/Factories/RequestRulesManagerFactory.php
@@ -31,11 +31,15 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredNonceRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule;
+use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseModeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeOfflineAccessRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\UiLocalesRule;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FormPostResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FragmentResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Services\AuthenticationService;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\Module\oidc\Utils\AuthenticatedOAuth2ClientResolver;
@@ -72,6 +76,9 @@ public function __construct(
private readonly AuthenticatedOAuth2ClientResolver $authenticatedOAuth2ClientResolver,
private readonly ?FederationCache $federationCache = null,
private readonly ?ProtocolCache $protocolCache = null,
+ private readonly QueryResponseMode $queryResponseMode,
+ private readonly FragmentResponseMode $fragmentResponseMode,
+ private readonly FormPostResponseMode $formPostResponseMode,
) {
}
@@ -107,6 +114,14 @@ private function getDefaultRules(): array
),
new ClientRedirectUriRule($this->requestParamsResolver, $this->helpers, $this->moduleConfig),
new RequestObjectRule($this->requestParamsResolver, $this->helpers, $this->jwksResolver),
+ new ResponseModeRule(
+ $this->requestParamsResolver,
+ $this->helpers,
+ $this->moduleConfig,
+ $this->queryResponseMode,
+ $this->fragmentResponseMode,
+ $this->formPostResponseMode,
+ ),
new PromptRule(
$this->requestParamsResolver,
$this->helpers,
diff --git a/src/Forms/ClientForm.php b/src/Forms/ClientForm.php
index 694a206e..c76b18bc 100644
--- a/src/Forms/ClientForm.php
+++ b/src/Forms/ClientForm.php
@@ -19,6 +19,7 @@
use Nette\Forms\Form;
use SimpleSAML\Locale\Translate;
use SimpleSAML\Module\oidc\Bridges\SspBridge;
+use SimpleSAML\Module\oidc\Entities\ClientEntity;
use SimpleSAML\Module\oidc\Forms\Controls\CsrfProtection;
use SimpleSAML\Module\oidc\Helpers;
use SimpleSAML\Module\oidc\ModuleConfig;
@@ -76,7 +77,6 @@ public function __construct(
public function validateRedirectUri(Form $form): void
{
- /** @var array $values */
$values = $form->getValues(self::TYPE_ARRAY);
/** @var string[] $redirectUris */
$redirectUris = $values['redirect_uri'] ?? [];
@@ -89,7 +89,6 @@ public function validateRedirectUri(Form $form): void
public function validateAllowedOrigin(Form $form): void
{
- /** @var array $values */
$values = $form->getValues(self::TYPE_ARRAY);
/** @var string[] $allowedOrigins */
$allowedOrigins = $values['allowed_origin'] ?? [];
@@ -102,7 +101,6 @@ public function validateAllowedOrigin(Form $form): void
public function validatePostLogoutRedirectUri(Form $form): void
{
- /** @var array $values */
$values = $form->getValues(self::TYPE_ARRAY);
/** @var string[] $postLogoutRedirectUris */
$postLogoutRedirectUris = $values['post_logout_redirect_uri'] ?? [];
@@ -219,7 +217,6 @@ protected function validateByMatchingRegex(
public function getValues(string|object|bool|null $returnType = null, ?array $controls = null): array
{
- /** @var array $values */
$values = parent::getValues(self::TYPE_ARRAY);
// Sanitize redirect_uri and allowed_origin
@@ -284,59 +281,72 @@ public function getValues(string|object|bool|null $returnType = null, ?array $co
$values[ClaimsEnum::IdTokenSignedResponseAlg->value] = empty($idTokenSignedResponseAlg) ?
null : $idTokenSignedResponseAlg;
+ $responseModesAllowed = is_array($values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES]) ?
+ $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] : [];
+ $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] = array_intersect(
+ $responseModesAllowed,
+ array_keys($this->getAllowedResponseModesValues()),
+ );
+
return $values;
}
/**
* @throws \Exception
*/
- public function setDefaults(object|array $data, bool $erase = false): static
+ public function setDefaults(object|array $values, bool $erase = false): static
{
- if (!is_array($data)) {
- if ($data instanceof Traversable) {
- $data = iterator_to_array($data);
+ if (!is_array($values)) {
+ if ($values instanceof Traversable) {
+ $values = iterator_to_array($values);
} else {
- $data = (array) $data;
+ $values = (array) $values;
}
}
/** @var string[] $redirectUris */
- $redirectUris = is_array($data['redirect_uri']) ? $data['redirect_uri'] : [];
- $data['redirect_uri'] = implode("\n", $redirectUris);
+ $redirectUris = is_array($values['redirect_uri']) ? $values['redirect_uri'] : [];
+ $values['redirect_uri'] = implode("\n", $redirectUris);
// Allowed origins are only available for public clients (not for confidential clients).
- if (!$data['is_confidential'] && isset($data['allowed_origin'])) {
+ if (!$values['is_confidential'] && isset($values['allowed_origin'])) {
/** @var string[] $allowedOrigins */
- $allowedOrigins = is_array($data['allowed_origin']) ? $data['allowed_origin'] : [];
- $data['allowed_origin'] = implode("\n", $allowedOrigins);
+ $allowedOrigins = is_array($values['allowed_origin']) ? $values['allowed_origin'] : [];
+ $values['allowed_origin'] = implode("\n", $allowedOrigins);
} else {
- $data['allowed_origin'] = '';
+ $values['allowed_origin'] = '';
}
/** @var string[] $postLogoutRedirectUris */
- $postLogoutRedirectUris = is_array($data['post_logout_redirect_uri']) ? $data['post_logout_redirect_uri'] : [];
- $data['post_logout_redirect_uri'] = implode("\n", $postLogoutRedirectUris);
+ $postLogoutRedirectUris = is_array($values['post_logout_redirect_uri']) ?
+ $values['post_logout_redirect_uri'] : [];
+ $values['post_logout_redirect_uri'] = implode("\n", $postLogoutRedirectUris);
- $scopes = is_array($data['scopes']) ? $data['scopes'] : [];
- $data['scopes'] = array_intersect($scopes, array_keys($this->getScopes()));
+ $scopes = is_array($values['scopes']) ? $values['scopes'] : [];
+ $values['scopes'] = array_intersect($scopes, array_keys($this->getScopes()));
- $data['client_registration_types'] = is_array($data['client_registration_types']) ?
- array_intersect($data['client_registration_types'], $this->getClientRegistrationTypes()) :
+ $values['client_registration_types'] = is_array($values['client_registration_types']) ?
+ array_intersect($values['client_registration_types'], $this->getClientRegistrationTypes()) :
[ClientRegistrationTypesEnum::Automatic->value];
- $data['federation_jwks'] = is_array($data['federation_jwks']) ? json_encode($data['federation_jwks']) : null;
+ $values['federation_jwks'] = is_array($values['federation_jwks']) ?
+ json_encode($values['federation_jwks']) : null;
- $data['jwks'] = is_array($data['jwks']) ? json_encode($data['jwks']) : null;
+ $values['jwks'] = is_array($values['jwks']) ? json_encode($values['jwks']) : null;
if (
- $data['auth_source'] !== null &&
- (!in_array($data['auth_source'], $this->sspBridge->auth()->source()->getSources()))
+ $values['auth_source'] !== null &&
+ (!in_array($values['auth_source'], $this->sspBridge->auth()->source()->getSources()))
) {
// Possible auth source name change without prior update in clients, resetting.
- $data['auth_source'] = null;
+ $values['auth_source'] = null;
}
- parent::setDefaults($data, $erase);
+ $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] = is_array(
+ $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES],
+ ) ? $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] : [];
+
+ parent::setDefaults($values, $erase);
return $this;
}
@@ -354,6 +364,7 @@ protected function buildForm(): void
$this->onValidate[] = $this->validateBackChannelLogoutUri(...);
$this->onValidate[] = $this->validateEntityIdentifier(...);
$this->onValidate[] = $this->validateClientRegistrationTypes(...);
+ $this->onValidate[] = $this->validateResponseModes(...);
$this->onValidate[] = $this->validateFederationJwks(...);
$this->onValidate[] = $this->validateProtocolJwks(...);
$this->onValidate[] = $this->validateJwksUri(...);
@@ -423,6 +434,43 @@ protected function buildForm(): void
->setHtmlAttribute('class', 'full-width')
->setItems(['RS256'], false)
->setPrompt(Translate::noop('-'));
+
+ $this->addMultiSelect(
+ ClientEntity::KEY_ALLOWED_RESPONSE_MODES,
+ Translate::noop('Allowed Response Modes'),
+ $this->getAllowedResponseModesValues(),
+ 3,
+ )->setHtmlAttribute('class', 'full-width')
+ ->setRequired(Translate::noop('At least one response mode is required.'));
+ }
+
+ /**
+ * Validate provided response modes
+ *
+ * @throws \Exception
+ */
+ public function validateResponseModes(Form $form): void
+ {
+ $values = $form->getValues(self::TYPE_ARRAY);
+ /** @var string[]|null $responseModes */
+ $responseModes = $values[ClientEntity::KEY_ALLOWED_RESPONSE_MODES] ?? null;
+ if (is_array($responseModes)) {
+ $allowed = array_keys($this->getAllowedResponseModesValues());
+ foreach ($responseModes as $mode) {
+ if (!in_array($mode, $allowed, true)) {
+ $this->addError("Invalid value: $mode");
+ }
+ }
+ }
+ }
+
+ /**
+ * @return string[] map of value => label
+ */
+ protected function getAllowedResponseModesValues(): array
+ {
+ $supported = $this->moduleConfig->getSupportedResponseModes();
+ return array_combine($supported, $supported);
}
/**
diff --git a/src/ModuleConfig.php b/src/ModuleConfig.php
index 7907bb07..947492c0 100644
--- a/src/ModuleConfig.php
+++ b/src/ModuleConfig.php
@@ -24,6 +24,7 @@
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmBag;
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
+use SimpleSAML\OpenID\Codebooks\ResponseModesEnum;
use SimpleSAML\OpenID\Codebooks\ScopesEnum;
use SimpleSAML\OpenID\Codebooks\TrustMarkStatusEndpointUsagePolicyEnum;
use SimpleSAML\OpenID\Serializers\JwsSerializerBag;
@@ -365,6 +366,18 @@ public function getSupportedSerializers(): SupportedSerializers
);
}
+ /**
+ * @return string[]
+ */
+ public function getSupportedResponseModes(): array
+ {
+ return [
+ ResponseModesEnum::Query->value,
+ ResponseModesEnum::Fragment->value,
+ ResponseModesEnum::FormPost->value,
+ ];
+ }
+
/**
* @throws ConfigurationError
* @return non-empty-array
diff --git a/src/Server/AuthorizationServer.php b/src/Server/AuthorizationServer.php
index f0cc0585..56e1d7c4 100644
--- a/src/Server/AuthorizationServer.php
+++ b/src/Server/AuthorizationServer.php
@@ -22,9 +22,11 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\IdTokenHintRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\PostLogoutRedirectUriRule;
+use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseModeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\UiLocalesRule;
use SimpleSAML\Module\oidc\Server\RequestTypes\LogoutRequest;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -85,13 +87,14 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): O
StateRule::class,
ClientRule::class,
ClientRedirectUriRule::class,
+ ResponseModeRule::class,
];
try {
$resultBag = $this->requestRulesManager->check(
$request,
$rulesToExecute,
- false,
+ new QueryResponseMode(),
[HttpMethodsEnum::GET, HttpMethodsEnum::POST],
);
} catch (OidcServerException $exception) {
@@ -114,6 +117,8 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): O
$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) {
$this->loggerService?->debug(
@@ -157,7 +162,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): O
'request.',
['requestQueryParams' => $request->getQueryParams()],
);
- throw OidcServerException::unsupportedResponseType($redirectUri, $state);
+ throw OidcServerException::unsupportedResponseType($redirectUri, $state, $responseMode);
}
/**
@@ -177,7 +182,7 @@ public function validateLogoutRequest(ServerRequestInterface $request): LogoutRe
$resultBag = $this->requestRulesManager->check(
$request,
$rulesToExecute,
- false,
+ new QueryResponseMode(),
[HttpMethodsEnum::GET, HttpMethodsEnum::POST],
);
} catch (OidcServerException $exception) {
diff --git a/src/Server/Exceptions/OidcServerException.php b/src/Server/Exceptions/OidcServerException.php
index 5a9be60d..42483165 100644
--- a/src/Server/Exceptions/OidcServerException.php
+++ b/src/Server/Exceptions/OidcServerException.php
@@ -6,10 +6,12 @@
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ResponseInterface;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FragmentResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface;
use SimpleSAML\OpenID\Codebooks\ErrorsEnum;
use Throwable;
-use function http_build_query;
use function json_encode;
class OidcServerException extends OAuthServerException
@@ -37,9 +39,9 @@ class OidcServerException extends OAuthServerException
protected ?string $redirectUri;
/**
- * @var bool
+ * @var null|ResponseModeInterface
*/
- protected bool $useFragmentInHttpResponses = false;
+ protected ?ResponseModeInterface $responseMode = null;
/**
* Throw a new exception.
@@ -62,12 +64,14 @@ public function __construct(
?string $redirectUri = null,
?Throwable $previous = null,
?string $state = null,
+ ?ResponseModeInterface $responseMode = null,
) {
parent::__construct($message, $code, $errorType, $httpStatusCode, $hint, $redirectUri, $previous);
$this->httpStatusCode = $httpStatusCode;
$this->errorType = $errorType;
$this->redirectUri = $redirectUri;
+ $this->responseMode = $responseMode;
if ($hint !== null) {
$message .= ' (' . $hint . ')';
@@ -90,20 +94,28 @@ public function __construct(
*
* @param string|null $redirectUri
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
- * @return self
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
+ * @return OidcServerException
*/
public static function unsupportedResponseType(
?string $redirectUri = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
$errorMessage = 'The response type is not supported by the authorization server.';
$hint = 'Check that all required parameters have been provided';
- $e = new self($errorMessage, 2, 'unsupported_response_type', 400, $hint, $redirectUri, null, $state);
- $e->useFragmentInHttpResponses($useFragment);
- return $e;
+ return new self(
+ $errorMessage,
+ 2,
+ 'unsupported_response_type',
+ 400,
+ $hint,
+ $redirectUri,
+ null,
+ $state,
+ $responseMode,
+ );
}
/**
@@ -112,19 +124,36 @@ public static function unsupportedResponseType(
* @param string $scope The bad scope
* @param string|null $redirectUri An HTTP URI to redirect the user back to
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
- * @return static
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
+ * @return OidcServerException
+ * @psalm-suppress LessSpecificImplementedReturnType
*/
public static function invalidScope(
$scope,
$redirectUri = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
- // OAuthServerException correctly implements this error, however, it misses state parameter.
- $e = parent::invalidScope($scope, $redirectUri);
- $e->setState($state);
- $e->useFragmentInHttpResponses($useFragment);
+ if (empty($scope)) {
+ $hint = 'Specify a scope in the request or set a default scope';
+ } else {
+ $hint = sprintf(
+ 'Check the `%s` scope',
+ htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false),
+ );
+ }
+
+ $e = new self(
+ 'The requested scope is invalid, unknown, or malformed',
+ 5,
+ 'invalid_scope',
+ 400,
+ $hint,
+ $redirectUri,
+ null,
+ $state,
+ $responseMode,
+ );
return $e;
}
@@ -137,8 +166,9 @@ public static function invalidScope(
* @param \Throwable|null $previous
* @param string|null $redirectUri
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
- * @return static
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
+ * @return OidcServerException
+ * @psalm-suppress LessSpecificImplementedReturnType
*/
public static function invalidRequest(
$parameter,
@@ -146,13 +176,12 @@ public static function invalidRequest(
?Throwable $previous = null,
?string $redirectUri = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
- $e = parent::invalidRequest($parameter, $hint, $previous);
- // OAuthServerException misses the ability to set redirectUri for invalid requests, as well as state.
- $e->setRedirectUri($redirectUri);
- $e->setState($state);
- $e->useFragmentInHttpResponses($useFragment);
+ $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
+ 'includes a parameter more than once, or is otherwise malformed.';
+ $hint = ($hint === null) ? \sprintf('Check the `%s` parameter', $parameter) : $hint;
+ $e = new self($errorMessage, 9, 'invalid_request', 400, $hint, $redirectUri, $previous, $state, $responseMode);
return $e;
}
@@ -162,19 +191,28 @@ public static function invalidRequest(
* @param string|null $redirectUri
* @param \Throwable|null $previous
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
- * @return static
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
+ * @return OidcServerException
+ * @psalm-suppress LessSpecificImplementedReturnType
*/
public static function accessDenied(
$hint = null,
$redirectUri = null,
?Throwable $previous = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
- $e = parent::accessDenied($hint, $redirectUri, $previous);
- $e->setState($state);
- $e->useFragmentInHttpResponses($useFragment);
+ $e = new self(
+ 'The resource owner or authorization server denied the request.',
+ 9,
+ 'access_denied',
+ 401,
+ $hint,
+ $redirectUri,
+ $previous,
+ $state,
+ $responseMode,
+ );
return $e;
}
@@ -186,21 +224,20 @@ public static function accessDenied(
* @param string|null $redirectUri
* @param \Throwable|null $previous
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
*
- * @return self
+ * @return OidcServerException
*/
public static function loginRequired(
?string $hint = null,
?string $redirectUri = null,
?Throwable $previous = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
$errorMessage = "End-User is not already authenticated.";
- $e = new self($errorMessage, 6, 'login_required', 400, $hint, $redirectUri, $previous, $state);
- $e->useFragmentInHttpResponses($useFragment);
+ $e = new self($errorMessage, 6, 'login_required', 400, $hint, $redirectUri, $previous, $state, $responseMode);
return $e;
}
@@ -212,21 +249,30 @@ public static function loginRequired(
* @param string|null $redirectUri
* @param \Throwable|null $previous
* @param string|null $state
- * @param bool $useFragment Use URI fragment to return error parameters
+ * @param \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface|null $responseMode
*
- * @return self
+ * @return OidcServerException
*/
public static function requestNotSupported(
?string $hint = null,
?string $redirectUri = null,
?Throwable $previous = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
$errorMessage = "Request object not supported.";
- $e = new self($errorMessage, 7, 'request_not_supported', 400, $hint, $redirectUri, $previous, $state);
- $e->useFragmentInHttpResponses($useFragment);
+ $e = new self(
+ $errorMessage,
+ 7,
+ 'request_not_supported',
+ 400,
+ $hint,
+ $redirectUri,
+ $previous,
+ $state,
+ $responseMode,
+ );
return $e;
}
@@ -237,7 +283,7 @@ public static function requestNotSupported(
* @param string|null $hint
* @param \Throwable|null $previous
*
- * @return self
+ * @return OidcServerException
* @psalm-suppress LessSpecificImplementedReturnType
*/
public static function invalidRefreshToken($hint = null, ?Throwable $previous = null): OidcServerException
@@ -250,7 +296,7 @@ public static function invalidTrustChain(
?string $redirectUri = null,
?Throwable $previous = null,
?string $state = null,
- bool $useFragment = false,
+ ?ResponseModeInterface $responseMode = null,
): OidcServerException {
$errorMessage = 'Trust chain validation failed.';
@@ -263,8 +309,8 @@ public static function invalidTrustChain(
$redirectUri,
$previous,
$state,
+ $responseMode,
);
- $e->useFragmentInHttpResponses($useFragment);
return $e;
}
@@ -361,8 +407,7 @@ public function setState(?string $state = null): void
* Generate an HTTP response.
*
* @param \Psr\Http\Message\ResponseInterface $response
- * @param bool $useFragment True if errors should be in the URI fragment instead of query string. Note
- * that this can also be set using useFragmentInHttpResponses().
+ * @param bool $useFragment
* @param int $jsonOptions options passed to json_encode
*
* @return \Psr\Http\Message\ResponseInterface
@@ -377,16 +422,13 @@ public function generateHttpResponse(
$payload = $this->getPayload();
- if ($this->redirectUri !== null) {
- $paramSeparator = '?';
-
- if ($this->useFragmentInHttpResponses || $useFragment) {
- $paramSeparator = '#';
- }
-
- $this->redirectUri .= (!str_contains($this->redirectUri, $paramSeparator)) ? $paramSeparator : '&';
+ if ($this->responseMode === null) {
+ // Fallback to useFragment if responseMode is not set
+ $this->responseMode = $useFragment ? new FragmentResponseMode() : new QueryResponseMode();
+ }
- return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
+ if ($this->redirectUri !== null) {
+ return $this->responseMode->buildResponse($this->redirectUri, $payload)->generateHttpResponse($response);
}
foreach ($headers as $header => $content) {
@@ -399,14 +441,4 @@ public function generateHttpResponse(
return $response->withStatus($this->getHttpStatusCode());
}
-
- /**
- * Use URI fragment to return parameters in HTTP redirection error responses
- *
- * @param bool $useFragment True if fragment should be used, false otherwise
- */
- public function useFragmentInHttpResponses(bool $useFragment): void
- {
- $this->useFragmentInHttpResponses = $useFragment;
- }
}
diff --git a/src/Server/Grants/AuthCodeGrant.php b/src/Server/Grants/AuthCodeGrant.php
index dbbbce0b..ff62f578 100644
--- a/src/Server/Grants/AuthCodeGrant.php
+++ b/src/Server/Grants/AuthCodeGrant.php
@@ -17,7 +17,7 @@
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface as OAuth2AuthCodeRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest as OAuth2AuthorizationRequest;
-use League\OAuth2\Server\ResponseTypes\RedirectResponse;
+use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
@@ -59,10 +59,12 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestedClaimsRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule;
+use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseModeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeOfflineAccessRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule;
use SimpleSAML\Module\oidc\Server\RequestTypes\AuthorizationRequest;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Server\ResponseTypes\Interfaces\AcrResponseTypeInterface;
use SimpleSAML\Module\oidc\Server\ResponseTypes\Interfaces\AuthTimeResponseTypeInterface;
use SimpleSAML\Module\oidc\Server\ResponseTypes\Interfaces\NonceResponseTypeInterface;
@@ -255,7 +257,7 @@ public function completeAuthorizationRequest(
*/
public function completeOidcAuthorizationRequest(
AuthorizationRequest $authorizationRequest,
- ): RedirectResponse {
+ ): AbstractResponseType {
$user = $authorizationRequest->getUser();
if ($user instanceof UserEntity === false) {
throw new LogicException('An instance of UserEntity should be set on the ' .
@@ -272,6 +274,7 @@ public function completeOidcAuthorizationRequest(
$finalRedirectUri,
null,
$authorizationRequest->getState(),
+ $authorizationRequest->getResponseMode(),
);
}
@@ -304,15 +307,13 @@ public function completeOidcAuthorizationRequest(
$jsonPayload = json_encode($payload, JSON_THROW_ON_ERROR);
- $response = new RedirectResponse();
- $response->setRedirectUri(
- $this->makeRedirectUri(
- $finalRedirectUri,
- [
- 'code' => $this->encrypt($jsonPayload),
- 'state' => $authorizationRequest->getState(),
- ],
- ),
+ $responseMode = $authorizationRequest->getResponseMode() ?? new QueryResponseMode();
+ $response = $responseMode->buildResponse(
+ $finalRedirectUri,
+ [
+ 'code' => $this->encrypt($jsonPayload),
+ 'state' => $authorizationRequest->getState(),
+ ],
);
return $response;
@@ -503,7 +504,9 @@ public function respondToAccessTokenRequest(
$resultBag = $this->requestRulesManager->check(
$request,
$rulesToExecute,
- false,
+ // Response mode is not relevant for token request, as there is
+ // no redirection, but we need to provide something to execute rules.
+ new QueryResponseMode(),
$this->allowedTokenHttpMethods,
);
@@ -770,6 +773,8 @@ public function validateAuthorizationRequestWithRequestRules(
$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:', [
'redirectUri' => $redirectUri,
@@ -784,7 +789,7 @@ public function validateAuthorizationRequestWithRequestRules(
$resultBag = $this->requestRulesManager->check(
$request,
$rulesToExecute,
- false,
+ $responseMode,
$this->allowedAuthorizationHttpMethods,
);
@@ -902,6 +907,14 @@ public function validateAuthorizationRequestWithRequestRules(
);
$authorizationRequest->setAuthorizationDetails($authorizationDetails);
+ /** @var \SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface $responseMode */
+ $responseMode = $resultBag->getOrFail(ResponseModeRule::class)->getValue();
+ $this->loggerService->debug(
+ 'AuthCodeGrant: Response mode: ',
+ ['responseMode' => $responseMode],
+ );
+ $authorizationRequest->setResponseMode($responseMode);
+
// TODO This is a band-aid fix for having credential claims in the userinfo endpoint when
// only VCI authorizationDetails are supplied. This requires configuring a matching OIDC scope
// that has all the credential type claims as well.
diff --git a/src/Server/Grants/ImplicitGrant.php b/src/Server/Grants/ImplicitGrant.php
index 2677ca98..75fcf1c8 100644
--- a/src/Server/Grants/ImplicitGrant.php
+++ b/src/Server/Grants/ImplicitGrant.php
@@ -7,7 +7,6 @@
use DateInterval;
use League\OAuth2\Server\Grant\ImplicitGrant as OAuth2ImplicitGrant;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest as OAuth2AuthorizationRequest;
-use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
@@ -32,10 +31,12 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredNonceRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule;
+use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseModeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule;
use SimpleSAML\Module\oidc\Server\RequestTypes\AuthorizationRequest;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FragmentResponseMode;
use SimpleSAML\Module\oidc\Services\IdTokenBuilder;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -62,10 +63,9 @@ public function __construct(
AccessTokenRepositoryInterface $accessTokenRepository,
protected RequestRulesManager $requestRulesManager,
protected RequestParamsResolver $requestParamsResolver,
- protected string $queryDelimiter,
AccessTokenEntityFactory $accessTokenEntityFactory,
) {
- parent::__construct($accessTokenTTL, $queryDelimiter);
+ parent::__construct($accessTokenTTL);
$this->accessTokenRepository = $accessTokenRepository;
$this->accessTokenEntityFactory = $accessTokenEntityFactory;
@@ -143,6 +143,8 @@ public function validateAuthorizationRequestWithRequestRules(
$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...
$this->requestRulesManager->setData('default_scope', $this->defaultScope);
@@ -151,7 +153,7 @@ public function validateAuthorizationRequestWithRequestRules(
$resultBag = $this->requestRulesManager->check(
$request,
$rulesToExecute,
- $this->shouldUseFragment(),
+ $responseMode,
$this->allowedAuthorizationHttpMethods,
);
@@ -195,6 +197,10 @@ public function validateAuthorizationRequestWithRequestRules(
$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);
+
return $authorizationRequest;
}
@@ -220,7 +226,7 @@ private function completeOidcAuthorizationRequest(AuthorizationRequest $authoriz
$redirectUrl,
null,
$authorizationRequest->getState(),
- $this->shouldUseFragment(),
+ $authorizationRequest->getResponseMode(),
);
}
@@ -274,14 +280,10 @@ private function completeOidcAuthorizationRequest(AuthorizationRequest $authoriz
$responseParams['id_token'] = $idToken->getToken();
- $response = new RedirectResponse();
-
- $response->setRedirectUri(
- $this->makeRedirectUri(
- $redirectUrl,
- $responseParams,
- $this->queryDelimiter,
- ),
+ $responseMode = $authorizationRequest->getResponseMode() ?? new FragmentResponseMode();
+ $response = $responseMode->buildResponse(
+ $redirectUrl,
+ $responseParams,
);
return $response;
@@ -301,14 +303,4 @@ private function getRedirectUrl(AuthorizationRequest $authorizationRequest): str
return $redirectUris;
}
-
- /**
- * Check if fragment should be used for params transportation in HTTP responses
- *
- * @return bool
- */
- protected function shouldUseFragment(): bool
- {
- return $this->queryDelimiter === '#';
- }
}
diff --git a/src/Server/Grants/PreAuthCodeGrant.php b/src/Server/Grants/PreAuthCodeGrant.php
index 5c7e0a24..befc7b15 100644
--- a/src/Server/Grants/PreAuthCodeGrant.php
+++ b/src/Server/Grants/PreAuthCodeGrant.php
@@ -22,6 +22,7 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AuthorizationDetailsRule;
use SimpleSAML\Module\oidc\Server\RequestTypes\AuthorizationRequest;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\OpenID\Codebooks\GrantTypesEnum;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -174,7 +175,9 @@ public function respondToAccessTokenRequest(
$resultBag = $this->requestRulesManager->check(
$request,
[AuthorizationDetailsRule::class],
- false,
+ // Response mode is not relevant for token request, as there is
+ // no redirection, but we need to provide something to execute rules.
+ new QueryResponseMode(),
$this->allowedTokenHttpMethods,
);
diff --git a/src/Server/RequestRules/Interfaces/RequestRuleInterface.php b/src/Server/RequestRules/Interfaces/RequestRuleInterface.php
index 6f719a31..65a39950 100644
--- a/src/Server/RequestRules/Interfaces/RequestRuleInterface.php
+++ b/src/Server/RequestRules/Interfaces/RequestRuleInterface.php
@@ -5,6 +5,8 @@
namespace SimpleSAML\Module\oidc\Server\RequestRules\Interfaces;
use Psr\Http\Message\ServerRequestInterface;
+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;
@@ -18,13 +20,15 @@ public function getKey(): string;
/**
* Check specific rule.
+ *
* @param \SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface $currentResultBag
* ResultBag with all results of the checks performed to current check
* @param array $data Data which will be available during check.
- * @param bool $useFragmentInHttpErrorResponses Indicate that in case of HTTP error responses, params should be
- * returned in URI fragment instead of query.
+ * @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
+ *
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException If check fails
*/
public function checkRule(
@@ -32,7 +36,7 @@ public function checkRule(
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface;
}
diff --git a/src/Server/RequestRules/RequestRulesManager.php b/src/Server/RequestRules/RequestRulesManager.php
index c08c1a67..9adfad34 100644
--- a/src/Server/RequestRules/RequestRulesManager.php
+++ b/src/Server/RequestRules/RequestRulesManager.php
@@ -9,6 +9,8 @@
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;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -45,15 +47,16 @@ public function add(RequestRuleInterface $rule): void
/**
* @param class-string[] $ruleKeysToExecute
- * @param bool $useFragmentInHttpErrorResponses Indicate that in case of HTTP error responses, params should be
- * returned in URI fragment instead of query.
+ * @param ResponseModeInterface $responseMode Response mode which will be
+ * used in rules execution, as some rules might need to adjust their
+ * behaviour based on response mode used in request.
* @param HttpMethodsEnum[] $allowedServerRequestMethods Indicate allowed HTTP methods used for request
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
*/
public function check(
ServerRequestInterface $request,
array $ruleKeysToExecute,
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ResultBagInterface {
foreach ($ruleKeysToExecute as $ruleKey) {
@@ -66,7 +69,7 @@ public function check(
$this->resultBag,
$this->loggerService,
$this->data,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
$allowedServerRequestMethods,
);
@@ -97,7 +100,7 @@ public function predefineResultBag(ResultBagInterface $resultBag): void
/**
* Set data which will be available in each check, using key value pair
*/
- public function setData(string $key, mixed $value): void
+ public function setData(string $key, string $value): void
{
$this->data[$key] = $value;
}
diff --git a/src/Server/RequestRules/Rules/AcrValuesRule.php b/src/Server/RequestRules/Rules/AcrValuesRule.php
index 7d02cf1f..d971303b 100644
--- a/src/Server/RequestRules/Rules/AcrValuesRule.php
+++ b/src/Server/RequestRules/Rules/AcrValuesRule.php
@@ -8,6 +8,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -16,13 +18,16 @@ class AcrValuesRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('AcrValuesRule::checkRule');
diff --git a/src/Server/RequestRules/Rules/AddClaimsToIdTokenRule.php b/src/Server/RequestRules/Rules/AddClaimsToIdTokenRule.php
index a6aae64f..e0ca70f1 100644
--- a/src/Server/RequestRules/Rules/AddClaimsToIdTokenRule.php
+++ b/src/Server/RequestRules/Rules/AddClaimsToIdTokenRule.php
@@ -8,6 +8,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
@@ -15,14 +17,17 @@ class AddClaimsToIdTokenRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
/** @var string $responseType */
diff --git a/src/Server/RequestRules/Rules/AuthorizationDetailsRule.php b/src/Server/RequestRules/Rules/AuthorizationDetailsRule.php
index 4b4ff89f..4af36369 100644
--- a/src/Server/RequestRules/Rules/AuthorizationDetailsRule.php
+++ b/src/Server/RequestRules/Rules/AuthorizationDetailsRule.php
@@ -11,6 +11,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -28,13 +30,16 @@ public function __construct(
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('AuthorizationDetailsRule::checkRule.');
diff --git a/src/Server/RequestRules/Rules/ClientAuthenticationRule.php b/src/Server/RequestRules/Rules/ClientAuthenticationRule.php
index 7a062bd6..f6e7a92f 100644
--- a/src/Server/RequestRules/Rules/ClientAuthenticationRule.php
+++ b/src/Server/RequestRules/Rules/ClientAuthenticationRule.php
@@ -10,6 +10,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\AuthenticatedOAuth2ClientResolver;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -28,13 +30,15 @@ public function __construct(
/**
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
diff --git a/src/Server/RequestRules/Rules/ClientIdRule.php b/src/Server/RequestRules/Rules/ClientIdRule.php
index b329c179..0a2e80bc 100644
--- a/src/Server/RequestRules/Rules/ClientIdRule.php
+++ b/src/Server/RequestRules/Rules/ClientIdRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -20,6 +22,7 @@ class ClientIdRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \JsonException
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws \Psr\SimpleCache\InvalidArgumentException
@@ -32,13 +35,16 @@ class ClientIdRule extends AbstractRule
* @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException
* @throws \SimpleSAML\OpenID\Exceptions\TrustChainException
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('ClientIdRule::checkRule');
diff --git a/src/Server/RequestRules/Rules/ClientRedirectUriRule.php b/src/Server/RequestRules/Rules/ClientRedirectUriRule.php
index 3c00c763..d4aa11f1 100644
--- a/src/Server/RequestRules/Rules/ClientRedirectUriRule.php
+++ b/src/Server/RequestRules/Rules/ClientRedirectUriRule.php
@@ -13,6 +13,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -30,14 +32,18 @@ public function __construct(
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('RedirectUriRule::checkRule');
diff --git a/src/Server/RequestRules/Rules/ClientRule.php b/src/Server/RequestRules/Rules/ClientRule.php
index ac6a1160..242c60d0 100644
--- a/src/Server/RequestRules/Rules/ClientRule.php
+++ b/src/Server/RequestRules/Rules/ClientRule.php
@@ -17,6 +17,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\FederationCache;
use SimpleSAML\Module\oidc\Utils\FederationParticipationValidator;
@@ -53,6 +55,7 @@ public function __construct(
/**
* @inheritDoc
+ *
* @throws \JsonException
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws \Psr\SimpleCache\InvalidArgumentException
@@ -65,13 +68,16 @@ public function __construct(
* @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException
* @throws \SimpleSAML\OpenID\Exceptions\TrustChainException
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('ClientRule::checkRule.');
diff --git a/src/Server/RequestRules/Rules/CodeChallengeMethodRule.php b/src/Server/RequestRules/Rules/CodeChallengeMethodRule.php
index 33d5f70a..f0e46c42 100644
--- a/src/Server/RequestRules/Rules/CodeChallengeMethodRule.php
+++ b/src/Server/RequestRules/Rules/CodeChallengeMethodRule.php
@@ -11,6 +11,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -29,13 +31,16 @@ public function __construct(
/**
* @throws \Throwable
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('CodeChallengeMethodRule::checkRule');
@@ -62,7 +67,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/CodeChallengeRule.php b/src/Server/RequestRules/Rules/CodeChallengeRule.php
index feb37160..b55d79d6 100644
--- a/src/Server/RequestRules/Rules/CodeChallengeRule.php
+++ b/src/Server/RequestRules/Rules/CodeChallengeRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -17,14 +19,18 @@ class CodeChallengeRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('CodeChallengeRule::checkRule');
@@ -50,7 +56,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
@@ -66,7 +72,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/CodeVerifierRule.php b/src/Server/RequestRules/Rules/CodeVerifierRule.php
index 8b3767eb..d4bcc04a 100644
--- a/src/Server/RequestRules/Rules/CodeVerifierRule.php
+++ b/src/Server/RequestRules/Rules/CodeVerifierRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -17,13 +19,16 @@ class CodeVerifierRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
/** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */
diff --git a/src/Server/RequestRules/Rules/IdTokenHintRule.php b/src/Server/RequestRules/Rules/IdTokenHintRule.php
index 8feccbf2..dc481b22 100644
--- a/src/Server/RequestRules/Rules/IdTokenHintRule.php
+++ b/src/Server/RequestRules/Rules/IdTokenHintRule.php
@@ -11,6 +11,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -32,14 +34,18 @@ public function __construct(
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
/** @var string|null $state */
diff --git a/src/Server/RequestRules/Rules/IssuerStateRule.php b/src/Server/RequestRules/Rules/IssuerStateRule.php
index 7ba9bf2d..962e94e9 100644
--- a/src/Server/RequestRules/Rules/IssuerStateRule.php
+++ b/src/Server/RequestRules/Rules/IssuerStateRule.php
@@ -8,6 +8,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -16,13 +18,16 @@ class IssuerStateRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$issuerState = $this->requestParamsResolver->getAsStringBasedOnAllowedMethods(
diff --git a/src/Server/RequestRules/Rules/MaxAgeRule.php b/src/Server/RequestRules/Rules/MaxAgeRule.php
index e5731a7f..1b02c085 100644
--- a/src/Server/RequestRules/Rules/MaxAgeRule.php
+++ b/src/Server/RequestRules/Rules/MaxAgeRule.php
@@ -12,6 +12,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\AuthenticationService;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -37,13 +39,16 @@ public function __construct(
* @throws \SimpleSAML\Error\NotFound
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('MaxAgeRule::checkRule');
@@ -80,7 +85,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/PostLogoutRedirectUriRule.php b/src/Server/RequestRules/Rules/PostLogoutRedirectUriRule.php
index 258d8186..d0d4e8df 100644
--- a/src/Server/RequestRules/Rules/PostLogoutRedirectUriRule.php
+++ b/src/Server/RequestRules/Rules/PostLogoutRedirectUriRule.php
@@ -11,6 +11,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -28,14 +30,18 @@ public function __construct(
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
/** @var string|null $state */
diff --git a/src/Server/RequestRules/Rules/PromptRule.php b/src/Server/RequestRules/Rules/PromptRule.php
index 8a994f45..9b708ab3 100644
--- a/src/Server/RequestRules/Rules/PromptRule.php
+++ b/src/Server/RequestRules/Rules/PromptRule.php
@@ -12,6 +12,8 @@
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
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\AuthenticationService;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -38,13 +40,16 @@ public function __construct(
* @throws \SimpleSAML\Error\NotFound
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('PromptRule::checkRule');
@@ -78,7 +83,7 @@ public function checkRule(
$redirectUri,
null,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/RequestObjectRule.php b/src/Server/RequestRules/Rules/RequestObjectRule.php
index 81c05812..e4eab6e3 100644
--- a/src/Server/RequestRules/Rules/RequestObjectRule.php
+++ b/src/Server/RequestRules/Rules/RequestObjectRule.php
@@ -10,6 +10,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\JwksResolver;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -29,13 +31,16 @@ public function __construct(
/**
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('RequestObjectRule::checkRule');
@@ -80,7 +85,7 @@ public function checkRule(
$redirectUri,
null,
$stateValue,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
try {
@@ -91,7 +96,7 @@ public function checkRule(
$redirectUri,
null,
$stateValue,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/RequestedClaimsRule.php b/src/Server/RequestRules/Rules/RequestedClaimsRule.php
index 3a7d60b3..18388d64 100644
--- a/src/Server/RequestRules/Rules/RequestedClaimsRule.php
+++ b/src/Server/RequestRules/Rules/RequestedClaimsRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\ClaimTranslatorExtractor;
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -28,13 +30,16 @@ public function __construct(
/**
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('RequestedClaimsRule::checkRule');
diff --git a/src/Server/RequestRules/Rules/RequiredNonceRule.php b/src/Server/RequestRules/Rules/RequiredNonceRule.php
index 16034d17..1839ca9c 100644
--- a/src/Server/RequestRules/Rules/RequiredNonceRule.php
+++ b/src/Server/RequestRules/Rules/RequiredNonceRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -17,14 +19,18 @@ class RequiredNonceRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
/** @var string $redirectUri */
@@ -45,7 +51,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/RequiredOpenIdScopeRule.php b/src/Server/RequestRules/Rules/RequiredOpenIdScopeRule.php
index 5fa0dc86..53b62375 100644
--- a/src/Server/RequestRules/Rules/RequiredOpenIdScopeRule.php
+++ b/src/Server/RequestRules/Rules/RequiredOpenIdScopeRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
@@ -16,14 +18,18 @@ class RequiredOpenIdScopeRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('RequiredOpenIdScopeRule::checkRule.');
@@ -53,7 +59,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
} catch (\Throwable $e) {
diff --git a/src/Server/RequestRules/Rules/ResponseModeRule.php b/src/Server/RequestRules/Rules/ResponseModeRule.php
new file mode 100644
index 00000000..a4175cbf
--- /dev/null
+++ b/src/Server/RequestRules/Rules/ResponseModeRule.php
@@ -0,0 +1,122 @@
+requestParamsResolver->getAllBasedOnAllowedMethods(
+ $request,
+ $allowedServerRequestMethods,
+ );
+
+ // response_mode requires client_id to be present
+ if (
+ !isset($requestParams[ParamsEnum::ClientId->value])
+ ) {
+ throw OidcServerException::invalidRequest(
+ ParamsEnum::ClientId->value,
+ 'Missing client_id',
+ );
+ }
+
+ $responseModeValue = isset($requestParams[ParamsEnum::ResponseMode->value]) ?
+ (string)$requestParams[ParamsEnum::ResponseMode->value] : null;
+ $loggerService->debug('ResponseModeRule: response_mode requestParams value: ' . ($responseModeValue ?? 'null'));
+
+
+ // if response_mode is not set, we set the default
+ // to 'code' if not set. Error will be thrown by ResponseTypeRule.
+ $responseType = isset($requestParams[ParamsEnum::ResponseType->value]) ?
+ (string)$requestParams[ParamsEnum::ResponseType->value] : 'code';
+ if (!$responseModeValue) {
+ $responseModeValue = str_contains($responseType, 'token') ?
+ ResponseModesEnum::Fragment->value : ResponseModesEnum::Query->value;
+ }
+
+ // Verify if response_mode is one of supported response modes
+ if (
+ !in_array(
+ $responseModeValue,
+ $this->moduleConfig->getSupportedResponseModes(),
+ true,
+ )
+ ) {
+ throw OidcServerException::invalidRequest(
+ ParamsEnum::ResponseMode->value,
+ 'Invalid response_mode',
+ );
+ }
+
+ // Validate whether response_mode is allowed by client configuration
+ /** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */
+ $client = $currentResultBag->getOrFail(ClientRule::class)->getValue();
+ $currentResultBag->getOrFail(ClientRedirectUriRule::class)->getValue();
+ $currentResultBag->getOrFail(StateRule::class)->getValue();
+
+ $allowedResponseModes = $client->getAllowedResponseModes();
+ if (!in_array($responseModeValue, $allowedResponseModes, true)) {
+ throw OidcServerException::invalidRequest(
+ 'response_mode',
+ 'response_mode "' . $responseModeValue . '" is not allowed for this client',
+ );
+ }
+
+ // Resolve ResponseModeStrategy
+ $responseMode = match ($responseModeValue) {
+ ResponseModesEnum::Query->value => $this->queryResponseMode,
+ ResponseModesEnum::Fragment->value => $this->fragmentResponseMode,
+ ResponseModesEnum::FormPost->value => $this->formPostResponseMode,
+ default => throw OidcServerException::invalidRequest(
+ ParamsEnum::ResponseMode->value,
+ 'Unsupported response_mode. How did we get here?',
+ ),
+ };
+
+ return new Result($this->getKey(), $responseMode);
+ }
+}
diff --git a/src/Server/RequestRules/Rules/ResponseTypeRule.php b/src/Server/RequestRules/Rules/ResponseTypeRule.php
index 30acb5ad..c13377c1 100644
--- a/src/Server/RequestRules/Rules/ResponseTypeRule.php
+++ b/src/Server/RequestRules/Rules/ResponseTypeRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -17,13 +19,16 @@ class ResponseTypeRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$requestParams = $this->requestParamsResolver->getAllBasedOnAllowedMethods(
@@ -35,7 +40,10 @@ public function checkRule(
!isset($requestParams[ParamsEnum::ResponseType->value]) ||
!isset($requestParams[ParamsEnum::ClientId->value])
) {
- throw OidcServerException::invalidRequest('Missing response_type or client_id');
+ throw OidcServerException::invalidRequest(
+ ParamsEnum::ResponseType->value,
+ 'Missing response_type or client_id',
+ );
}
// TODO v7 consider checking for supported response types, for example, from configuration...
diff --git a/src/Server/RequestRules/Rules/ScopeOfflineAccessRule.php b/src/Server/RequestRules/Rules/ScopeOfflineAccessRule.php
index ee4188b2..fd41cdfe 100644
--- a/src/Server/RequestRules/Rules/ScopeOfflineAccessRule.php
+++ b/src/Server/RequestRules/Rules/ScopeOfflineAccessRule.php
@@ -9,6 +9,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
@@ -16,14 +18,17 @@ class ScopeOfflineAccessRule extends AbstractRule
{
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('ScopeOfflineAccessRule::checkRule');
@@ -50,7 +55,7 @@ public function checkRule(
null,
$redirectUri,
$state,
- $useFragmentInHttpErrorResponses,
+ $responseMode,
);
}
diff --git a/src/Server/RequestRules/Rules/ScopeRule.php b/src/Server/RequestRules/Rules/ScopeRule.php
index bc6b753c..2587aaee 100644
--- a/src/Server/RequestRules/Rules/ScopeRule.php
+++ b/src/Server/RequestRules/Rules/ScopeRule.php
@@ -12,6 +12,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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\Module\oidc\Utils\RequestParamsResolver;
use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum;
@@ -29,14 +31,18 @@ public function __construct(
/**
* @inheritDoc
+ *
* @throws \Throwable
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('ScopeRule::checkRule.');
@@ -69,7 +75,7 @@ public function checkRule(
if ($scope instanceof ScopeEntityInterface === false) {
$loggerService->error('ScopeRule: Invalid scope: ' . $scopeItem);
- throw OidcServerException::invalidScope($scopeItem, $redirectUri, $state);
+ throw OidcServerException::invalidScope($scopeItem, $redirectUri, $state, $responseMode);
}
$loggerService->debug('ScopeRule: Valid scope: ' . $scopeItem);
$validScopes[] = $scope;
diff --git a/src/Server/RequestRules/Rules/StateRule.php b/src/Server/RequestRules/Rules/StateRule.php
index d60d31d7..8f337281 100644
--- a/src/Server/RequestRules/Rules/StateRule.php
+++ b/src/Server/RequestRules/Rules/StateRule.php
@@ -8,6 +8,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -16,13 +18,16 @@ class StateRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
$loggerService->debug('StateRule::checkRule');
diff --git a/src/Server/RequestRules/Rules/UiLocalesRule.php b/src/Server/RequestRules/Rules/UiLocalesRule.php
index 3bbbd3c3..4561da7b 100644
--- a/src/Server/RequestRules/Rules/UiLocalesRule.php
+++ b/src/Server/RequestRules/Rules/UiLocalesRule.php
@@ -8,6 +8,8 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface;
use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface;
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;
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
@@ -16,13 +18,16 @@ class UiLocalesRule extends AbstractRule
{
/**
* @inheritDoc
+ *
+ * @param ResponseModeInterface $responseMode
+ * @param HttpMethodsEnum[] $allowedServerRequestMethods
*/
public function checkRule(
ServerRequestInterface $request,
ResultBagInterface $currentResultBag,
LoggerService $loggerService,
array $data = [],
- bool $useFragmentInHttpErrorResponses = false,
+ ResponseModeInterface $responseMode = new QueryResponseMode(),
array $allowedServerRequestMethods = [HttpMethodsEnum::GET],
): ?ResultInterface {
return new Result($this->getKey(), $this->requestParamsResolver->getBasedOnAllowedMethods(
diff --git a/src/Server/RequestTypes/AuthorizationRequest.php b/src/Server/RequestTypes/AuthorizationRequest.php
index 1278e9f9..bc969f10 100644
--- a/src/Server/RequestTypes/AuthorizationRequest.php
+++ b/src/Server/RequestTypes/AuthorizationRequest.php
@@ -6,6 +6,7 @@
use League\OAuth2\Server\RequestTypes\AuthorizationRequest as OAuth2AuthorizationRequest;
use SimpleSAML\Module\oidc\Codebooks\FlowTypeEnum;
+use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface;
class AuthorizationRequest extends OAuth2AuthorizationRequest
{
@@ -70,6 +71,8 @@ class AuthorizationRequest extends OAuth2AuthorizationRequest
*/
protected ?string $issuerState = null;
+ private ?ResponseModeInterface $responseMode = null;
+
public static function fromOAuth2AuthorizationRequest(
OAuth2AuthorizationRequest $oAuth2authorizationRequest,
): AuthorizationRequest {
@@ -162,6 +165,16 @@ public function getResponseType(): ?string
return $this->responseType;
}
+ public function setResponseMode(ResponseModeInterface $responseMode): void
+ {
+ $this->responseMode = $responseMode;
+ }
+
+ public function getResponseMode(): ?ResponseModeInterface
+ {
+ return $this->responseMode;
+ }
+
/**
* Check if access token should be issued in authorization response (implicit flow, hybrid flow...).
* @return bool
diff --git a/src/Server/ResponseModes/FormPostResponseMode.php b/src/Server/ResponseModes/FormPostResponseMode.php
new file mode 100644
index 00000000..70064cd1
--- /dev/null
+++ b/src/Server/ResponseModes/FormPostResponseMode.php
@@ -0,0 +1,39 @@
+templateFactory = $templateFactory;
+ }
+
+ public function buildResponse(string $redirectUri, array $params): AbstractResponseType
+ {
+ $template = $this->templateFactory->build(
+ templateName: 'oidc:formpost.twig',
+ data: [
+ 'redirectUri' => $redirectUri,
+ 'params' => $params,
+ ],
+ showMenu: false,
+ showModuleName: false,
+ showSubPageTitle: false,
+ );
+ $html = $template->getContents(); // renders to a string
+
+ $response = new HtmlResponse();
+ $response->setHtml($html);
+ return $response;
+ }
+}
diff --git a/src/Server/ResponseModes/FragmentResponseMode.php b/src/Server/ResponseModes/FragmentResponseMode.php
new file mode 100644
index 00000000..b8e40652
--- /dev/null
+++ b/src/Server/ResponseModes/FragmentResponseMode.php
@@ -0,0 +1,20 @@
+setRedirectUri($redirectUri . $separator . http_build_query($params));
+
+ return $response;
+ }
+}
diff --git a/src/Server/ResponseModes/QueryResponseMode.php b/src/Server/ResponseModes/QueryResponseMode.php
new file mode 100644
index 00000000..2a379447
--- /dev/null
+++ b/src/Server/ResponseModes/QueryResponseMode.php
@@ -0,0 +1,20 @@
+setRedirectUri($redirectUri . $separator . http_build_query($params));
+
+ return $response;
+ }
+}
diff --git a/src/Server/ResponseModes/ResponseModeInterface.php b/src/Server/ResponseModes/ResponseModeInterface.php
new file mode 100644
index 00000000..529e4824
--- /dev/null
+++ b/src/Server/ResponseModes/ResponseModeInterface.php
@@ -0,0 +1,12 @@
+html = $html;
+ }
+
+ /**
+ * @param ResponseInterface $response
+ *
+ * @return ResponseInterface
+ */
+ public function generateHttpResponse(ResponseInterface $response)
+ {
+ $response->getBody()->write($this->html);
+
+ return $response->withStatus(200)->withHeader('Content-Type', 'text/html');
+ }
+}
diff --git a/src/Services/Container.php b/src/Services/Container.php
index d03173b5..8f418899 100644
--- a/src/Services/Container.php
+++ b/src/Services/Container.php
@@ -92,12 +92,16 @@
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredNonceRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule;
+use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseModeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeOfflineAccessRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule;
use SimpleSAML\Module\oidc\Server\RequestRules\Rules\UiLocalesRule;
use SimpleSAML\Module\oidc\Server\ResourceServer;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FormPostResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\FragmentResponseMode;
+use SimpleSAML\Module\oidc\Server\ResponseModes\QueryResponseMode;
use SimpleSAML\Module\oidc\Server\ResponseTypes\TokenResponse;
use SimpleSAML\Module\oidc\Server\TokenIssuers\RefreshTokenIssuer;
use SimpleSAML\Module\oidc\Server\Validators\BearerTokenValidator;
@@ -424,6 +428,10 @@ public function __construct()
);
$this->services[AuthenticatedOAuth2ClientResolver::class] = $authenticatedOAuth2ClientResolver;
+ $queryResponseMode = new QueryResponseMode();
+ $fragmentResponseMode = new FragmentResponseMode();
+ $formPostResponseMode = new FormPostResponseMode($templateFactory);
+
$requestRules = [
new StateRule($requestParamsResolver, $helpers),
new ClientRule(
@@ -440,6 +448,14 @@ public function __construct()
),
new ClientRedirectUriRule($requestParamsResolver, $helpers, $moduleConfig),
new RequestObjectRule($requestParamsResolver, $helpers, $jwksResolver),
+ new ResponseModeRule(
+ $requestParamsResolver,
+ $helpers,
+ $moduleConfig,
+ $queryResponseMode,
+ $fragmentResponseMode,
+ $formPostResponseMode,
+ ),
new PromptRule($requestParamsResolver, $helpers, $authSimpleFactory, $authenticationService, $sspBridge),
new MaxAgeRule($requestParamsResolver, $helpers, $authSimpleFactory, $authenticationService, $sspBridge),
new ScopeRule($requestParamsResolver, $helpers, $scopeRepository),
diff --git a/src/Services/OpMetadataService.php b/src/Services/OpMetadataService.php
index 91537d7f..df958b03 100644
--- a/src/Services/OpMetadataService.php
+++ b/src/Services/OpMetadataService.php
@@ -100,6 +100,8 @@ private function initMetadata(): void
$this->metadata[ClaimsEnum::ClaimsSupported->value] = $claimsSupported;
}
+ $this->metadata[ClaimsEnum::ResponseModesSupported->value] = $this->moduleConfig->getSupportedResponseModes();
+
// https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-oauth-20-authorization-serv
// OPTIONAL
// pre-authorized_grant_anonymous_access_supported // TODO mivanci Make configurable
diff --git a/src/Utils/Routes.php b/src/Utils/Routes.php
index ab37629d..68791e1e 100644
--- a/src/Utils/Routes.php
+++ b/src/Utils/Routes.php
@@ -53,7 +53,7 @@ public function newResponse(
}
public function newJsonResponse(
- mixed $data = null,
+ array|null $data = null,
int $status = 200,
array $headers = [],
bool $json = false,
diff --git a/templates/base.twig b/templates/base.twig
index 66f51f85..40f0435a 100644
--- a/templates/base.twig
+++ b/templates/base.twig
@@ -1,7 +1,9 @@
{% set showMenu = showMenu ?? false %}
+{% set showModuleName = showModuleName ?? false %}
+{% set showSubpageTitle = showSubpageTitle ?? false %}
{% set moduleName = 'OIDC'|trans %}
{% set subPageTitle = subPageTitle ?? '' %}
-{% set pagetitle = 'OIDC' ~ (subPageTitle ? ' | ' ~ subPageTitle : '') %}
+{% set pagetitle = pagetitle ?? ('OIDC' ~ (subPageTitle ? ' | ' ~ subPageTitle : '')) %}
{% extends "base.twig" %}
diff --git a/templates/clients/includes/form.twig b/templates/clients/includes/form.twig
index 51b1a869..c574d4f6 100644
--- a/templates/clients/includes/form.twig
+++ b/templates/clients/includes/form.twig
@@ -150,6 +150,15 @@
{% endif %}
+
+ {{ form.allowed_response_modes.control | raw }}
+
+ {% if form.allowed_response_modes.hasErrors %}
+
+ {% endif %}
+
{{ 'Please wait while we redirect you...'|trans }}
+ +{% endblock %} + +{% block postload %} + {{ parent() }} + +{% endblock %} \ No newline at end of file diff --git a/tests/unit/src/Controllers/Admin/ClientControllerTest.php b/tests/unit/src/Controllers/Admin/ClientControllerTest.php index 02134ac8..7f853d8d 100644 --- a/tests/unit/src/Controllers/Admin/ClientControllerTest.php +++ b/tests/unit/src/Controllers/Admin/ClientControllerTest.php @@ -11,6 +11,7 @@ use SimpleSAML\Module\oidc\Bridges\SspBridge; use SimpleSAML\Module\oidc\Codebooks\RegistrationTypeEnum; use SimpleSAML\Module\oidc\Controllers\Admin\ClientController; +use SimpleSAML\Module\oidc\Entities\ClientEntity; use SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface; use SimpleSAML\Module\oidc\Exceptions\OidcException; use SimpleSAML\Module\oidc\Factories\Entities\ClientEntityFactory; @@ -82,6 +83,7 @@ class ClientControllerTest extends TestCase ], 'jwks_uri' => 'https://example.com/jwks', 'signed_jwks_uri' => 'https://example.com/signed-jwks', + ClientEntity::KEY_ALLOWED_RESPONSE_MODES => ['query', 'fragment', 'form_post'], ]; protected function setUp(): void diff --git a/tests/unit/src/Forms/ClientFormTest.php b/tests/unit/src/Forms/ClientFormTest.php index 425c3d2e..896d59b5 100644 --- a/tests/unit/src/Forms/ClientFormTest.php +++ b/tests/unit/src/Forms/ClientFormTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use SimpleSAML\Module\oidc\Bridges\SspBridge; use SimpleSAML\Module\oidc\Codebooks\RegistrationTypeEnum; +use SimpleSAML\Module\oidc\Entities\ClientEntity; use SimpleSAML\Module\oidc\Forms\ClientForm; use SimpleSAML\Module\oidc\Forms\Controls\CsrfProtection; use SimpleSAML\Module\oidc\Helpers; @@ -43,6 +44,7 @@ public function setUp(): void parent::setUp(); $this->csrfProtectionMock = $this->createMock(CsrfProtection::class); $this->moduleConfigMock = $this->createMock(ModuleConfig::class); + $this->moduleConfigMock->method('getSupportedResponseModes')->willReturn(['query', 'fragment', 'form_post']); $this->serverRequestMock = $this->createMock(ServerRequest::class); $this->sspBridgeMock = $this->createMock(SspBridge::class); $this->helpers = new Helpers(); @@ -81,6 +83,7 @@ public function setUp(): void ), 'expires_at' => null, 'allowed_origin' => [], + ClientEntity::KEY_ALLOWED_RESPONSE_MODES => ['query', 'fragment', 'form_post',], ]; } diff --git a/tests/unit/src/Server/Grants/ImplicitGrantTest.php b/tests/unit/src/Server/Grants/ImplicitGrantTest.php index d28dabda..5f487746 100644 --- a/tests/unit/src/Server/Grants/ImplicitGrantTest.php +++ b/tests/unit/src/Server/Grants/ImplicitGrantTest.php @@ -31,7 +31,6 @@ class ImplicitGrantTest extends TestCase protected MockObject $accessTokenRepositoryMock; protected MockObject $requestRulesManagerMock; protected MockObject $requestParamsResolverMock; - protected string $queryDelimiter; protected MockObject $accessTokenEntityFactoryMock; protected MockObject $scopeRepositoryMock; protected MockObject $serverRequestMock; @@ -48,7 +47,6 @@ protected function setUp(): void $this->accessTokenRepositoryMock = $this->createMock(AccessTokenRepository::class); $this->requestRulesManagerMock = $this->createMock(RequestRulesManager::class); $this->requestParamsResolverMock = $this->createMock(RequestParamsResolver::class); - $this->queryDelimiter = '#'; $this->accessTokenEntityFactoryMock = $this->createMock(AccessTokenEntityFactory::class); $this->scopeRepositoryMock = $this->createMock(ScopeRepositoryInterface::class); @@ -66,7 +64,6 @@ protected function sut( ?AccessTokenRepositoryInterface $accessTokenRepository = null, ?RequestRulesManager $requestRulesManager = null, ?RequestParamsResolver $requestParamsResolver = null, - ?string $queryDelimiter = null, ?AccessTokenEntityFactory $accessTokenEntityFactory = null, ?ScopeRepositoryInterface $scopeRepository = null, ): ImplicitGrant { @@ -75,7 +72,6 @@ protected function sut( $accessTokenRepository ??= $this->accessTokenRepositoryMock; $requestRulesManager ??= $this->requestRulesManagerMock; $requestParamsResolver ??= $this->requestParamsResolverMock; - $queryDelimiter ??= $this->queryDelimiter; $accessTokenEntityFactory ??= $this->accessTokenEntityFactoryMock; $scopeRepository ??= $this->scopeRepositoryMock; @@ -86,7 +82,6 @@ protected function sut( $accessTokenRepository, $requestRulesManager, $requestParamsResolver, - $queryDelimiter, $accessTokenEntityFactory, ); diff --git a/tests/unit/src/Server/RequestRules/RequestRulesManagerTest.php b/tests/unit/src/Server/RequestRules/RequestRulesManagerTest.php index c70118db..2efabc97 100644 --- a/tests/unit/src/Server/RequestRules/RequestRulesManagerTest.php +++ b/tests/unit/src/Server/RequestRules/RequestRulesManagerTest.php @@ -12,6 +12,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface; use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; /** @@ -122,6 +123,7 @@ public function testSetData(RequestRulesManager $requestRulesManager): void $this->isInstanceOf(ResultBagInterface::class), $this->isInstanceOf(LoggerService::class), $this->arrayHasKey($this->key), + $this->isInstanceOf(ResponseModeInterface::class), ); $requestRulesManager->add($ruleMock); diff --git a/tests/unit/src/Server/RequestRules/Rules/AcrValuesRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/AcrValuesRuleTest.php index 302eaa30..8a147402 100644 --- a/tests/unit/src/Server/RequestRules/Rules/AcrValuesRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/AcrValuesRuleTest.php @@ -12,6 +12,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Result; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AcrValuesRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -25,6 +26,7 @@ class AcrValuesRuleTest extends TestCase protected Stub $resultStub; protected Stub $loggerServiceStub; protected Stub $requestParamsResolverStub; + protected Stub $responseModeStub; protected Helpers $helpers; /** @@ -37,6 +39,7 @@ protected function setUp(): void $this->resultStub = $this->createStub(ResultInterface::class); $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); $this->helpers = new Helpers(); } @@ -62,6 +65,8 @@ public function testNoAcrIsSetIfAcrValuesNotRequested(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ) ?? new Result(AcrValuesRule::class, null); $this->assertNull($result->getValue()); } @@ -79,6 +84,8 @@ public function testPopulatesAcrValuesFromClaimsParameter(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ) ?? new Result(AcrValuesRule::class, null); $this->assertSame(['1', '0'], $result->getValue()['values']); @@ -96,6 +103,8 @@ public function testPopulatesAcrValuesFromAcrValuesRequestParameter(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ) ?? new Result(AcrValuesRule::class, null); $this->assertSame(['1', '0'], $result->getValue()['values']); diff --git a/tests/unit/src/Server/RequestRules/Rules/AddClaimsToIdTokenRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/AddClaimsToIdTokenRuleTest.php index fb54da45..121dc7e2 100644 --- a/tests/unit/src/Server/RequestRules/Rules/AddClaimsToIdTokenRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/AddClaimsToIdTokenRuleTest.php @@ -13,6 +13,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\ResultBag; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AddClaimsToIdTokenRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -48,6 +49,7 @@ class AddClaimsToIdTokenRuleTest extends TestCase private ResultBag $resultBag; private Stub $loggerServiceStub; + private Stub $responseModeStub; /** * @throws \Exception @@ -60,6 +62,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -83,7 +86,13 @@ public function testAddClaimsToIdTokenRuleTest($responseType) { $this->resultBag->add(new Result(ResponseTypeRule::class, $responseType)); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(AddClaimsToIdTokenRule::class, null); $this->assertTrue($result->getValue()); } @@ -103,7 +112,13 @@ public function testDoNotAddClaimsToIdTokenRuleTest($responseType) { $this->resultBag->add(new Result(ResponseTypeRule::class, $responseType)); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(AddClaimsToIdTokenRule::class, null); $this->assertFalse($result->getValue()); @@ -128,6 +143,12 @@ public static function invalidResponseTypeProvider(): array public function testAddClaimsToIdTokenRuleThrowsWithNoResponseTypeParamTest() { $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } } diff --git a/tests/unit/src/Server/RequestRules/Rules/ClientRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/ClientRuleTest.php index abf0eb9e..191c982a 100644 --- a/tests/unit/src/Server/RequestRules/Rules/ClientRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/ClientRuleTest.php @@ -16,6 +16,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\FederationCache; use SimpleSAML\Module\oidc\Utils\FederationParticipationValidator; @@ -41,6 +42,7 @@ class ClientRuleTest extends TestCase protected Stub $helpersStub; protected Stub $jwksResolverStub; protected Stub $federationParticipationValidatorStub; + protected Stub $responseModeStub; /** * @throws \Exception @@ -60,6 +62,7 @@ protected function setUp(): void $this->helpersStub = $this->createStub(Helpers::class); $this->jwksResolverStub = $this->createStub(JwksResolver::class); $this->federationParticipationValidatorStub = $this->createStub(FederationParticipationValidator::class); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut(): ClientRule @@ -91,6 +94,8 @@ public function testCheckRuleEmptyClientIdThrows(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ); } @@ -103,6 +108,8 @@ public function testCheckRuleInvalidClientThrows(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ); } @@ -119,6 +126,8 @@ public function testCheckRuleForValidClientId(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertInstanceOf(ClientEntityInterface::class, $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/CodeChallengeMethodRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/CodeChallengeMethodRuleTest.php index f01343ca..e3c66093 100644 --- a/tests/unit/src/Server/RequestRules/Rules/CodeChallengeMethodRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/CodeChallengeMethodRuleTest.php @@ -19,6 +19,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\CodeChallengeMethodRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -36,6 +37,7 @@ class CodeChallengeMethodRuleTest extends TestCase protected Stub $requestParamsResolverStub; protected MockObject $codeChallengeVerifiersRepositoryMock; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception */ @@ -49,6 +51,7 @@ protected function setUp(): void $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->codeChallengeVerifiersRepositoryMock = $this->createMock(CodeChallengeVerifiersRepository::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -75,7 +78,7 @@ public function testCheckRuleRedirectUriDependency(): void { $resultBag = new ResultBag(); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -87,7 +90,7 @@ public function testCheckRuleStateDependency(): void $resultBag = new ResultBag(); $resultBag->add($this->redirectUriResult); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -100,7 +103,7 @@ public function testCheckRuleWithInvalidCodeChallengeMethodThrows(): void $this->codeChallengeVerifiersRepositoryMock->expects($this->once())->method('has') ->with('invalid')->willReturn(false); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -113,7 +116,13 @@ public function testCheckRuleForValidCodeChallengeMethod(): void $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods')->willReturn('plain'); $this->codeChallengeVerifiersRepositoryMock->expects($this->once())->method('has') ->with('plain')->willReturn(true); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertSame('plain', $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/CodeChallengeRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/CodeChallengeRuleTest.php index 1755ea6f..6e84765d 100644 --- a/tests/unit/src/Server/RequestRules/Rules/CodeChallengeRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/CodeChallengeRuleTest.php @@ -19,6 +19,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\CodeChallengeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -39,6 +40,7 @@ class CodeChallengeRuleTest extends TestCase protected Stub $clientStub; protected Result $clientIdResult; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception @@ -54,6 +56,7 @@ protected function setUp(): void $this->clientStub = $this->createStub(ClientEntityInterface::class); $this->clientIdResult = new Result(ClientRule::class, $this->clientStub); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -77,7 +80,7 @@ public function testCheckRuleRedirectUriDependency(): void { $resultBag = new ResultBag(); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -89,7 +92,7 @@ public function testCheckRuleStateDependency(): void $resultBag = new ResultBag(); $resultBag->add($this->redirectUriResult); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -100,7 +103,13 @@ public function testCheckRuleNoCodeReturnsNullForConfidentialClients(): void $this->clientStub->method('isConfidential')->willReturn(true); $resultBag = $this->prepareValidResultBag(); $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods')->willReturn(null); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertNull($result->getValue()); } @@ -113,7 +122,7 @@ public function testCheckRuleInvalidCodeChallengeThrows(): void $resultBag = $this->prepareValidResultBag(); $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods')->willReturn('too-short'); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -125,7 +134,13 @@ public function testCheckRuleForValidCodeChallenge(): void $resultBag = $this->prepareValidResultBag(); $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods')->willReturn($this->codeChallenge); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertSame($this->codeChallenge, $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/IdTokenHintRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/IdTokenHintRuleTest.php index d8137a8e..65de0270 100644 --- a/tests/unit/src/Server/RequestRules/Rules/IdTokenHintRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/IdTokenHintRuleTest.php @@ -14,6 +14,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Result; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\IdTokenHintRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; use SimpleSAML\OpenID\Core; @@ -46,6 +47,7 @@ class IdTokenHintRuleTest extends TestCase protected MockObject $coreMock; protected MockObject $idTokenFactoryMock; protected MockObject $idTokenMock; + protected Stub $responseModeStub; /** * @throws \ReflectionException @@ -70,6 +72,7 @@ protected function setUp(): void $this->idTokenFactoryMock = $this->createMock(IdTokenFactory::class); $this->idTokenMock = $this->createMock(IdToken::class); $this->coreMock->method('idTokenFactory')->willReturn($this->idTokenFactoryMock); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -110,6 +113,8 @@ public function testCheckRuleIsNullWhenParamNotSet(): void $this->requestStub, $this->resultBagStub, $this->loggerServiceStub, + [], + $this->responseModeStub, ) ?? new Result(IdTokenHintRule::class); $this->assertNull($result->getValue()); @@ -122,7 +127,13 @@ public function testCheckRuleThrowsForMalformedIdToken(): void { $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods')->willReturn('malformed'); $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -139,7 +150,13 @@ public function testCheckRuleThrowsForIdTokenWithInvalidSignature(): void ->with('invalid-it-token') ->willReturn($this->idTokenMock); $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -157,7 +174,13 @@ public function testCheckRuleThrowsForIdTokenWithInvalidIssuer(): void $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods') ->willReturn('id-token'); $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -172,7 +195,13 @@ public function testCheckRulePassesForValidIdToken(): void $this->idTokenMock->method('getIssuer')->willReturn(self::$issuer); $this->idTokenFactoryMock->method('fromToken') ->willReturn($this->idTokenMock); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(IdTokenHintRule::class); $this->assertInstanceOf(IdToken::class, $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/PostLogoutRedirectUriRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/PostLogoutRedirectUriRuleTest.php index 7bf7d279..8b5c225a 100644 --- a/tests/unit/src/Server/RequestRules/Rules/PostLogoutRedirectUriRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/PostLogoutRedirectUriRuleTest.php @@ -21,6 +21,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\IdTokenHintRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\PostLogoutRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; use SimpleSAML\OpenID\Core\IdToken; @@ -50,6 +51,7 @@ class PostLogoutRedirectUriRuleTest extends TestCase protected Stub $requestParamsResolverStub; protected Helpers $helpers; protected MockObject $idTokenMock; + protected Stub $responseModeStub; public static function setUpBeforeClass(): void { @@ -83,6 +85,7 @@ protected function setUp(): void $this->helpers = new Helpers(); $this->idTokenMock = $this->createMock(IdToken::class); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -107,7 +110,13 @@ protected function sut( */ public function testCheckRuleReturnsNullIfNoParamSet(): void { - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); $this->assertNull($result->getValue()); @@ -123,7 +132,13 @@ public function testCheckRuleThrowsWhenIdTokenHintNotAvailable(): void $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); } @@ -148,7 +163,13 @@ public function testCheckRuleThrowsWhenAudClaimNotValid(): void $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); } @@ -176,7 +197,13 @@ public function testCheckRuleThrowsWhenClientNotFound(): void $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); } @@ -204,7 +231,13 @@ public function testCheckRuleThrowsWhenPostLogoutRegisteredUriNotRegistered(): v $this->expectException(Throwable::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); } @@ -231,7 +264,13 @@ public function testCheckRuleReturnsForRegisteredPostLogoutRedirectUri(): void new Result(IdTokenHintRule::class, $this->idTokenMock), ); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? (new Result(PostLogoutRedirectUriRule::class)); $this->assertEquals(self::$postLogoutRedirectUri, $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/RedirectUriRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/RedirectUriRuleTest.php index 33a31f4e..68dbd391 100644 --- a/tests/unit/src/Server/RequestRules/Rules/RedirectUriRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/RedirectUriRuleTest.php @@ -17,6 +17,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\ResultBag; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -34,6 +35,7 @@ class RedirectUriRuleTest extends TestCase protected Stub $requestParamsResolverStub; protected Helpers $helpers; protected Stub $moduleConfigStub; + protected Stub $responseModeStub; /** @@ -48,6 +50,7 @@ protected function setUp(): void $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); $this->moduleConfigStub = $this->createStub(ModuleConfig::class); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -73,7 +76,13 @@ protected function sut( public function testCheckRuleClientIdDependency(): void { $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -84,7 +93,13 @@ public function testCheckRuleWithInvalidClientDependancy(): void { $this->resultBag->add(new Result(ClientRule::class, 'invalid')); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -95,7 +110,7 @@ public function testCheckRuleRedirectUriNotSetThrows(): void $resultBag = $this->prepareValidResultBag(); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -107,7 +122,7 @@ public function testCheckRuleDifferentClientRedirectUriThrows(): void $resultBag = $this->prepareValidResultBag(); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -121,7 +136,13 @@ public function testCheckRuleDifferentClientRedirectUriArrayThrows(): void $this->resultBag->add(new Result(ClientRule::class, $this->clientStub)); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } /** @@ -134,7 +155,13 @@ public function testCheckRuleWithValidRedirectUri(): void $resultBag = $this->prepareValidResultBag(); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertSame($this->redirectUri, $result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/RequestObjectRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/RequestObjectRuleTest.php index 45861adb..56f0679a 100644 --- a/tests/unit/src/Server/RequestRules/Rules/RequestObjectRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/RequestObjectRuleTest.php @@ -17,6 +17,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\JwksResolver; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -33,6 +34,7 @@ class RequestObjectRuleTest extends TestCase protected Stub $loggerServiceStub; protected MockObject $jwksResolverMock; protected Helpers $helpers; + protected Stub $responseModeStub; protected function setUp(): void { @@ -49,6 +51,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->jwksResolverMock = $this->createMock(JwksResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -74,7 +77,13 @@ public function testCanCreateInstance(): void public function testRequestParamCanBeAbsent(): void { - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertNull($result); } @@ -85,7 +94,13 @@ public function testUnprotectedRequestParamCanBeUsed(): void $this->requestParamsResolverMock->expects($this->once())->method('parseRequestObjectToken') ->with('token')->willReturn($this->requestObjectMock); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(Result::class, $result); $this->assertIsArray($result->getValue()); $this->assertNotEmpty($result->getValue()); @@ -100,7 +115,13 @@ public function testMissingClientJwksThrows(): void $this->clientStub->expects($this->once())->method('getJwks')->willReturn(null); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } public function testThrowsForInvalidRequestObject(): void @@ -116,7 +137,13 @@ public function testThrowsForInvalidRequestObject(): void ->willReturn(['jwks']); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } public function testReturnsValidRequestObject(): void @@ -132,7 +159,13 @@ public function testReturnsValidRequestObject(): void ->with($this->clientStub) ->willReturn(['jwks']); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertInstanceOf(Result::class, $result); $this->assertIsArray($result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/RequestedClaimsRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/RequestedClaimsRuleTest.php index a17f677d..33caca10 100644 --- a/tests/unit/src/Server/RequestRules/Rules/RequestedClaimsRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/RequestedClaimsRuleTest.php @@ -15,6 +15,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\ResultBag; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestedClaimsRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\ClaimTranslatorExtractor; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -33,6 +34,7 @@ class RequestedClaimsRuleTest extends TestCase protected Stub $requestParamsResolverStub; protected Stub $claimSetEntityFactoryStub; protected Helpers $helpers; + protected Stub $responseModeStub; /** @@ -57,6 +59,7 @@ protected function setUp(): void }); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -80,7 +83,13 @@ protected function sut( */ public function testNoRequestedClaims(): void { - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertNull($result); } @@ -113,7 +122,13 @@ public function testWithClaims(): void $this->requestParamsResolverStub->method('getBasedOnAllowedMethods')->willReturn(json_encode($requestedClaims)); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertNotNull($result); $this->assertEquals($expectedClaims, $result->getValue()); } @@ -132,7 +147,13 @@ public function testOnlyWithNonStandardClaimRequest(): void $requestedClaims = $expectedClaims; $this->requestParamsResolverStub->method('getBasedOnAllowedMethods')->willReturn(json_encode($requestedClaims)); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); $this->assertNotNull($result); $this->assertEquals($expectedClaims, $result->getValue()); } diff --git a/tests/unit/src/Server/RequestRules/Rules/RequiredNonceRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/RequiredNonceRuleTest.php index 6bfbd34e..cb15a571 100644 --- a/tests/unit/src/Server/RequestRules/Rules/RequiredNonceRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/RequiredNonceRuleTest.php @@ -15,6 +15,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredNonceRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -37,6 +38,7 @@ class RequiredNonceRuleTest extends TestCase protected Stub $loggerServiceStub; protected Stub $requestParamsResolverStub; + protected Stub $responseModeStub; /** * @throws \Exception @@ -54,6 +56,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -77,7 +80,7 @@ public function testCheckRuleRedirectUriDependency(): void { $resultBag = new ResultBag(); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -89,7 +92,7 @@ public function testCheckRuleStateDependency(): void $resultBag = new ResultBag(); $resultBag->add($this->redirectUriResult); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -101,7 +104,13 @@ public function testCheckRulePassesWhenNonceIsPresent() $this->requestParamsResolverStub->method('getAsStringBasedOnAllowedMethods') ->willReturn($this->requestQueryParams['nonce']); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(RequiredNonceRule::class, null); $this->assertEquals($this->requestQueryParams['nonce'], $result->getValue()); @@ -114,6 +123,12 @@ public function testCheckRuleThrowsWhenNonceIsNotPresent() { $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } } diff --git a/tests/unit/src/Server/RequestRules/Rules/RequiredOpenIdScopeRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/RequiredOpenIdScopeRuleTest.php index 05668b79..84de98eb 100644 --- a/tests/unit/src/Server/RequestRules/Rules/RequiredOpenIdScopeRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/RequiredOpenIdScopeRuleTest.php @@ -17,6 +17,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -36,6 +37,7 @@ class RequiredOpenIdScopeRuleTest extends TestCase protected Stub $loggerServiceStub; protected Stub $requestParamsResolverStub; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception @@ -53,6 +55,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -76,7 +79,7 @@ public function testCheckRuleRedirectUriDependency(): void { $resultBag = new ResultBag(); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -88,7 +91,7 @@ public function testCheckRuleStateDependency(): void $resultBag = new ResultBag(); $resultBag->add($this->redirectUriResult); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } /** @@ -102,7 +105,13 @@ public function testCheckRulePassesWhenOpenIdScopeIsPresent() $resultBag->add($this->stateResult); $resultBag->add($this->scopeResult); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(RequiredOpenIdScopeRule::class, null); $this->assertTrue($result->getValue()); @@ -123,6 +132,6 @@ public function testCheckRuleThrowsWhenOpenIdScopeIsNotPresent() $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub); + $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, [], $this->responseModeStub); } } diff --git a/tests/unit/src/Server/RequestRules/Rules/ResponseModeRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/ResponseModeRuleTest.php new file mode 100644 index 00000000..78ab747a --- /dev/null +++ b/tests/unit/src/Server/RequestRules/Rules/ResponseModeRuleTest.php @@ -0,0 +1,339 @@ + 'client123', + 'response_type' => 'code', + 'response_mode' => 'query', + ]; + + protected function setUp(): void + { + $this->requestStub = $this->createStub(ServerRequestInterface::class); + $this->loggerServiceStub = $this->createStub(LoggerService::class); + $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); + $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); + + $this->clientStub = $this->createStub(ClientEntityInterface::class); + $this->clientStub->method('getAllowedResponseModes')->willReturn(['query', 'fragment', 'form_post']); + + $this->queryResponseModeStub = $this->createStub(QueryResponseMode::class); + $this->fragmentResponseModeStub = $this->createStub(FragmentResponseMode::class); + $this->formPostResponseModeStub = $this->createStub(FormPostResponseMode::class); + + $this->moduleConfigStub = $this->createStub(ModuleConfig::class); + $this->moduleConfigStub->method('getSupportedResponseModes')->willReturn(['query', 'fragment', 'form_post']); + + $this->resultBag = new ResultBag(); + $this->resultBag->add(new Result(ClientRule::class, $this->clientStub)); + $this->resultBag->add(new Result(ClientRedirectUriRule::class, 'https://example.org/callback')); + $this->resultBag->add(new Result(StateRule::class, 'state123')); + } + + protected function sut( + ?RequestParamsResolver $requestParamsResolver = null, + ?Helpers $helpers = null, + ?ModuleConfig $moduleConfig = null, + ?QueryResponseMode $queryResponseMode = null, + ?FragmentResponseMode $fragmentResponseMode = null, + ?FormPostResponseMode $formPostResponseMode = null, + ): ResponseModeRule { + return new ResponseModeRule( + $requestParamsResolver ?? $this->requestParamsResolverStub, + $helpers ?? $this->helpers, + $moduleConfig ?? $this->moduleConfigStub, + $queryResponseMode ?? $this->queryResponseModeStub, + $fragmentResponseMode ?? $this->fragmentResponseModeStub, + $formPostResponseMode ?? $this->formPostResponseModeStub, + ); + } + + public function testThrowsWhenClientIdMissing(): void + { + $params = $this->requestParams; + unset($params['client_id']); + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(OidcServerException::class); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testReturnsQueryResponseModeWhenExplicitlyRequested(): void + { + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($this->requestParams); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->queryResponseModeStub, $result->getValue()); + } + + public function testReturnsFragmentResponseModeWhenExplicitlyRequested(): void + { + $params = $this->requestParams; + $params['response_mode'] = 'fragment'; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->fragmentResponseModeStub, $result->getValue()); + } + + public function testReturnsFormPostResponseModeWhenExplicitlyRequested(): void + { + $params = $this->requestParams; + $params['response_mode'] = 'form_post'; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->formPostResponseModeStub, $result->getValue()); + } + + public function testDefaultsToQueryWhenResponseModeNotSetAndResponseTypeIsCode(): void + { + $params = $this->requestParams; + unset($params['response_mode']); + $params['response_type'] = 'code'; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->queryResponseModeStub, $result->getValue()); + } + + /** + * @dataProvider tokenResponseTypeProvider + */ + public function testDefaultsToFragmentWhenResponseModeNotSetAndResponseTypeContainsToken( + string $responseType, + ): void { + $params = $this->requestParams; + unset($params['response_mode']); + $params['response_type'] = $responseType; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->fragmentResponseModeStub, $result->getValue()); + } + + public static function tokenResponseTypeProvider(): array + { + return [ + 'token' => ['token'], + 'id_token token' => ['id_token token'], + 'code token' => ['code token'], + 'code id_token token' => ['code id_token token'], + ]; + } + + public function testDefaultsToQueryWhenResponseModeAndResponseTypeNotSet(): void + { + $params = ['client_id' => 'client123']; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame($this->queryResponseModeStub, $result->getValue()); + } + + public function testThrowsOnInvalidResponseMode(): void + { + $params = $this->requestParams; + $params['response_mode'] = 'invalid'; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(OidcServerException::class); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testThrowsWhenResponseModeNotAllowedByClient(): void + { + $this->clientStub = $this->createStub(ClientEntityInterface::class); + $this->clientStub->method('getAllowedResponseModes')->willReturn(['query']); + + $this->resultBag = new ResultBag(); + $this->resultBag->add(new Result(ClientRule::class, $this->clientStub)); + $this->resultBag->add(new Result(ClientRedirectUriRule::class, 'https://example.org/callback')); + $this->resultBag->add(new Result(StateRule::class, 'state123')); + + $params = $this->requestParams; + $params['response_mode'] = 'fragment'; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(OidcServerException::class); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testThrowsWhenClientRuleResultMissing(): void + { + $resultBag = new ResultBag(); + + $params = $this->requestParams; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(LogicException::class); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testThrowsWhenRedirectUriResultMissing(): void + { + $resultBag = new ResultBag(); + $resultBag->add(new Result(ClientRule::class, $this->clientStub)); + + $params = $this->requestParams; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(LogicException::class); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testThrowsWhenStateResultMissing(): void + { + $resultBag = new ResultBag(); + $resultBag->add(new Result(ClientRule::class, $this->clientStub)); + $resultBag->add(new Result(ClientRedirectUriRule::class, 'https://example.org/callback')); + + $params = $this->requestParams; + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); + + $this->expectException(LogicException::class); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + } + + public function testResultKeyMatchesRuleClass(): void + { + $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($this->requestParams); + + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); + + $this->assertNotNull($result); + $this->assertSame(ResponseModeRule::class, $result->getKey()); + } +} diff --git a/tests/unit/src/Server/RequestRules/Rules/ResponseTypeRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/ResponseTypeRuleTest.php index ecd2d1e6..5b1c5c91 100644 --- a/tests/unit/src/Server/RequestRules/Rules/ResponseTypeRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/ResponseTypeRuleTest.php @@ -12,6 +12,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Result; use SimpleSAML\Module\oidc\Server\RequestRules\ResultBag; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -49,6 +50,7 @@ class ResponseTypeRuleTest extends TestCase private ResultBag $resultBag; protected Stub $loggerServiceStub; + protected Stub $responseModeStub; /** * @throws \Exception @@ -61,6 +63,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -84,7 +87,13 @@ public function testResponseTypeRuleTest($responseType) { $this->requestParams['response_type'] = $responseType; $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($this->requestParams); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(ResponseTypeRule::class, null); $this->assertSame($responseType, $result->getValue()); } @@ -103,6 +112,12 @@ public function testResponseTypeRuleThrowsWithNoResponseTypeParamTest() unset($params['response_type']); $this->requestParamsResolverStub->method('getAllBasedOnAllowedMethods')->willReturn($params); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $this->resultBag, $this->loggerServiceStub); + $this->sut()->checkRule( + $this->requestStub, + $this->resultBag, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ); } } diff --git a/tests/unit/src/Server/RequestRules/Rules/ScopeOfflineAccessRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/ScopeOfflineAccessRuleTest.php index e9bddd38..a640af98 100644 --- a/tests/unit/src/Server/RequestRules/Rules/ScopeOfflineAccessRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/ScopeOfflineAccessRuleTest.php @@ -17,6 +17,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeOfflineAccessRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -39,6 +40,7 @@ class ScopeOfflineAccessRuleTest extends TestCase protected Stub $openIdConfigurationStub; protected Stub $requestParamsResolverStub; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception @@ -69,6 +71,7 @@ protected function setUp(): void $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -115,7 +118,13 @@ public function testReturnsFalseWhenOfflineAccessScopeNotPresent(): void $this->moduleConfigStub->method('config') ->willReturn($this->openIdConfigurationStub); - $result = $this->sut()->checkRule($this->serverRequestStub, $this->resultBagMock, $this->loggerServiceMock); + $result = $this->sut()->checkRule( + $this->serverRequestStub, + $this->resultBagMock, + $this->loggerServiceMock, + [], + $this->responseModeStub, + ); $this->assertNotNull($result); $this->assertFalse($result->getValue()); @@ -146,7 +155,13 @@ public function testThrowsWhenClientDoesntHaveOfflineAccessScopeRegistered(): vo $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->serverRequestStub, $this->resultBagMock, $this->loggerServiceMock); + $this->sut()->checkRule( + $this->serverRequestStub, + $this->resultBagMock, + $this->loggerServiceMock, + [], + $this->responseModeStub, + ); } /** @@ -173,7 +188,13 @@ public function testReturnsTrueWhenClientDoesHaveOfflineAccessScopeRegistered(): $this->moduleConfigStub->method('config') ->willReturn($this->openIdConfigurationStub); - $result = $this->sut()->checkRule($this->serverRequestStub, $this->resultBagMock, $this->loggerServiceMock); + $result = $this->sut()->checkRule( + $this->serverRequestStub, + $this->resultBagMock, + $this->loggerServiceMock, + [], + $this->responseModeStub, + ); $this->assertNotNull($result); $this->assertTrue($result->getValue()); diff --git a/tests/unit/src/Server/RequestRules/Rules/ScopeRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/ScopeRuleTest.php index 1916686e..fa144662 100644 --- a/tests/unit/src/Server/RequestRules/Rules/ScopeRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/ScopeRuleTest.php @@ -20,6 +20,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientRedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -47,6 +48,7 @@ class ScopeRuleTest extends TestCase protected Stub $requestParamsResolverStub; protected Stub $helpersStub; protected Stub $strHelperMock; + protected Stub $responseModeStub; /** * @throws \Exception @@ -67,6 +69,7 @@ protected function setUp(): void $this->helpersStub = $this->createStub(Helpers::class); $this->strHelperMock = $this->createMock(Helpers\Str::class); $this->helpersStub->method('str')->willReturn($this->strHelperMock); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -98,7 +101,13 @@ public function testCheckRuleRedirectUriDependency(): void { $resultBag = new ResultBag(); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, $this->data); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + $this->data, + $this->responseModeStub, + ); } /** @@ -110,7 +119,13 @@ public function testCheckRuleStateDependency(): void $resultBag = new ResultBag(); $resultBag->add($this->redirectUriResult); $this->expectException(LogicException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, $this->data); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + $this->data, + $this->responseModeStub, + ); } /** @@ -134,7 +149,13 @@ public function testValidScopes(): void ), ); - $result = $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, $this->data); + $result = $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + $this->data, + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertIsArray($result->getValue()); $this->assertSame($this->scopeEntities['openid'], $result->getValue()[0]); @@ -161,7 +182,13 @@ public function testInvalidScopeThrows(): void ); $this->expectException(OidcServerException::class); - $this->sut()->checkRule($this->requestStub, $resultBag, $this->loggerServiceStub, $this->data); + $this->sut()->checkRule( + $this->requestStub, + $resultBag, + $this->loggerServiceStub, + $this->data, + $this->responseModeStub, + ); } protected function prepareValidResultBag(): ResultBag diff --git a/tests/unit/src/Server/RequestRules/Rules/StateRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/StateRuleTest.php index aa38de1b..336ca8bb 100644 --- a/tests/unit/src/Server/RequestRules/Rules/StateRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/StateRuleTest.php @@ -11,6 +11,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultInterface; use SimpleSAML\Module\oidc\Server\RequestRules\ResultBag; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -23,6 +24,7 @@ class StateRuleTest extends TestCase protected Stub $loggerServiceStub; protected Stub $requestParamsResolverStub; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception @@ -32,6 +34,7 @@ public function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -65,7 +68,13 @@ public function testCheckRuleHasValue(): void $resultBag = new ResultBag(); $data = []; - $result = $this->sut()->checkRule($request, $resultBag, $this->loggerServiceStub, $data); + $result = $this->sut()->checkRule( + $request, + $resultBag, + $this->loggerServiceStub, + $data, + $this->responseModeStub, + ); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertSame($value, $result->getValue()); @@ -85,6 +94,8 @@ public function testCheckRulePostMethod(): void $request, $resultBag, $this->loggerServiceStub, + [], + $this->responseModeStub, ); $this->assertInstanceOf(ResultInterface::class, $result); diff --git a/tests/unit/src/Server/RequestRules/Rules/UiLocalesRuleTest.php b/tests/unit/src/Server/RequestRules/Rules/UiLocalesRuleTest.php index a4eefd8c..749525c3 100644 --- a/tests/unit/src/Server/RequestRules/Rules/UiLocalesRuleTest.php +++ b/tests/unit/src/Server/RequestRules/Rules/UiLocalesRuleTest.php @@ -11,6 +11,7 @@ use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Result; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\UiLocalesRule; +use SimpleSAML\Module\oidc\Server\ResponseModes\ResponseModeInterface; use SimpleSAML\Module\oidc\Services\LoggerService; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; @@ -24,6 +25,7 @@ class UiLocalesRuleTest extends TestCase protected Stub $loggerServiceStub; protected Stub $requestParamsResolverStub; protected Helpers $helpers; + protected Stub $responseModeStub; /** * @throws \Exception @@ -37,6 +39,7 @@ protected function setUp(): void $this->loggerServiceStub = $this->createStub(LoggerService::class); $this->requestParamsResolverStub = $this->createStub(RequestParamsResolver::class); $this->helpers = new Helpers(); + $this->responseModeStub = $this->createStub(ResponseModeInterface::class); } protected function sut( @@ -59,7 +62,13 @@ public function testCheckRuleReturnsResultWhenParamSet() { $this->requestParamsResolverStub->method('getBasedOnAllowedMethods')->willReturn('en'); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(UiLocalesRule::class); $this->assertEquals('en', $result->getValue()); @@ -72,7 +81,13 @@ public function testCheckRuleReturnsNullWhenParamNotSet() { $this->requestStub->method('getQueryParams')->willReturn([]); - $result = $this->sut()->checkRule($this->requestStub, $this->resultBagStub, $this->loggerServiceStub) ?? + $result = $this->sut()->checkRule( + $this->requestStub, + $this->resultBagStub, + $this->loggerServiceStub, + [], + $this->responseModeStub, + ) ?? new Result(UiLocalesRule::class); $this->assertNull($result->getValue()); diff --git a/tests/unit/src/Server/ResponseModes/FormPostResponseModeTest.php b/tests/unit/src/Server/ResponseModes/FormPostResponseModeTest.php new file mode 100644 index 00000000..f091d9c2 --- /dev/null +++ b/tests/unit/src/Server/ResponseModes/FormPostResponseModeTest.php @@ -0,0 +1,69 @@ + 'simplesaml/', + 'module.enable' => ['oidc' => true], + ], '', 'simplesaml'); + + $template = new Template($config, 'oidc:formpost.twig'); + + $templateFactory = $this->createMock(TemplateFactory::class); + $templateFactory->method('build')->willReturnCallback( + function ( + string $templateName, + array $data = [], + ?string $activeHrefPath = null, + ?bool $includeDefaultMenuItems = null, + ?bool $showMenu = null, + ?bool $showModuleName = null, + ?bool $showSubPageTitle = null, + ) use ($template) { + $template->data = array_merge([ + 'showMenu' => $showMenu ?? false, + 'showModuleName' => $showModuleName ?? false, + 'showSubpageTitle' => $showSubPageTitle ?? false, + ], $data); + return $template; + }, + ); + + $this->sut = new FormPostResponseMode($templateFactory); + } + + public function testBuildResponseReturnsHtmlWithFormPost(): void + { + $result = $this->sut->buildResponse( + 'https://example.org/callback', + ['code' => 'abc123', 'state' => 'xyz'], + ); + + $this->assertInstanceOf(HtmlResponse::class, $result); + + $body = (string) $result->generateHttpResponse(new Response())->getBody(); + $this->assertStringContainsString('https://example.org/callback', $body); + $this->assertStringContainsString('abc123', $body); + $this->assertStringContainsString('xyz', $body); + $this->assertMatchesRegularExpression('/method=["\']post["\']/i', $body); + } +} diff --git a/tests/unit/src/Services/OpMetadataServiceTest.php b/tests/unit/src/Services/OpMetadataServiceTest.php index f354b630..e6024104 100644 --- a/tests/unit/src/Services/OpMetadataServiceTest.php +++ b/tests/unit/src/Services/OpMetadataServiceTest.php @@ -53,6 +53,7 @@ public function setUp(): void return $paths[$path] ?? null; }); $this->moduleConfigMock->method('getAcrValuesSupported')->willReturn(['1']); + $this->moduleConfigMock->method('getSupportedResponseModes')->willReturn(['query', 'fragment', 'form_post']); $this->claimTranslatorExtractorMock = $this->createMock(ClaimTranslatorExtractor::class); @@ -142,6 +143,7 @@ public function testItReturnsExpectedMetadata(): void 'acr_values_supported' => ['1'], 'backchannel_logout_supported' => true, 'backchannel_logout_session_supported' => true, + 'response_modes_supported' => ['query', 'fragment', 'form_post'], ], $this->sut()->getMetadata(), );