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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ jobs:
- 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
- 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: |
Expand Down
1 change: 1 addition & 0 deletions public/assets/js/src/formpost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
document.forms[0].submit();
6 changes: 6 additions & 0 deletions routing/services/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
4 changes: 4 additions & 0 deletions src/Controllers/Admin/ClientController.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ public function edit(Request $request): Response

$clientData = $originalClient->toArray();
$clientData['allowed_origin'] = $clientAllowedOrigins;
$clientData['response_modes_allowed'] = $originalClient->getAllowedResponseModes();

// Handle extra metadata

Expand Down Expand Up @@ -358,6 +359,9 @@ protected function buildClientEntityFromFormData(
ClaimsEnum::IdTokenSignedResponseAlg->value => $idTokenSignedResponseAlg,
];

$allowedResponseModes = is_array($data['response_modes_allowed']) ? $data['response_modes_allowed'] : [];
$extraMetadata['allowed_response_modes'] = $allowedResponseModes;

return $this->clientEntityFactory->fromData(
$identifier,
$secret,
Expand Down
17 changes: 17 additions & 0 deletions src/Entities/ClientEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -388,4 +388,21 @@ public function getIdTokenSignedResponseAlg(): ?string

return $idTokenSignedResponseAlg;
}

public function getAllowedResponseModes(): array
{
if (!is_array($this->extraMetadata)) {
// Default to allowing all response modes
return ['query', 'fragment', 'form_post'];
}

$allowedResponseModes = $this->extraMetadata['allowed_response_modes'] ?? null;

if (!is_array($allowedResponseModes)) {
// Default to allowing all response modes
return ['query', 'fragment', 'form_post'];
}

return $allowedResponseModes;
}
}
1 change: 1 addition & 0 deletions src/Entities/Interfaces/ClientEntityInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ public function isGeneric(): bool;

public function getExtraMetadata(): array;
public function getIdTokenSignedResponseAlg(): ?string;
public function getAllowedResponseModes(): array;
}
1 change: 0 additions & 1 deletion src/Factories/Grant/ImplicitGrantFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public function build(): ImplicitGrant
$this->accessTokenRepository,
$this->requestRulesManager,
$this->requestParamsResolver,
'#',
$this->accessTokenEntityFactory,
);
}
Expand Down
14 changes: 14 additions & 0 deletions src/Factories/RequestRulesManagerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
) {
}

Expand Down Expand Up @@ -107,6 +114,13 @@ 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->queryResponseMode,
$this->fragmentResponseMode,
$this->formPostResponseMode,
),
new PromptRule(
$this->requestParamsResolver,
$this->helpers,
Expand Down
50 changes: 50 additions & 0 deletions src/Forms/ClientForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ public function getValues(string|object|bool|null $returnType = null, ?array $co
$values[ClaimsEnum::IdTokenSignedResponseAlg->value] = empty($idTokenSignedResponseAlg) ?
null : $idTokenSignedResponseAlg;

$responseModesAllowed = is_array($values['response_modes_allowed']) ? $values['response_modes_allowed'] : [];
$values['response_modes_allowed'] = array_intersect(
$responseModesAllowed,
array_keys($this->getAllowedResponseModesValues()),
);

return $values;
}

Expand Down Expand Up @@ -336,6 +342,9 @@ public function setDefaults(object|array $data, bool $erase = false): static
$data['auth_source'] = null;
}

$data['response_modes_allowed'] = is_array($data['response_modes_allowed']) ?
$data['response_modes_allowed'] : [];

parent::setDefaults($data, $erase);

return $this;
Expand All @@ -354,6 +363,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(...);
Expand Down Expand Up @@ -423,6 +433,46 @@ protected function buildForm(): void
->setHtmlAttribute('class', 'full-width')
->setItems(['RS256'], false)
->setPrompt(Translate::noop('-'));

$this->addMultiSelect(
'response_modes_allowed',
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['response_modes_allowed'] ?? 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
{
return [
'query' => 'query',
'fragment' => 'fragment',
'form_post' => 'form_post',
];
}

/**
Expand Down
11 changes: 8 additions & 3 deletions src/Server/AuthorizationServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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) {
Expand Down
Loading
Loading