Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
83f6c9b
Move JwsParser to JwsDecoratorBuilder
cicnavi Apr 8, 2025
7631f77
Rename JwksFactory to JwksDecoratorFactory
cicnavi Apr 8, 2025
e85095c
Start with JWK
cicnavi Apr 8, 2025
803bacc
Fix styles
cicnavi Apr 9, 2025
37c62c7
Propagate fromData JWS factory method
cicnavi Apr 9, 2025
03c7ad1
Introduce JWK suite
cicnavi Apr 10, 2025
d2731fa
Update type header handling when building specific JWS
cicnavi Apr 10, 2025
55c90e7
WIP JwtVcJson
cicnavi Apr 10, 2025
8ef11c9
WIP JwtVcJson
cicnavi Apr 11, 2025
141e58c
WIP JwtVcJson
cicnavi Apr 23, 2025
be52cdd
Move to ClaimInterface
cicnavi Apr 23, 2025
68b189d
WIP JwtVcJson
cicnavi Apr 24, 2025
d3195e1
Merge branch 'master' into wip-vci
cicnavi Apr 25, 2025
06cafe8
WIP JwtVcJson
cicnavi Apr 25, 2025
498b759
WIP JwtVcJson
cicnavi Apr 29, 2025
1c101ae
Add ClaimsPathPointerResolver
cicnavi May 1, 2025
5e13306
Add LanguageTagsEnum
cicnavi May 1, 2025
faa7ce8
Start with VerifiableCredentials manager
cicnavi May 6, 2025
420bd4e
WIP
cicnavi May 9, 2025
70b7f10
WIP
cicnavi May 9, 2025
4f48a3f
WIP
cicnavi May 13, 2025
f696bf9
WIP
cicnavi May 13, 2025
561b297
WIP
cicnavi May 15, 2025
2402df0
WIP
cicnavi May 15, 2025
9946a95
WIP
cicnavi May 15, 2025
c711fa4
WIP
cicnavi Jun 3, 2025
04fdda9
WIP
cicnavi Jun 6, 2025
5db5adb
WIP
cicnavi Jun 6, 2025
5e62bea
WIP
cicnavi Jun 6, 2025
c01d13f
Merge with master
cicnavi Jul 28, 2025
e90296c
WIP SD-JWT
cicnavi Jul 30, 2025
87a9330
WIP
cicnavi Aug 4, 2025
737eac1
WIP
cicnavi Aug 4, 2025
4e56d8b
WIP
cicnavi Aug 4, 2025
4bcdc19
WIP
cicnavi Aug 4, 2025
a4b991e
WIP
cicnavi Aug 6, 2025
eadc216
WIP
cicnavi Aug 11, 2025
0144143
Add TxCode
cicnavi Aug 18, 2025
c1a8fc7
Add ext-mbstring requirement
cicnavi Aug 18, 2025
5d19dfe
Get TxCode as string
cicnavi Aug 18, 2025
6a8222e
Get TxCode as string
cicnavi Aug 18, 2025
c0a72b5
Update ClaimsEnum
cicnavi Sep 10, 2025
aa31a9f
Update ClaimsEnum
cicnavi Sep 10, 2025
07d879d
Lint
cicnavi Sep 10, 2025
4a1681c
Add gmp to windows CI run
cicnavi Sep 10, 2025
d5892ea
Update ClaimsEnum
cicnavi Sep 17, 2025
8c794da
Lint
cicnavi Sep 18, 2025
37aff22
Merge branch 'master' into wip-vci
cicnavi Sep 18, 2025
2fa09c2
Explicitly add phpstan/phpdoc-parser to require-dev
cicnavi Sep 18, 2025
3906ce6
Revert "Explicitly add phpstan/phpdoc-parser to require-dev"
cicnavi Sep 29, 2025
f6c50f3
WIP tests
cicnavi Sep 29, 2025
7b521db
Use local phpcs
cicnavi Sep 29, 2025
e2e7595
Merge with master
cicnavi Oct 25, 2025
5c0f41e
Add ext-hash to composer.json
cicnavi Oct 25, 2025
a56a234
Lint
cicnavi Oct 25, 2025
4efcbe6
Add coverage
cicnavi Oct 25, 2025
3605580
Add coverage
cicnavi Oct 25, 2025
76bcfe5
Merge branch 'master' into wip-vci
cicnavi Nov 3, 2025
02c6337
Merge with master
cicnavi Nov 10, 2025
d3907b2
Merge branch 'master' into wip-vci
cicnavi Nov 10, 2025
2bb4d9d
Merge with master
cicnavi Nov 11, 2025
9203355
Update master code to new API
cicnavi Nov 11, 2025
5ad7f95
WIP coverage
cicnavi Nov 11, 2025
b378d70
WIP coverage
cicnavi Nov 17, 2025
b78e83b
WIP coverage
cicnavi Nov 17, 2025
a80aad3
WIP coverage
cicnavi Nov 17, 2025
80069e6
WIP coverage
cicnavi Nov 19, 2025
81314c4
WIP coverage
cicnavi Nov 19, 2025
7c42560
Initial VCI coverage
cicnavi Nov 19, 2025
d8bc7bf
Move to dedicated docs
cicnavi Nov 19, 2025
e8d6878
Add Build docmuentation workflow
cicnavi Nov 19, 2025
cd1ed84
Reduce phpstan testsuite level
cicnavi Nov 19, 2025
6ba472a
Initial dedicated docs
cicnavi Nov 21, 2025
55d63cd
Compare unique items regularly
cicnavi Nov 21, 2025
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
21 changes: 21 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,24 @@ jobs:
path: '**/*.md'
check_filenames: true
ignore_words_list: tekst

