Skip to content

Commit 402645a

Browse files
committed
WIP
1 parent 0890c85 commit 402645a

9 files changed

Lines changed: 91 additions & 77 deletions

File tree

‎docs/9-oidc-dcr-client-metadata.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ contract for what was honored.
3232
| `redirect_uris` | Honored | Honored | Required; scheme required, fragment rejected. |
3333
| `client_name` | Honored | Honored | Defaults to client_id. |
3434
| `scope` | Honored | Honored | DCR default = `OPTION_DCR_DEFAULT_SCOPES`. |
35-
| `grant_types` | **Honored** (persist + echo + enforce) | Honored | DCR default `["authorization_code"]` stored at registration. Unsupported values rejected (`SupportedClientMetadata`). Enforced for the code grant (presence + non-empty); refresh grant exempt (see note). |
35+
| `grant_types` | **Honored** (persist + echo + enforce) | Honored | DCR default `["authorization_code"]` stored at registration. Unsupported values rejected (`ModuleConfig`). Enforced for the code grant (presence + non-empty); refresh grant exempt (see note). |
3636
| `response_types` | **Honored** (persist + echo + enforce) | Honored | DCR default `["code"]` stored at registration. Unsupported values rejected. Enforced at the authorization endpoint (presence + non-empty). |
3737
| `token_endpoint_auth_method` | **Honored** (persist + echo + enforce) | Honored | DCR default `client_secret_basic` (or `none` for public) stored at registration. Unsupported values rejected. Enforced at the token endpoint (presence). Also the primary signal for the client type (see below): `none` ⇒ public, any real method ⇒ confidential. |
3838
| `jwks` | Honored | Honored | Stored (column). |

‎src/Forms/ClientForm.php‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use SimpleSAML\Module\oidc\Helpers;
2525
use SimpleSAML\Module\oidc\ModuleConfig;
2626
use SimpleSAML\Module\oidc\Utils\ResponseTypeGrantTypeCorrespondence;
27-
use SimpleSAML\Module\oidc\Utils\SupportedClientMetadata;
2827
use SimpleSAML\OpenID\Codebooks\ApplicationTypesEnum;
2928
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
3029
use SimpleSAML\OpenID\Codebooks\ClientRegistrationTypesEnum;
@@ -854,7 +853,7 @@ protected function getAllowedResponseModesValues(): array
854853
*/
855854
protected function getSupportedGrantTypes(): array
856855
{
857-
$supported = SupportedClientMetadata::grantTypes();
856+
$supported = $this->moduleConfig->getSupportedGrantTypes();
858857

859858
return array_combine($supported, $supported);
860859
}
@@ -867,7 +866,7 @@ protected function getSupportedGrantTypes(): array
867866
*/
868867
protected function getSupportedResponseTypes(): array
869868
{
870-
$supported = SupportedClientMetadata::responseTypes();
869+
$supported = $this->moduleConfig->getSupportedResponseTypes();
871870

872871
return array_combine($supported, $supported);
873872
}
@@ -879,7 +878,7 @@ protected function getSupportedResponseTypes(): array
879878
*/
880879
protected function getSupportedTokenEndpointAuthMethods(): array
881880
{
882-
$supported = SupportedClientMetadata::tokenEndpointAuthMethods();
881+
$supported = $this->moduleConfig->getSupportedTokenEndpointAuthMethods();
883882

884883
return array_combine($supported, $supported);
885884
}

‎src/ModuleConfig.php‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@
2727
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmBag;
2828
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
2929
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
30+
use SimpleSAML\OpenID\Codebooks\GrantTypesEnum;
3031
use SimpleSAML\OpenID\Codebooks\ResponseModesEnum;
32+
use SimpleSAML\OpenID\Codebooks\ResponseTypesEnum;
3133
use SimpleSAML\OpenID\Codebooks\ScopesEnum;
34+
use SimpleSAML\OpenID\Codebooks\TokenEndpointAuthMethodsEnum;
3235
use SimpleSAML\OpenID\Codebooks\TrustMarkStatusEndpointUsagePolicyEnum;
3336
use SimpleSAML\OpenID\Serializers\JwsSerializerBag;
3437
use SimpleSAML\OpenID\Serializers\JwsSerializerEnum;
@@ -488,6 +491,57 @@ public function getSupportedResponseModes(): array
488491
];
489492
}
490493

