Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
path: '**/*.md'
check_filenames: true
ignore_words_list: tekst
skip: './.git, ./specifications, ./vendor'
skip: ./.git,./vendor,./specifications

build:
name: Build documentation
Expand Down
3,927 changes: 3,927 additions & 0 deletions specifications/did-1.0.md

Large diffs are not rendered by default.

10,308 changes: 10,308 additions & 0 deletions specifications/json-ld11.md

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions specifications/update-specs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ then
fi

URLS=(
# OpenID specifications
"https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html"
"https://openid.net/specs/openid-federation-1_0.html"
"https://www.w3.org/TR/vc-data-model-2.0/"
"https://www.w3.org/TR/vc-jose-cose/"
"https://fidescommunity.github.io/DIIP/"
"https://openid.net/specs/openid-connect-core-1_0.html"
"https://openid.net/specs/openid-connect-discovery-1_0.html"
"https://openid.net/specs/openid-connect-rpinitiated-1_0.html"
"https://openid.net/specs/openid-connect-frontchannel-1_0.html"
"https://openid.net/specs/openid-connect-backchannel-1_0.html"
# W3C specifications
"https://www.w3.org/TR/vc-data-model-2.0/"
"https://www.w3.org/TR/vc-jose-cose/"
"https://www.w3.org/TR/vc-imp-guide/"
"https://www.w3.org/TR/did-1.0/"
"https://www.w3.org/TR/vc-data-integrity/"
"https://www.w3.org/TR/json-ld11/"
# DIIP specifications
"https://fidescommunity.github.io/DIIP/"
)

# For each of the specifications, fetch the content from the URL and save it
Expand Down
1,833 changes: 1,833 additions & 0 deletions specifications/vc-data-integrity.md

Large diffs are not rendered by default.

948 changes: 948 additions & 0 deletions specifications/vc-imp-guide.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/Codebooks/AtContextsEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
enum AtContextsEnum: string
{
case W3Org2018CredentialsV1 = 'https://www.w3.org/2018/credentials/v1';

case W3OrgNsCredentialsV2 = 'https://www.w3.org/ns/credentials/v2';
}
10 changes: 10 additions & 0 deletions src/Codebooks/ClaimsEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ enum ClaimsEnum: string
// @context
case AtContext = '@context';

case AtDirection = '@direction';

case AtLanguage = '@language';

// @type
case AtType = '@type';

case AtValue = '@value';

// Authentication Context Class Reference
case Acr = 'acr';

Expand Down Expand Up @@ -447,6 +453,10 @@ enum ClaimsEnum: string

case UserinfoSigningAlgValuesSupported = 'userinfo_signing_alg_values_supported';

case ValidFrom = 'validFrom';

case ValidUntil = 'validUntil';

// VerifiableCredential
case Vc = 'vc';

Expand Down
20 changes: 18 additions & 2 deletions src/Codebooks/ContentTypesEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,27 @@ enum ContentTypesEnum: string
{
case ApplicationDcSdJwt = 'application/dc+sd-jwt';

case ApplicationJwt = 'application/jwt';

case ApplicationEntityStatementJwt = 'application/entity-statement+jwt';

case ApplicationJwt = 'application/jwt';

case ApplicationTrustMarkJwt = 'application/trust-mark+jwt';

case ApplicationTrustMarkStatusResponseJwt = 'application/trust-mark-status-response+jwt';

case ApplicationVc = 'application/vc';

case ApplicationVcCose = 'application/vc+cose';

case ApplicationVcJwt = 'application/vc+jwt';

case ApplicationVcSdJwt = 'application/vc+sd-jwt';

case ApplicationVp = 'application/vp';

case ApplicationVpCose = 'application/vp+cose';

case ApplicationVpJwt = 'application/vp+jwt';

case ApplicationVpSdJwt = 'application/vp+sd-jwt';
}
7 changes: 4 additions & 3 deletions src/Codebooks/CredentialFormatIdentifiersEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ enum CredentialFormatIdentifiersEnum: string
// VC signed as a JWT, not using JSON-LD
case JwtVcJson = 'jwt_vc_json';

// VC Data Model v2.0 secured with SD-JWT
// https://www.w3.org/TR/vc-jose-cose/#with-sd-jwt
case VcSdJwt = 'vc+sd-jwt';

// VC signed as a JWT, using JSON-LD
case JwtVcJsonLd = 'jwt_vc_json-ld';

Expand All @@ -22,7 +26,4 @@ enum CredentialFormatIdentifiersEnum: string

// IETF SD-JWT VC
case DcSdJwt = 'dc+sd-jwt';

// Deprecated identifier for IETF SD-JWT VC. Use 'dc+sd-jwt' instead.
case VcSdJwt = 'vc+sd-jwt';
}
55 changes: 34 additions & 21 deletions src/Codebooks/MetadataPolicyOperatorsEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,25 +137,37 @@ public function getSupportedParameterContainedValueTypes(): array