build:
name: Build documentation
needs: quality
runs-on: [ubuntu-latest]

steps:
- name: Run docs build
if: github.event_name != 'pull_request'
uses: actions/github-script@v8
with:
# Token has to be generated on a user account that controls the docs-repository.
# The _only_ scope to select is "Access public repositories", nothing more.
github-token: ${{ secrets.PAT_TOKEN }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'simplesamlphp',
repo: 'docs',
workflow_id: 'mk_docs.yml',
ref: 'main'
})
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: ctype, date, dom, filter, hash, mbstring, openssl, pcre, soap, spl, xml, sodium
extensions: ctype, date, dom, filter, hash, mbstring, openssl, pcre, soap, spl, xml, sodium, gmp
tools: composer
ini-values: error_reporting=E_ALL
coverage: none
Expand Down
284 changes: 13 additions & 271 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,281 +3,23 @@
[![Build Status](https://github.com/simplesamlphp/openid/actions/workflows/php.yml/badge.svg)](https://github.com/simplesamlphp/openid/actions/workflows/php.yml)
[![Coverage Status](https://codecov.io/gh/simplesamlphp/openid/branch/master/graph/badge.svg)](https://app.codecov.io/gh/simplesamlphp/openid)

The library is under development, and you can expect braking changes along the way.
The library provides some common tools that you might find useful when working with the OpenID family of specifications.

The library provides some common tools that you might find useful when working with OpenID family of specifications.
> The library is under development, and you can expect braking changes along the way.

## Installation

Library can be installed by using Composer:

```shell
composer require simplesamlphp/openid
```

## OpenID Federation (draft 43)

The initial functionality of the library revolves around the OpenID Federation specification. To use it, create an
instance of the class `\SimpleSAML\OpenID\Federation`

```php
<?php

declare(strict_types=1);

namespace Your\Super\App;

use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmBag;
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
use SimpleSAML\OpenID\SupportedAlgorithms;
use Psr\SimpleCache\CacheInterface;
use Psr\Log\LoggerInterface;
use SimpleSAML\OpenID\Federation;
use Symfony\Component\HttpFoundation\Response;

class Test
{
public function __construct(
protected CacheInterface $cache,
protected LoggerInterface $logger,
) {
}

public function __invoke(): Response
{
// Instantiation example by using default options.
// * 'RS256' as supported algorithm
// * no caching support (not recommended for production environment)
// * no logging support
$federationTools = new Federation();

// Instantiation example by injecting some of the dependencies
// Define the supported signature algorithms:
$supportedAlgorithms = new SupportedAlgorithms(
new SignatureAlgorithmBag(
SignatureAlgorithmEnum::RS256,
// ... if needed, add other supported signature algorithms here
)
);

// Define the maximum cache Time-To-Live (TTL) for federation artifacts. This will be used together with 'exp'
// claim to resolve the maximum cache time for trust chains, entity statements, etc.
$maxCacheDuration = new DateInterval('PT6H');

// Instantiate by injecting own options / dependencies:
$federationTools = new Federation(
supportedAlgorithms: $supportedAlgorithms,
maxCacheDuration: $maxCacheDuration,
cache: $this->cache, // \Psr\SimpleCache\CacheInterface
logger: $this->logger, // \Psr\Log\LoggerInterface
);

// Continue with using available tools ...

return new Response();
}
}
```

### Trust chain resolver

Once you have a `\SimpleSAML\OpenID\Federation` instantiated, you can continue with using available tools. The first
tool we will take a look at is trust chain resolver. This tool can be used to try and resolve the (shortest) trust chain
for given leaf entity (subject) and trusted anchors:

```php

// ...

try {
/** @var \SimpleSAML\OpenID\Federation $federationTools */
/** @var \SimpleSAML\OpenID\Federation\TrustChainBag $trustChainBag */
$trustChainBag = $federationTools->trustChainResolver()->for(
'https://leaf-entity-id.example.org/', // Trust chain subject (leaf entity).
[
// List of valid trust anchors.
'https://trust-achor-id.example.org/',
'https://other-trust-achor-id.example.org/',
],
);
} catch (\Throwable $exception) {
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
return;
}

```

If the trust chain is successfully resolved, this will return an instance of
`\SimpleSAML\OpenID\Federation\TrustChainBag`. Otherwise, exception will be thrown.
From the TrustChainBag you can get the TrustChain using several methods.

```php

// ...

try {
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
/** @var \SimpleSAML\OpenID\Federation\TrustChainBag $trustChainBag */
// Simply get the shortest available chain.
$trustChain = $trustChainBag->getShortest();
// Get the shortest chain, but take into account the Trust Anchor priority.
$trustChain = $trustChainBag->getShortestByTrustAnchorPriority(
'https://other-trust-achor-id.example.org/', // Get chain for this Trust Anchor even if the chain is longer.
'https://trust-achor-id.example.org/',
);
} catch (\Throwable $exception) {
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
return;
}

```

Once you have the Trust Chain, you can try and get the resolved metadata for particular entity type. Resolved metadata
means that all metadata policies from all intermediates have been successfully applied. Here is one example for trying
to get metadata for OpenID RP, which will return an array (or null if no metadata is available for given entity type):

```php
// ...

$entityType = \SimpleSAML\OpenID\Codebooks\EntityTypesEnum::OpenIdRelyingParty;

try {
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
$metadata = $trustChain->getResolvedMetadata($entityType);
} catch (\Throwable $exception) {
$this->logger->error(
sprintf(
'Error resolving metadata for entity type %s. Error: %s.',
$entityType->value,
$exception->getMessage(),
),
);
return;
}

if (is_null($metadata)) {
$this->logger->error(
sprintf(
'No metadata available for entity type %s.',
$entityType->value,
),
);
return;
}
```

Check failure on line 10 in README.md

View workflow job for this annotation

GitHub Actions / Quality checks

Fenced code blocks should have a language specified [Context: "```"]

If getting metadata results in an exception, the metadata is considered invalid and is to be discarded.

### Additional verification of signatures

The whole trust chain (each entity statement) has been verified using public keys from JWKS claims in configuration /
subordinate statements. As per specification recommendation, you can also validate the signature of the Trust Chain
Configuration Statement by using the Trust Anchor public keys (JWKS) that you have acquired in some secure out-of-band
way (so to not only rely on TLS protection while fetching Trust Anchor Configuration Statement):

```php

// ...

// Get entity statement for the resolved Trust Anchor:
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
$trustAnchorConfigurationStatement = $trustChain->getResolvedTrustAnchor();
// Get data that you need to prepare appropriate public keys, for example, the entity ID:
$trustAnchorEntityId = $trustAnchorConfigurationStatement->getIssuer();

// Prepare JWKS array containing Trust Anchor public keys that you have acquired in secure out-of-band way ...
/** @var array $trustAnchorJwks */

try {
$trustAnchorConfigurationStatement->verifyWithKeySet($trustAnchorJwks);
} catch (\Throwable $exception) {
$this->logger->error('Could not verify trust anchor configuration statement signature: ' .
$exception->getMessage());
return;
}

/*
*
* |
* \ ___ / _________
* _ / \ _ GÉANT | * * | Co-Funded by
* | ~ | Trust & Identity | * * | the European
* \_/ Incubator |__*_*__| Union
* =
*
*/
```

### Fetching Trust Marks

Federation tools expose Trust Mark Fetcher which you can use to dynamically fetch or refresh (short-living) Trust Marks.

```php
// ...

/** @var \SimpleSAML\OpenID\Federation $federationTools */
To get started, refer to [library documentation](docs/1-openid.md).

// Trust Mark Type that you want to fetch.
$trustMarkType = 'https://example.com/trust-mark/member';
// ID of Subject for which to fetch the Trust Mark.
$subjectId = 'https://leaf-entity.org'
// ID of the Trust Mark Issuer from which to fetch the Trust Mark.
$trustMarkIssuerEntityId = 'https://trust-mark-issuer.org'

Check failure on line 25 in README.md

View workflow job for this annotation

GitHub Actions / Quality checks

Multiple consecutive blank lines [Expected: 1; Actual: 2]
try {
// First, fetch the Configuration Statement for Trust Mark Issuer.
$trustMarkIssuerConfigurationStatement = $this->federation
->entityStatementFetcher()
->fromCacheOrWellKnownEndpoint($trustMarkIssuerEntityId);

// Fetch the Trust Mark from Issuer.
$trustMarkEntity = $federationTools->trustMarkFetcher()->fromCacheOrFederationTrustMarkEndpoint(
$trustMarkType,
$subjectId,
$trustMarkIssuerConfigurationStatement
);

} catch (\Throwable $exception) {
$this->logger->error('Trust Mark fetch failed. Error was: ' . $exception->getMessage());
return;
}

```

### Validating Trust Marks

Federation tools expose Trust Mark Validator with several methods for validating
Trust Marks, with the most common one being the one to validate Trust Mark for
some entity simply based on the Trust Mark Type.

If cache is used, Trust Mark validation will be cached with cache TTL being the minimum expiration
time of Trust Mark, Leaf Entity Statement or `maxCacheDuration`, whatever is smaller.

```php
// ...

/** @var \SimpleSAML\OpenID\Federation $federationTools */
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */


// Trust Mark Type that you want to validate.
$trustMarkType = 'https://example.com/trust-mark/member';
// Leaf for which you want to validate the Trust Mark with ID above.
$leafEntityConfigurationStatement = $trustChain->getResolvedLeaf();
// Trust Anchor under which you want to validate Trust Mark.
$trustAnchorConfigurationStatement = $trustChain->getResolvedTrustAnchor();

try {
// Example which queries cache for previously validated Trust Mark and does formal validation if not cached.
$federationTools->trustMarkValidator()->fromCacheOrDoForTrustMarkType(
$trustMarkType,
$leafEntityConfigurationStatement,
$trustAnchorConfigurationStatement,
$expectedJwtType = \SimpleSAML\OpenID\Codebooks\JwtTypesEnum::TrustMarkJwt,
);

// Example which always does formal validation (does not use cache), and requires usage of Trust Mark
// Status Endpoint for non-expiring Trust Marks.
$federationTools->trustMarkValidator()->doForTrustMarkType(
$trustMarkType,
$leafEntityConfigurationStatement,
$trustAnchorConfigurationStatement,
$expectedJwtType = \SimpleSAML\OpenID\Codebooks\JwtTypesEnum::TrustMarkJwt,
\SimpleSAML\OpenID\Codebooks\TrustMarkStatusEndpointUsagePolicyEnum::RequiredForNonExpiringTrustMarksOnly,
);
} catch (\Throwable $exception) {
$this->logger->error('Trust Mark validation failed. Error was: ' . $exception->getMessage());
return;
}

```
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
},
"require": {
"php": "^8.2",
"ext-gmp": "*",
"ext-filter": "*",
"ext-mbstring": "*",
"ext-hash": "*",

"guzzlehttp/guzzle": "^7.8",
"psr/http-client": "^1",
Expand Down
5 changes: 5 additions & 0 deletions docs/1-openid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# OpenID Tools Library

1. [Installation](2-installation.md)
2. [OpenID Federation Tools](3-federation.md)
3. [OpenID for Verifiable Credential Issuance (OpenID4VCI) Tools](4-vci.md)
7 changes: 7 additions & 0 deletions docs/2-installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## OpenID Tools Library Installation

Check failure on line 1 in docs/2-installation.md

View workflow job for this annotation

GitHub Actions / Quality checks

First line in a file should be a top-level heading [Context: "## OpenID Tools Library Instal..."]

Library can be installed by using Composer:

```shell
composer require simplesamlphp/openid
```
Loading
Loading