494+
/**
495+
* Response types a client may be registered to use.
496+
*
497+
* Shared by OP discovery metadata, the dynamic client registration validator,
498+
* and the client admin form so that the advertised, accepted, and admin-selectable
499+
* sets cannot drift apart.
500+
*
501+
* @return string[]
502+
*/
503+
public function getSupportedResponseTypes(): array
504+
{
505+
return [
506+
ResponseTypesEnum::Code->value,
507+
ResponseTypesEnum::IdToken->value,
508+
ResponseTypesEnum::IdTokenToken->value,
509+
];
510+
}
511+
512+
/**
513+
* Grant types a client may be registered to use.
514+
*
515+
* Note: the discovery `grant_types_supported` may advertise additional grant types
516+
* that are not registered per client (e.g. the VCI pre-authorized_code grant);
517+
* that extension is applied by OpMetadataService on top of these values.
518+
*
519+
* @return string[]
520+
*/
521+
public function getSupportedGrantTypes(): array
522+
{
523+
return [
524+
GrantTypesEnum::AuthorizationCode->value,
525+
GrantTypesEnum::Implicit->value,
526+
GrantTypesEnum::RefreshToken->value,
527+
];
528+
}
529+
530+
/**
531+
* Token endpoint authentication methods a client may be registered to use.
532+
*
533+
* @return string[]
534+
*/
535+
public function getSupportedTokenEndpointAuthMethods(): array
536+
{
537+
return [
538+
TokenEndpointAuthMethodsEnum::ClientSecretBasic->value,
539+
TokenEndpointAuthMethodsEnum::ClientSecretPost->value,
540+
TokenEndpointAuthMethodsEnum::PrivateKeyJwt->value,
541+
TokenEndpointAuthMethodsEnum::None->value,
542+
];
543+
}
544+
491545
/**
492546
* @throws ConfigurationError
493547
* @return non-empty-array

‎src/Server/Registration/ClientMetadataValidator.php‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use SimpleSAML\Module\oidc\ModuleConfig;
88
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
99
use SimpleSAML\Module\oidc\Utils\ResponseTypeGrantTypeCorrespondence;
10-
use SimpleSAML\Module\oidc\Utils\SupportedClientMetadata;
1110
use SimpleSAML\OpenID\Codebooks\ApplicationTypesEnum;
1211
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
1312
use SimpleSAML\OpenID\Codebooks\GrantTypesEnum;
@@ -242,7 +241,7 @@ private function validateApplicationType(array $metadata): void
242241

243242
/**
244243
* Reject registration of grant_types / response_types / token_endpoint_auth_method values that this OP does not
245-
* support (the same sets it advertises in discovery via SupportedClientMetadata). Without this, a client could
244+
* support (the same sets it advertises in discovery via ModuleConfig). Without this, a client could
246245
* register values that can never be honored and would fail at authentication/token time.
247246
*
248247
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
@@ -252,24 +251,24 @@ private function validateRegisterableProtocolValues(array $metadata): void
252251
$this->rejectUnsupportedArrayValues(
253252
$metadata,
254253
ClaimsEnum::GrantTypes->value,
255-
SupportedClientMetadata::grantTypes(),
254+
$this->moduleConfig->getSupportedGrantTypes(),
256255
);
257256
$this->rejectUnsupportedArrayValues(
258257
$metadata,
259258
ClaimsEnum::ResponseTypes->value,
260-
SupportedClientMetadata::responseTypes(),
259+
$this->moduleConfig->getSupportedResponseTypes(),
261260
);
262261

263262
if (array_key_exists(ClaimsEnum::TokenEndpointAuthMethod->value, $metadata)) {
264263
/** @var mixed $authMethod */
265264
$authMethod = $metadata[ClaimsEnum::TokenEndpointAuthMethod->value];
266265
if (
267266
!is_string($authMethod) ||
268-
!in_array($authMethod, SupportedClientMetadata::tokenEndpointAuthMethods(), true)
267+
!in_array($authMethod, $this->moduleConfig->getSupportedTokenEndpointAuthMethods(), true)
269268
) {
270269
throw OidcServerException::invalidClientMetadata(
271270
'Unsupported token_endpoint_auth_method. Supported: ' .
272-
implode(', ', SupportedClientMetadata::tokenEndpointAuthMethods()) . '.',
271+
implode(', ', $this->moduleConfig->getSupportedTokenEndpointAuthMethods()) . '.',
273272
);
274273
}
275274
}