/**
* @phpstan-ignore missingType.iterableValue (We can handle mixed type using array_diff)
* @param mixed[] $superset
*/
public function isValueSubsetOf(mixed $value, array $superset): bool
{
$value = is_array($value) ? $value : [$value];

return array_diff($value, $superset) === [];
foreach ($value as $item) {
if (!in_array($item, $superset, true)) {
return false;
}
}

return true;
}


/**
* @phpstan-ignore missingType.iterableValue (We can handle mixed type using array_diff)
* @param mixed[] $subset
*/
public function isValueSupersetOf(mixed $value, array $subset): bool
{
$value = is_array($value) ? $value : [$value];

// Like subset, but from different perspective.
return array_diff($subset, $value) === [];
// Like subset, but from a different perspective.
foreach ($subset as $item) {
if (!in_array($item, $value, true)) {
return false;
}
}

return true;
}


Expand Down Expand Up @@ -301,7 +313,7 @@ public static function validateGeneralParameterOperationRules(array $parameterOp

$operatorValue = $parameterOperations[$metadataPolicyOperatorsEnum->value];
// Check common policy resolving rules for each supported operator.
// If operator value type is not supported, throw.
// If an operator value type is not supported, throw.
if (!$metadataPolicyOperatorsEnum->isOperatorValueTypeSupported($operatorValue)) {
throw new MetadataPolicyException(
sprintf(
Expand All @@ -312,7 +324,7 @@ public static function validateGeneralParameterOperationRules(array $parameterOp
);
}

// If operator combination is not allowed, throw.
// If an operator combination is not allowed, throw.
if (!$metadataPolicyOperatorsEnum->isOperatorCombinationSupported($parameterOperatorKeys)) {
throw new MetadataPolicyException(
sprintf(
Expand Down Expand Up @@ -342,13 +354,14 @@ public static function validateSpecificParameterOperationRules(array $parameterO

$operatorValue = $parameterOperations[$metadataPolicyOperatorEnum->value];

// Start with operator 'value'.
// Start with the operator 'value'.
if ($metadataPolicyOperatorEnum === MetadataPolicyOperatorsEnum::Value) {
// MAY be combined with add, in which case the values of add MUST be a subset of the values of value.
// MAY be combined with 'add', in which case the values of 'add' MUST be a subset of the values
// of value.
if (
in_array(MetadataPolicyOperatorsEnum::Add->value, $parameterOperatorKeys, true)
) {
/** @var array<mixed> $subset We ensured this is array. */
/** @var array<mixed> $subset We ensured this is an array. */
$subset = $parameterOperations[MetadataPolicyOperatorsEnum::Add->value];
if (!MetadataPolicyOperatorsEnum::Value->isValueSupersetOf($operatorValue, $subset)) {
throw new MetadataPolicyException(
Expand All @@ -362,7 +375,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO
}
}

// MAY be combined with default if the value of value is not null.
// MAY be combined with default if the value of 'value' is not null.
if (
in_array(MetadataPolicyOperatorsEnum::Default->value, $parameterOperatorKeys, true) &&
is_null($operatorValue)
Expand All @@ -379,7 +392,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO
if (
in_array(MetadataPolicyOperatorsEnum::OneOf->value, $parameterOperatorKeys, true)
) {
/** @var array<mixed> $oneOf We ensured this is array. */
/** @var array<mixed> $oneOf We ensured this is an array. */
$oneOf = $parameterOperations[MetadataPolicyOperatorsEnum::OneOf->value];
if (!in_array($operatorValue, $oneOf)) {
throw new MetadataPolicyException(
Expand All @@ -398,7 +411,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO
if (
in_array(MetadataPolicyOperatorsEnum::SubsetOf->value, $parameterOperatorKeys, true)
) {
/** @var array<mixed> $superset We ensured this is array. */
/** @var array<mixed> $superset We ensured this is an array. */
$superset = $parameterOperations[MetadataPolicyOperatorsEnum::SubsetOf->value];
if (!MetadataPolicyOperatorsEnum::Value->isValueSubsetOf($operatorValue, $superset)) {
throw new MetadataPolicyException(
Expand All @@ -417,7 +430,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO
if (
in_array(MetadataPolicyOperatorsEnum::SupersetOf->value, $parameterOperatorKeys, true)
) {
/** @var array<mixed> $subset We ensured this is array. */
/** @var array<mixed> $subset We ensured this is an array. */
$subset = $parameterOperations[MetadataPolicyOperatorsEnum::SupersetOf->value];
if (!MetadataPolicyOperatorsEnum::Value->isValueSupersetOf($operatorValue, $subset)) {
throw new MetadataPolicyException(
Expand Down Expand Up @@ -447,15 +460,15 @@ public static function validateSpecificParameterOperationRules(array $parameterO
}
}
} elseif ($metadataPolicyOperatorEnum === MetadataPolicyOperatorsEnum::Add) {
// MAY be combined with value, in which case the values of add MUST be a subset of the values of value.
// We handle this in value case.
// MAY be combined with value, in which case the values of 'add' MUST be a subset of the values of
// 'value'. We handle this in value case.

// MAY be combined with subset_of, in which case the values of add MUST be a subset of the values of
// MAY be combined with subset_of, in which case the values of 'add' MUST be a subset of the values of
// subset_of.
if (
in_array(MetadataPolicyOperatorsEnum::SubsetOf->value, $parameterOperatorKeys, true)
) {
/** @var array<mixed> $superset We ensured this is array. */
/** @var array<mixed> $superset We ensured this is an array. */
$superset = $parameterOperations[
MetadataPolicyOperatorsEnum::SubsetOf->value
];
Expand All @@ -481,8 +494,8 @@ public static function validateSpecificParameterOperationRules(array $parameterO
// MAY be combined with value, in which case the values of value MUST be a subset of the values of
// subset_of. -> handled in value case.

// MAY be combined with add, in which case the values of add MUST be a subset of the values of
// subset_of. -> handled in add case.
// MAY be combined with 'add', in which case the values of 'add' MUST be a subset of the values of
// subset_of. -> handled in the 'add' case.

// MAY be combined with superset_of, in which case the values of subset_of MUST be a superset of the
// values of superset_of.
Expand All @@ -493,7 +506,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO
true,
)
) {
/** @var array<mixed> $subset We ensured this is array. */
/** @var array<mixed> $subset We ensured this is an array. */
$subset = $parameterOperations[
MetadataPolicyOperatorsEnum::SupersetOf->value
];
Expand Down
12 changes: 12 additions & 0 deletions src/Factories/ClaimFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
use SimpleSAML\OpenID\ValueAbstracts\GenericClaim;
use SimpleSAML\OpenID\ValueAbstracts\JwksClaim;
use SimpleSAML\OpenID\VerifiableCredentials\VcDataModel\Factories\VcDataModelClaimFactory;
use SimpleSAML\OpenID\VerifiableCredentials\VcDataModel2\Factories\VcDataModel2ClaimFactory;

class ClaimFactory
{
protected FederationClaimFactory $federationClaimFactory;

protected VcDataModelClaimFactory $vcDataModelClaimFactory;

protected VcDataModel2ClaimFactory $vcDataModel2ClaimFactory;


public function __construct(
protected readonly Helpers $helpers,
Expand All @@ -43,6 +46,15 @@ public function forVcDataModel(): VcDataModelClaimFactory
}


public function forVcDataModel2(): VcDataModel2ClaimFactory
{
return $this->vcDataModel2ClaimFactory ??= new VcDataModel2ClaimFactory(
$this->helpers,
$this,
);
}


/**
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
*/
Expand Down
30 changes: 20 additions & 10 deletions src/Federation/MetadataPolicyApplicator.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ public function for(

/** @var array<mixed> $metadataParameterValueBeforePolicy */
/** @var array<mixed> $operatorValue */
$metadataParameterValue = array_values(array_unique(array_merge(
$metadataParameterValueBeforePolicy,
$operatorValue,
)));
$uniqueValues = [];
foreach (array_merge($metadataParameterValueBeforePolicy, $operatorValue) as $item) {
if (!in_array($item, $uniqueValues, true)) {
$uniqueValues[] = $item;
}
}

$metadataParameterValue = $uniqueValues;

$metadata[$policyParameterName] = $this->resolveParameterValueAfterPolicy(
$metadataParameterValue,
Expand Down Expand Up @@ -127,10 +131,12 @@ public function for(

/** @var array<mixed> $metadataParameterValueBeforePolicy */
/** @var array<mixed> $operatorValue */
$intersection = array_values(array_intersect(
$metadataParameterValueBeforePolicy,
$operatorValue,
));
$intersection = [];
foreach ($metadataParameterValueBeforePolicy as $item) {
if (in_array($item, $operatorValue, true)) {
$intersection[] = $item;
}
}

$metadata[$policyParameterName] = $this->resolveParameterValueAfterPolicy(
$intersection,
Expand Down Expand Up @@ -198,7 +204,7 @@ protected function resolveParameterValueBeforePolicy(array $metadata, string $pa
{
$value = $metadata[$parameter] ?? null;

// Special case for 'scope' parameter, which needs to be converted to array before policy application.
// Special case for the 'scope' parameter, which needs to be converted to array before policy application.
if (($parameter === ClaimsEnum::Scope->value) && is_string($value)) {
return explode(' ', $value);
}
Expand All @@ -209,8 +215,12 @@ protected function resolveParameterValueBeforePolicy(array $metadata, string $pa

protected function resolveParameterValueAfterPolicy(mixed $value, string $parameter): mixed
{
// Special case for 'scope' parameter, which needs to be converted to string after policy application.
// Special case for the 'scope' parameter, which needs to be converted to string after policy application.
if (($parameter === ClaimsEnum::Scope->value) && is_array($value)) {
$value = array_filter(
$value,
is_string(...),
);
return implode(' ', $value);
}

Expand Down
Loading
Loading