|
3 | 3 | [](https://github.com/simplesamlphp/openid/actions/workflows/php.yml) |
4 | 4 | [](https://app.codecov.io/gh/simplesamlphp/openid) |
5 | 5 |
|
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. |
7 | 7 |
|
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. |
9 | 9 |
|
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 | | -} |
167 | 10 | ``` |
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 | + */ |
199 | 21 | ``` |
200 | 22 |
|
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). |
209 | 24 |
|
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' |
216 | 25 |
|
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 | | -``` |
0 commit comments