‎src/Services/OpMetadataService.php‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use SimpleSAML\Module\oidc\ModuleConfig;
99
use SimpleSAML\Module\oidc\Utils\ClaimTranslatorExtractor;
1010
use SimpleSAML\Module\oidc\Utils\Routes;
11-
use SimpleSAML\Module\oidc\Utils\SupportedClientMetadata;
1211
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
1312
use SimpleSAML\OpenID\Codebooks\GrantTypesEnum;
1413

@@ -67,12 +66,12 @@ private function initMetadata(): void
6766
$this->routes->getModuleUrl(RoutesEnum::Registration->value);
6867
}
6968
$this->metadata[ClaimsEnum::ScopesSupported->value] = array_keys($this->moduleConfig->getScopes());
70-
$this->metadata[ClaimsEnum::ResponseTypesSupported->value] = SupportedClientMetadata::responseTypes();
69+
$this->metadata[ClaimsEnum::ResponseTypesSupported->value] = $this->moduleConfig->getSupportedResponseTypes();
7170
$this->metadata[ClaimsEnum::SubjectTypesSupported->value] = ['public'];
7271
$this->metadata[ClaimsEnum::IdTokenSigningAlgValuesSupported->value] = $protocolSignatureAlgorithmNames;
7372
$this->metadata[ClaimsEnum::CodeChallengeMethodsSupported->value] = ['plain', 'S256'];
7473
$this->metadata[ClaimsEnum::TokenEndpointAuthMethodsSupported->value] =
75-
SupportedClientMetadata::tokenEndpointAuthMethods();
74+
$this->moduleConfig->getSupportedTokenEndpointAuthMethods();
7675
$this->metadata[ClaimsEnum::TokenEndpointAuthSigningAlgValuesSupported->value] =
7776
$supportedSignatureAlgorithmNames;
7877
$this->metadata[ClaimsEnum::RequestParameterSupported->value] = true;
@@ -90,10 +89,10 @@ private function initMetadata(): void
9089
$this->metadata[ClaimsEnum::RequirePushedAuthorizationRequests->value] =
9190
$this->moduleConfig->getRequirePushedAuthorizationRequests();
9291

93-
$grantTypesSupported = SupportedClientMetadata::grantTypes();
92+
$grantTypesSupported = $this->moduleConfig->getSupportedGrantTypes();
9493
if ($this->moduleConfig->getVciEnabled()) {
9594
// The VCI pre-authorized_code grant is an OP capability advertised in discovery, but it is not a
96-
// per-client registerable grant type (it is not part of SupportedClientMetadata).
95+
// per-client registerable grant type.
9796
$grantTypesSupported[] = GrantTypesEnum::PreAuthorizedCode->value;
9897
}
9998
$this->metadata[ClaimsEnum::GrantTypesSupported->value] = $grantTypesSupported;

‎src/Utils/SupportedClientMetadata.php‎

Lines changed: 0 additions & 61 deletions
This file was deleted.

