Skip to content

Commit b2e67c5

Browse files
authored
Introduce initial VCI tools (#15)
Introduce initial VCI tools
1 parent 604660b commit b2e67c5

File tree

195 files changed

+12821
-785
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

195 files changed

+12821
-785
lines changed

.github/workflows/documentation.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,24 @@ jobs:
3333
path: '**/*.md'
3434
check_filenames: true
3535
ignore_words_list: tekst
36+
37+
build:
38+
name: Build documentation
39+
needs: quality
40+
runs-on: [ubuntu-latest]
41+
42+
steps:
43+
- name: Run docs build
44+
if: github.event_name != 'pull_request'
45+
uses: actions/github-script@v8
46+
with:
47+
# Token has to be generated on a user account that controls the docs-repository.
48+
# The _only_ scope to select is "Access public repositories", nothing more.
49+
github-token: ${{ secrets.PAT_TOKEN }}
50+
script: |
51+
await github.rest.actions.createWorkflowDispatch({
52+
owner: 'simplesamlphp',
53+
repo: 'docs',
54+
workflow_id: 'mk_docs.yml',
55+
ref: 'main'
56+
})

.github/workflows/php.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
uses: shivammathur/setup-php@v2
121121
with:
122122
php-version: ${{ matrix.php-versions }}
123-
extensions: ctype, date, dom, filter, hash, mbstring, openssl, pcre, soap, spl, xml, sodium
123+
extensions: ctype, date, dom, filter, hash, mbstring, openssl, pcre, soap, spl, xml, sodium, gmp
124124
tools: composer
125125
ini-values: error_reporting=E_ALL
126126
coverage: none

README.md

Lines changed: 13 additions & 271 deletions
Original file line numberDiff line numberDiff line change
@@ -3,281 +3,23 @@
33
[![Build Status](https://github.com/simplesamlphp/openid/actions/workflows/php.yml/badge.svg)](https://github.com/simplesamlphp/openid/actions/workflows/php.yml)
44
[![Coverage Status](https://codecov.io/gh/simplesamlphp/openid/branch/master/graph/badge.svg)](https://app.codecov.io/gh/simplesamlphp/openid)
55

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

8-
The library provides some common tools that you might find useful when working with OpenID family of specifications.
8+
> The library is under development, and you can expect braking changes along the way.
99
10-
## Installation
11-
12-
Library can be installed by using Composer:
13-
14-
```shell
15-
composer require simplesamlphp/openid
16-
```
17-
18-
## OpenID Federation (draft 43)
19-
20-
The initial functionality of the library revolves around the OpenID Federation specification. To use it, create an
21-
instance of the class `\SimpleSAML\OpenID\Federation`
22-
23-
```php
24-
<?php
25-
26-
declare(strict_types=1);
27-
28-
namespace Your\Super\App;
29-
30-
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmBag;
31-
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
32-
use SimpleSAML\OpenID\SupportedAlgorithms;
33-
use Psr\SimpleCache\CacheInterface;
34-
use Psr\Log\LoggerInterface;
35-
use SimpleSAML\OpenID\Federation;
36-
use Symfony\Component\HttpFoundation\Response;
37-
38-
class Test
39-
{
40-
public function __construct(
41-
protected CacheInterface $cache,
42-
protected LoggerInterface $logger,
43-
) {
44-
}
45-
46-
public function __invoke(): Response
47-
{
48-
// Instantiation example by using default options.
49-
// * 'RS256' as supported algorithm
50-
// * no caching support (not recommended for production environment)
51-
// * no logging support
52-
$federationTools = new Federation();
53-
54-
// Instantiation example by injecting some of the dependencies
55-
// Define the supported signature algorithms:
56-
$supportedAlgorithms = new SupportedAlgorithms(
57-
new SignatureAlgorithmBag(
58-
SignatureAlgorithmEnum::RS256,
59-
// ... if needed, add other supported signature algorithms here
60-
)
61-
);
62-
63-
// Define the maximum cache Time-To-Live (TTL) for federation artifacts. This will be used together with 'exp'
64-
// claim to resolve the maximum cache time for trust chains, entity statements, etc.
65-
$maxCacheDuration = new DateInterval('PT6H');
66-
67-
// Instantiate by injecting own options / dependencies:
68-
$federationTools = new Federation(
69-
supportedAlgorithms: $supportedAlgorithms,
70-
maxCacheDuration: $maxCacheDuration,
71-
cache: $this->cache, // \Psr\SimpleCache\CacheInterface
72-
logger: $this->logger, // \Psr\Log\LoggerInterface
73-
);
74-
75-
// Continue with using available tools ...
76-
77-
return new Response();
78-
}
79-
}
80-
```
81-
82-
### Trust chain resolver
83-
84-
Once you have a `\SimpleSAML\OpenID\Federation` instantiated, you can continue with using available tools. The first
85-
tool we will take a look at is trust chain resolver. This tool can be used to try and resolve the (shortest) trust chain
86-
for given leaf entity (subject) and trusted anchors:
87-
88-
```php
89-
90-
// ...
91-
92-
try {
93-
/** @var \SimpleSAML\OpenID\Federation $federationTools */
94-
/** @var \SimpleSAML\OpenID\Federation\TrustChainBag $trustChainBag */
95-
$trustChainBag = $federationTools->trustChainResolver()->for(
96-
'https://leaf-entity-id.example.org/', // Trust chain subject (leaf entity).
97-
[
98-
// List of valid trust anchors.
99-
'https://trust-achor-id.example.org/',
100-
'https://other-trust-achor-id.example.org/',
101-
],
102-
);
103-
} catch (\Throwable $exception) {
104-
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
105-
return;
106-
}
107-
108-
```
109-
110-
If the trust chain is successfully resolved, this will return an instance of
111-
`\SimpleSAML\OpenID\Federation\TrustChainBag`. Otherwise, exception will be thrown.
112-
From the TrustChainBag you can get the TrustChain using several methods.
113-
114-
```php
115-
116-
// ...
117-
118-
try {
119-
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
120-
/** @var \SimpleSAML\OpenID\Federation\TrustChainBag $trustChainBag */
121-
// Simply get the shortest available chain.
122-
$trustChain = $trustChainBag->getShortest();
123-
// Get the shortest chain, but take into account the Trust Anchor priority.
124-
$trustChain = $trustChainBag->getShortestByTrustAnchorPriority(
125-
'https://other-trust-achor-id.example.org/', // Get chain for this Trust Anchor even if the chain is longer.
126-
'https://trust-achor-id.example.org/',
127-
);
128-
} catch (\Throwable $exception) {
129-
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
130-
return;
131-
}
132-
133-
```
134-
135-
Once you have the Trust Chain, you can try and get the resolved metadata for particular entity type. Resolved metadata
136-
means that all metadata policies from all intermediates have been successfully applied. Here is one example for trying
137-
to get metadata for OpenID RP, which will return an array (or null if no metadata is available for given entity type):
138-
139-
```php
140-
// ...
141-
142-
$entityType = \SimpleSAML\OpenID\Codebooks\EntityTypesEnum::OpenIdRelyingParty;
143-
144-
try {
145-
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
146-
$metadata = $trustChain->getResolvedMetadata($entityType);
147-
} catch (\Throwable $exception) {
148-
$this->logger->error(
149-
sprintf(
150-
'Error resolving metadata for entity type %s. Error: %s.',
151-
$entityType->value,
152-
$exception->getMessage(),
153-
),
154-
);
155-
return;
156-
}
157-
158-
if (is_null($metadata)) {
159-
$this->logger->error(
160-
sprintf(
161-
'No metadata available for entity type %s.',
162-
$entityType->value,
163-
),
164-
);
165-
return;
166-
}
16710
```
168-
169-
If getting metadata results in an exception, the metadata is considered invalid and is to be discarded.
170-
171-
### Additional verification of signatures
172-
173-
The whole trust chain (each entity statement) has been verified using public keys from JWKS claims in configuration /
174-
subordinate statements. As per specification recommendation, you can also validate the signature of the Trust Chain
175-
Configuration Statement by using the Trust Anchor public keys (JWKS) that you have acquired in some secure out-of-band
176-
way (so to not only rely on TLS protection while fetching Trust Anchor Configuration Statement):
177-
178-
```php
179-
180-
// ...
181-
182-
// Get entity statement for the resolved Trust Anchor:
183-
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
184-
$trustAnchorConfigurationStatement = $trustChain->getResolvedTrustAnchor();
185-
// Get data that you need to prepare appropriate public keys, for example, the entity ID:
186-
$trustAnchorEntityId = $trustAnchorConfigurationStatement->getIssuer();
187-
188-
// Prepare JWKS array containing Trust Anchor public keys that you have acquired in secure out-of-band way ...
189-
/** @var array $trustAnchorJwks */
190-
191-
try {
192-
$trustAnchorConfigurationStatement->verifyWithKeySet($trustAnchorJwks);
193-
} catch (\Throwable $exception) {
194-
$this->logger->error('Could not verify trust anchor configuration statement signature: ' .
195-
$exception->getMessage());
196-
return;
197-
}
198-
11+
/*
12+
*
13+
* |
14+
* \ ___ / _________
15+
* _ / \ _ GÉANT | * * | Co-Funded by
16+
* | ~ | Trust & Identity | * * | the European
17+
* \_/ Incubator |__*_*__| Union
18+
* =
19+
*
20+
*/
19921
```
20022

201-
### Fetching Trust Marks
202-
203-
Federation tools expose Trust Mark Fetcher which you can use to dynamically fetch or refresh (short-living) Trust Marks.
204-
205-
```php
206-
// ...
207-
208-
/** @var \SimpleSAML\OpenID\Federation $federationTools */
23+
To get started, refer to [library documentation](docs/1-openid.md).
20924

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

217-
try {
218-
// First, fetch the Configuration Statement for Trust Mark Issuer.
219-
$trustMarkIssuerConfigurationStatement = $this->federation
220-
->entityStatementFetcher()
221-
->fromCacheOrWellKnownEndpoint($trustMarkIssuerEntityId);
222-
223-
// Fetch the Trust Mark from Issuer.
224-
$trustMarkEntity = $federationTools->trustMarkFetcher()->fromCacheOrFederationTrustMarkEndpoint(
225-
$trustMarkType,
226-
$subjectId,
227-
$trustMarkIssuerConfigurationStatement
228-
);
229-
230-
} catch (\Throwable $exception) {
231-
$this->logger->error('Trust Mark fetch failed. Error was: ' . $exception->getMessage());
232-
return;
233-
}
234-
235-
```
236-
237-
### Validating Trust Marks
238-
239-
Federation tools expose Trust Mark Validator with several methods for validating
240-
Trust Marks, with the most common one being the one to validate Trust Mark for
241-
some entity simply based on the Trust Mark Type.
242-
243-
If cache is used, Trust Mark validation will be cached with cache TTL being the minimum expiration
244-
time of Trust Mark, Leaf Entity Statement or `maxCacheDuration`, whatever is smaller.
245-
246-
```php
247-
// ...
248-
249-
/** @var \SimpleSAML\OpenID\Federation $federationTools */
250-
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
251-
252-
253-
// Trust Mark Type that you want to validate.
254-
$trustMarkType = 'https://example.com/trust-mark/member';
255-
// Leaf for which you want to validate the Trust Mark with ID above.
256-
$leafEntityConfigurationStatement = $trustChain->getResolvedLeaf();
257-
// Trust Anchor under which you want to validate Trust Mark.
258-
$trustAnchorConfigurationStatement = $trustChain->getResolvedTrustAnchor();
259-
260-
try {
261-
// Example which queries cache for previously validated Trust Mark and does formal validation if not cached.
262-
$federationTools->trustMarkValidator()->fromCacheOrDoForTrustMarkType(
263-
$trustMarkType,
264-
$leafEntityConfigurationStatement,
265-
$trustAnchorConfigurationStatement,
266-
$expectedJwtType = \SimpleSAML\OpenID\Codebooks\JwtTypesEnum::TrustMarkJwt,
267-
);
268-
269-
// Example which always does formal validation (does not use cache), and requires usage of Trust Mark
270-
// Status Endpoint for non-expiring Trust Marks.
271-
$federationTools->trustMarkValidator()->doForTrustMarkType(
272-
$trustMarkType,
273-
$leafEntityConfigurationStatement,
274-
$trustAnchorConfigurationStatement,
275-
$expectedJwtType = \SimpleSAML\OpenID\Codebooks\JwtTypesEnum::TrustMarkJwt,
276-
\SimpleSAML\OpenID\Codebooks\TrustMarkStatusEndpointUsagePolicyEnum::RequiredForNonExpiringTrustMarksOnly,
277-
);
278-
} catch (\Throwable $exception) {
279-
$this->logger->error('Trust Mark validation failed. Error was: ' . $exception->getMessage());
280-
return;
281-
}
282-
283-
```

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
},
2323
"require": {
2424
"php": "^8.2",
25+
"ext-gmp": "*",
2526
"ext-filter": "*",
27+
"ext-mbstring": "*",
28+
"ext-hash": "*",
2629

2730
"guzzlehttp/guzzle": "^7.8",
2831
"psr/http-client": "^1",

docs/1-openid.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# OpenID Tools Library
2+
3+
1. [Installation](2-installation.md)
4+
2. [OpenID Federation Tools](3-federation.md)
5+
3. [OpenID for Verifiable Credential Issuance (OpenID4VCI) Tools](4-vci.md)

docs/2-installation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## OpenID Tools Library Installation
2+
3+
Library can be installed by using Composer:
4+
5+
```shell
6+
composer require simplesamlphp/openid
7+
```

0 commit comments

Comments
 (0)