‎tests/unit/src/Forms/ClientFormTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public function setUp(): void
4949
$this->moduleConfigMock->method('getSupportedResponseModes')->willReturn(['query', 'fragment', 'form_post']);
5050
$this->moduleConfigMock->method('getAcrValuesSupported')
5151
->willReturn(['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze']);
52+
$this->moduleConfigMock->method('getSupportedResponseTypes')
53+
->willReturn(['code', 'id_token', 'id_token token']);
54+
$this->moduleConfigMock->method('getSupportedGrantTypes')
55+
->willReturn(['authorization_code', 'implicit', 'refresh_token']);
56+
$this->moduleConfigMock->method('getSupportedTokenEndpointAuthMethods')
57+
->willReturn(['client_secret_basic', 'client_secret_post', 'private_key_jwt', 'none']);
5258

5359
$signatureKeyPairBagMock = $this->createMock(SignatureKeyPairBag::class);
5460
$signatureKeyPairBagMock->method('getAllAlgorithmNamesUnique')->willReturn(['RS256', 'ES256']);

‎tests/unit/src/Server/Registration/ClientMetadataValidatorTest.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ protected function setUp(): void
2323
$this->moduleConfigMock->method('getDcrImpersonationProtectionEnabled')->willReturn(true);
2424
// Default: the OP advertises a single supported ACR.
2525
$this->moduleConfigMock->method('getAcrValuesSupported')->willReturn(['urn:mace:incommon:iap:silver']);
26+
$this->moduleConfigMock->method('getSupportedResponseTypes')
27+
->willReturn(['code', 'id_token', 'id_token token']);
28+
$this->moduleConfigMock->method('getSupportedGrantTypes')
29+
->willReturn(['authorization_code', 'implicit', 'refresh_token']);
30+
$this->moduleConfigMock->method('getSupportedTokenEndpointAuthMethods')
31+
->willReturn(['client_secret_basic', 'client_secret_post', 'private_key_jwt', 'none']);
2632
}
2733

2834
protected function sut(): ClientMetadataValidator
@@ -384,6 +390,12 @@ public function testImpersonationProtectionCanBeDisabled(): void
384390
{
385391
$moduleConfigMock = $this->createMock(ModuleConfig::class);
386392
$moduleConfigMock->method('getDcrImpersonationProtectionEnabled')->willReturn(false);
393+
$moduleConfigMock->method('getSupportedResponseTypes')
394+
->willReturn(['code', 'id_token', 'id_token token']);
395+
$moduleConfigMock->method('getSupportedGrantTypes')
396+
->willReturn(['authorization_code', 'implicit', 'refresh_token']);
397+
$moduleConfigMock->method('getSupportedTokenEndpointAuthMethods')
398+
->willReturn(['client_secret_basic', 'client_secret_post', 'private_key_jwt', 'none']);
387399

388400
$metadata = [
389401
'redirect_uris' => ['https://client.example.org/cb'],

‎tests/unit/src/Services/OpMetadataServiceTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ public function setUp(): void
5959
});
6060
$this->moduleConfigMock->method('getAcrValuesSupported')->willReturn(['1']);
6161
$this->moduleConfigMock->method('getSupportedResponseModes')->willReturn(['query', 'fragment', 'form_post']);
62+
$this->moduleConfigMock->method('getSupportedResponseTypes')
63+
->willReturn(['code', 'id_token', 'id_token token']);
64+
$this->moduleConfigMock->method('getSupportedGrantTypes')
65+
->willReturn(['authorization_code', 'implicit', 'refresh_token']);
66+
$this->moduleConfigMock->method('getSupportedTokenEndpointAuthMethods')
67+
->willReturn(['client_secret_basic', 'client_secret_post', 'private_key_jwt', 'none']);
6268

6369
$this->claimTranslatorExtractorMock = $this->createMock(ClaimTranslatorExtractor::class);
6470

0 commit comments

Comments
 (0)