[java-idp-plugin-vci] branch dev/READMEDOCS updated: Apply change to rest of sections
Codeberg
noreply at shibboleth.net
Fri Oct 2 05:23:17 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch dev/READMEDOCS
in repository java-idp-plugin-vci.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/a7de844d3a5a531c353cda6f4bd5bbbfc70c7afb
The following commit(s) were added to refs/heads/dev/READMEDOCS by this push:
new a7de844 Apply change to rest of sections
a7de844 is described below
commit a7de844d3a5a531c353cda6f4bd5bbbfc70c7afb
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Fri Oct 2 08:23:04 2026 +0300
Apply change to rest of sections
---
README.md | 756 ++++++++++-----------
docs/example/step-1/conf/relying-party.xml | 4 -
docs/example/step-2/conf/relying-party.xml | 4 -
docs/example/step-3/conf/attribute-filter.xml | 15 +
.../example/step-3/conf/openid-vci-credentials.xml | 18 +
.../{step-2 => step-3}/conf/relying-party.xml | 6 +-
docs/example/step-3/etc/jetty-rewrite-rules.xml | 68 ++
docs/example/step-3/metadata/oidc-client.json | 29 +
.../step-3/metadata/verifiable-credentials.json | 123 ++++
.../step-3/static/oauth-authorization-server.json | 39 ++
.../credentials/GeantIncubatorDiploma_W3C.json | 10 +
docs/example/step-4/conf/attribute-filter.xml | 15 +
.../example/step-4/conf/openid-vci-credentials.xml | 18 +
.../{step-2 => step-4}/conf/relying-party.xml | 8 +-
docs/example/step-4/etc/jetty-rewrite-rules.xml | 77 +++
docs/example/step-4/metadata/oidc-client.json | 29 +
.../step-4/metadata/verifiable-credentials.json | 130 ++++
.../step-4/static/oauth-authorization-server.json | 39 ++
.../credentials/GeantIncubatorDiploma_W3C.json | 10 +
docs/example/step-5/conf/attribute-filter.xml | 26 +
.../example/step-5/conf/openid-vci-credentials.xml | 46 ++
.../{step-2 => step-5}/conf/relying-party.xml | 26 +-
docs/example/step-5/etc/jetty-rewrite-rules.xml | 77 +++
docs/example/step-5/metadata/oidc-client.json | 39 ++
.../step-5/metadata/verifiable-credentials.json | 197 ++++++
.../step-5/static/oauth-authorization-server.json | 49 ++
.../credentials/GeantIncubatorDiploma_W3C.json | 10 +
27 files changed, 1457 insertions(+), 411 deletions(-)
diff --git a/README.md b/README.md
index d61ac64..7f9f051 100644
--- a/README.md
+++ b/README.md
@@ -353,6 +353,8 @@ of the specification, per credential format. Below only the members that need ex
}
```
+Full file: [metadata/verifiable-credentials.json](docs/example/step-1/metadata/verifiable-credentials.json)
+
</details>
The configuration id, `GeantIncubatorDiploma_SDJWT` here, is used everywhere. A wallet uses it
@@ -383,6 +385,8 @@ in it:
}
```
+Full file: [webapps/root/credentials/GeantIncubatorDiploma_W3C.json](docs/example/step-1/webapps/root/credentials/GeantIncubatorDiploma_W3C.json)
+
</details>
You publish it at the url that is in `@context`, see [Metadata endpoints](#metadata-endpoints)
@@ -509,6 +513,8 @@ endpoints go to the anonymous relying party. Both flows below need it:
...
```
+Full file: [conf/relying-party.xml](docs/example/step-1/conf/relying-party.xml)
+
</details>
You do not need to write your host name into the templates of *static/*. They use `$issuer`,
@@ -528,6 +534,8 @@ One thing you need to add is the credential scopes in `scopes_supported` of
}
```
+Full file: [static/oauth-authorization-server.json](docs/example/step-1/static/oauth-authorization-server.json)
+
The `/.well-known/` locations are not IdP paths, you wire them in Jetty. For this step you need
these rules in *etc/jetty-rewrite-rules.xml*. The later steps add one rule each and the whole
file is in [Discovery documents](#discovery-documents).
@@ -573,6 +581,8 @@ file is in [Discovery documents](#discovery-documents).
</Configure>
```
+Full file: [etc/jetty-rewrite-rules.xml](docs/example/step-1/etc/jetty-rewrite-rules.xml)
+
</details>
Wallets running in a browser need also the `Access-Control-Allow-Origin` rule of the whole file in
@@ -609,6 +619,8 @@ different identity. You register both:
]
```
+Full file: [metadata/oidc-client.json](docs/example/step-1/metadata/oidc-client.json)
+
</details>
The `/wallet` suffix is fixed. A wallet that redeems a pre-authorized
@@ -649,6 +661,8 @@ wallet that redeems the code:
...
```
+Full file: [conf/relying-party.xml](docs/example/step-1/conf/relying-party.xml)
+
</details>
The offer request is a `POST` of `application/json` to
@@ -761,6 +775,8 @@ in the same *metadata/oidc-client.json* as the clients above:
]
```
+Full file: [metadata/oidc-client.json](docs/example/step-1/metadata/oidc-client.json)
+
</details>
Note the `scope` value. Every configuration id you want to be requestable by scope needs to be
@@ -802,6 +818,8 @@ authorization request:
...
```
+Full file: [conf/relying-party.xml](docs/example/step-1/conf/relying-party.xml)
+
</details>
The *value* of the attribute is the claims as an array of `{"path": [...], "value": ...}`, the
@@ -865,6 +883,8 @@ And you release it to the wallet in *conf/attribute-filter.xml*. This flow reads
</AttributeFilterPolicy>
```
+Full file: [conf/attribute-filter.xml](docs/example/step-1/conf/attribute-filter.xml)
+
</details>
The token endpoint of the code flow keeps only the credentials the wallet was authorized for: a
@@ -1306,65 +1326,133 @@ What is new:
- the credential header `kid` is the plain name of the key, as in the keyset of the OP
- the Status List url of the credential ends with `~url`
-The credential in *metadata/verifiable-credentials.json* is like the one of Step 2, with id
-`GeantIncubatorDiploma_URL` and:
+The changes per file, each with a link to the whole file after this step:
-```json
-{
- "credential_configurations_supported": {
- ...
- "GeantIncubatorDiploma_URL": {
- "format": "dc+sd-jwt",
- "vct": "GeantIncubatorDiploma_URL",
- "scope": "GeantIncubatorDiploma_URL",
- ...
- "_issuance_configuration": {
- "issuer": "url"
- },
- "credential_metadata": { "...": "..." }
- }
- }
-}
+<details>
+<summary>metadata/verifiable-credentials.json</summary>
+
+```diff
+ ...
+ ]
+ }
++ },
++ "GeantIncubatorDiploma_URL": {
++ "format": "dc+sd-jwt",
++ "vct": "GeantIncubatorDiploma_URL",
++ "scope": "GeantIncubatorDiploma_URL",
++ "cryptographic_binding_methods_supported": ["did:jwk"],
++ "proof_types_supported": {
++ "jwt": {
++ "proof_signing_alg_values_supported": ["ES256"]
++ }
++ },
++ "_issuance_configuration": {
++ "issuer": "url"
++ },
++ "credential_metadata": {
++ "display": [
++ { "name": "Incubator Diploma, url", "locale": "en" }
++ ],
++ "claims": [
++ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
++ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
++ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
++ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
++ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
++ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
++ ]
++ }
+ }
+ }
```
+Full file: [metadata/verifiable-credentials.json](docs/example/step-3/metadata/verifiable-credentials.json)
+
+</details>
+
`url` stands for the value of `idp.oidc.issuer`. You may also write the url itself.
-Add **OAuth.JWTVCIssuer** to the anonymous relying party:
+<details>
+<summary>conf/relying-party.xml</summary>
-```xml
-<bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty">
- <property name="profileConfigurations">
- <list>
- ...
- <!-- A verifier finds the key of a url issuer through this -->
- <ref bean="OAuth.JWTVCIssuer" />
- </list>
- </property>
-</bean>
+```diff
+ ...
+ <!-- A verifier resolves the did:web identifier from this -->
+ <ref bean="DID.Document" />
++ <!-- A verifier finds the key of a url issuer through this -->
++ <ref bean="OAuth.JWTVCIssuer" />
+ </list>
+ </property>
```
-and wire it in *etc/jetty-rewrite-rules.xml*:
+Full file: [conf/relying-party.xml](docs/example/step-3/conf/relying-party.xml)
-```xml
-<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
- ...
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
- <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
- </New>
- </Arg>
- </Call>
-</Configure>
+</details>
+
+<details>
+<summary>etc/jetty-rewrite-rules.xml</summary>
+
+```diff
+ ...
+ </Arg>
+ </Call>
++ <!-- JWT VC issuer metadata -->
++ <Call name="addRule">
++ <Arg>
++ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
++ <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
++ <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
++ </New>
++ </Arg>
++ </Call>
+ <!-- Wallets running in a browser need this -->
+ <Call name="addRule">
+```
+
+Full file: [etc/jetty-rewrite-rules.xml](docs/example/step-3/etc/jetty-rewrite-rules.xml)
+
+</details>
+
+<details>
+<summary>metadata/oidc-client.json</summary>
+
+```diff
+ ...
+ "client_name": "Paradym Wallet",
+ "redirect_uris": ["https://paradym.id/invitation/redirect"],
+- "scope": "openid GeantIncubatorDiploma_SDJWT GeantIncubatorDiploma_W3C GeantIncubatorDiploma_DIDWEB",
++ "scope": "openid GeantIncubatorDiploma_SDJWT GeantIncubatorDiploma_W3C GeantIncubatorDiploma_DIDWEB GeantIncubatorDiploma_URL",
+ "audience": [
+ "credentials",
```
+Full file: [metadata/oidc-client.json](docs/example/step-3/metadata/oidc-client.json)
+
+</details>
+
+<details>
+<summary>static/oauth-authorization-server.json</summary>
+
+```diff
+ ...
+ "GeantIncubatorDiploma_SDJWT",
+ "GeantIncubatorDiploma_W3C",
+- "GeantIncubatorDiploma_DIDWEB"
++ "GeantIncubatorDiploma_DIDWEB",
++ "GeantIncubatorDiploma_URL"
+ ],
+ "authorization_details_types_supported":[
+```
+
+Full file: [static/oauth-authorization-server.json](docs/example/step-3/static/oauth-authorization-server.json)
+
+</details>
+
*static/jwt-vc-issuer.json* as it comes with the plugin is fine. Its `jwks_uri` is the keyset of
the OP, served by `OIDC.Keyset` that is already in the anonymous relying party. The keyset has
the keys of the OP, which sign the credentials until Step 5.
-Scope, scopes supported, attribute encoder and offer as in Step 2, with
-`GeantIncubatorDiploma_URL`.
+Attribute encoder and offer as in Step 2, with `GeantIncubatorDiploma_URL`.
### Step 4: Make the url credential an OpenID Federation credential
@@ -1400,45 +1488,88 @@ Import the federation file into *conf/global.xml*, after the import of Step 2:
<import resource="openid-vci-oidfed.xml"/>
```
-Add `OIDFED.Configuration` of the federation plugin to the anonymous relying party and wire
-`/.well-known/openid-federation` to `/idp/profile/oidfed/entity-configuration` in Jetty, the same
-way as the rules above.
-
Once the import is there every credential states the Entity Identifier, unless it says
-`"oidfed": false`. We want only the url credential in the federation, so the three others get:
+`"oidfed": false`. We want only the url credential in the federation, so the three others get
+`"oidfed": false`:
-```json
-{
- "credential_configurations_supported": {
- "GeantIncubatorDiploma_SDJWT": {
- ...
- "_issuance_configuration": {
- "oidfed": false
- }
- },
- "GeantIncubatorDiploma_W3C": {
- ...
- "_issuance_configuration": {
- "oidfed": false
- }
- },
- "GeantIncubatorDiploma_DIDWEB": {
- ...
- "_issuance_configuration": {
- "issuer": "did:web",
- "oidfed": false
- }
- },
- "GeantIncubatorDiploma_URL": {
- ...
- "_issuance_configuration": {
- "issuer": "url"
- }
- }
- }
-}
+<details>
+<summary>metadata/verifiable-credentials.json</summary>
+
+```diff
+ ...
+ "vct": "GeantIncubatorDiploma_SDJWT",
+ "scope": "GeantIncubatorDiploma_SDJWT",
++ "_issuance_configuration": {
++ "oidfed": false
++ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ ...
+ "format": "vc+sd-jwt",
+ "scope": "GeantIncubatorDiploma_W3C",
++ "_issuance_configuration": {
++ "oidfed": false
++ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ ...
+ },
+ "_issuance_configuration": {
+- "issuer": "did:web"
++ "issuer": "did:web",
++ "oidfed": false
+ },
+ "credential_metadata": {
+```
+
+Full file: [metadata/verifiable-credentials.json](docs/example/step-4/metadata/verifiable-credentials.json)
+
+</details>
+
+`OIDFED.Configuration` of the federation plugin goes to the anonymous relying party, and
+`/.well-known/openid-federation` is wired in Jetty:
+
+<details>
+<summary>conf/relying-party.xml</summary>
+
+```diff
+ ...
+ <!-- A verifier finds the key of a url issuer through this -->
+ <ref bean="OAuth.JWTVCIssuer" />
++ <!-- Of the federation plugin. Serves the Entity Configuration -->
++ <ref bean="OIDFED.Configuration" />
+ </list>
+ </property>
```
+Full file: [conf/relying-party.xml](docs/example/step-4/conf/relying-party.xml)
+
+</details>
+
+<details>
+<summary>etc/jetty-rewrite-rules.xml</summary>
+
+```diff
+ ...
+ </Arg>
+ </Call>
++ <!-- Entity Configuration, of the federation plugin -->
++ <Call name="addRule">
++ <Arg>
++ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
++ <Set name="pattern">/.well-known/openid-federation</Set>
++ <Set name="replacement">/idp/profile/oidfed/entity-configuration</Set>
++ </New>
++ </Arg>
++ </Call>
+ <!-- Wallets running in a browser need this -->
+ <Call name="addRule">
+```
+
+Full file: [etc/jetty-rewrite-rules.xml](docs/example/step-4/etc/jetty-rewrite-rules.xml)
+
+</details>
+
The url credential then carries:
```json
@@ -1496,39 +1627,51 @@ In *conf/openid-vci-credentials.xml* the signing key is put **first** in the lis
credential of Step 2 is still issued, so the new keys need a name, `p:keyNames`, for the did:web
document:
-```xml
-<bean id="openidvci.X509ESSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
- p:privateKey="%{idp.home}/credentials/vci-signing.key" p:keyNames="signingvci-x509">
- <property name="certificates">
- <list>
- <value>%{idp.home}/credentials/vci-signing.crt</value>
- </list>
- </property>
-</bean>
-
-<bean id="openidvci.X509StatusListSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
- p:privateKey="%{idp.home}/credentials/vci-status-list.key" p:keyNames="statuslist-x509">
- <property name="certificates">
- <list>
- <value>%{idp.home}/credentials/vci-status-list.crt</value>
- </list>
- </property>
-</bean>
-
-<util:list id="openidvci.SigningCredentials">
- <ref bean="openidvci.X509ESSigningCredential" />
- <ref bean="openidvci.OPESSigningCredential" />
-</util:list>
+<details>
+<summary>conf/openid-vci-credentials.xml</summary>
-<util:list id="openidvci.status-list.SigningCredentials">
- <ref bean="openidvci.X509StatusListSigningCredential" />
- <ref bean="openidvci.OPESSigningCredential" />
-</util:list>
+```diff
+ ...
+ p:resource="%{idp.signing.oidc.es.key}" />
+
++ <bean id="openidvci.X509ESSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
++ p:privateKey="%{idp.home}/credentials/vci-signing.key" p:keyNames="signingvci-x509">
++ <property name="certificates">
++ <list>
++ <value>%{idp.home}/credentials/vci-signing.crt</value>
++ </list>
++ </property>
++ </bean>
++
++ <bean id="openidvci.X509StatusListSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
++ p:privateKey="%{idp.home}/credentials/vci-status-list.key" p:keyNames="statuslist-x509">
++ <property name="certificates">
++ <list>
++ <value>%{idp.home}/credentials/vci-status-list.crt</value>
++ </list>
++ </property>
++ </bean>
++
+ <util:list id="openidvci.SigningCredentials">
++ <ref bean="openidvci.X509ESSigningCredential" />
+ <ref bean="openidvci.OPESSigningCredential" />
+ </util:list>
+
++ <util:list id="openidvci.status-list.SigningCredentials">
++ <ref bean="openidvci.X509StatusListSigningCredential" />
++ <ref bean="openidvci.OPESSigningCredential" />
++ </util:list>
++
++ <util:list id="openidvci.issuerMetadata.SigningCredentials">
++ <ref bean="openidvci.X509ESSigningCredential" />
++ </util:list>
++
+ </beans>
+```
+
+Full file: [conf/openid-vci-credentials.xml](docs/example/step-5/conf/openid-vci-credentials.xml)
-<util:list id="openidvci.issuerMetadata.SigningCredentials">
- <ref bean="openidvci.X509ESSigningCredential" />
-</util:list>
-```
+</details>
The credentials of Steps 1 to 4 were signed with the key of the OP, so it stays in both lists,
after the new keys, see [Step 6](#step-6-rolling-the-signing-key). In the Status List list it signs
@@ -1590,6 +1733,8 @@ attestation required:
}
```
+Full file: [metadata/verifiable-credentials.json](docs/example/step-5/metadata/verifiable-credentials.json)
+
</details>
For the values of `key_attestations_required`, see
@@ -1652,6 +1797,8 @@ Issuer identifier is not used.
}
```
+Full file: [metadata/verifiable-credentials.json](docs/example/step-5/metadata/verifiable-credentials.json)
+
</details>
What is different from an SD-JWT VC configuration:
@@ -1709,6 +1856,31 @@ The paths differ from the SD-JWT VC credentials, so the content is an attribute
Add `GeantIncubatorDiplomaMdoc` to `idp.oidc.encodedAttributes` in *conf/oidc.properties* and
release it to the HAIP wallet in *conf/attribute-filter.xml*.
+<details>
+<summary>conf/attribute-filter.xml</summary>
+
+```diff
+ ...
+ </AttributeFilterPolicy>
+
++ <AttributeFilterPolicy id="GeantIncubatorDiplomaToHAIPWallets">
++ <PolicyRequirementRule xsi:type="Requester"
++ value="haip-wallet" />
++ <AttributeRule attributeID="GeantIncubatorDiploma">
++ <PermitValueRule xsi:type="ANY" />
++ </AttributeRule>
++ <AttributeRule attributeID="GeantIncubatorDiplomaMdoc">
++ <PermitValueRule xsi:type="ANY" />
++ </AttributeRule>
++ </AttributeFilterPolicy>
++
+ </AttributeFilterPolicyGroup>
+```
+
+Full file: [conf/attribute-filter.xml](docs/example/step-5/conf/attribute-filter.xml)
+
+</details>
+
The Status List needs nothing more for the mdoc, it is also served as CWT, see
[OAuth.StatusList](#oauth-statuslist).
@@ -1761,24 +1933,28 @@ idp.oauth2.authn.flows = OpenIDVCIClient
The client id is the `sub` of the Wallet Attestation of the wallet. In *metadata/oidc-client.json*:
<details>
-<summary>Example snippet of metadata/oidc-client.json</summary>
+<summary>metadata/oidc-client.json</summary>
-```json
-[
- ...
- {
- "client_id": "haip-wallet",
- "client_name": "HAIP Wallet",
- "redirect_uris": ["https://wallet.example.org/callback"],
- "scope": "openid GeantIncubatorDiploma_HAIP GeantIncubatorDiploma_MDOC",
- "audience": "credentials",
- "token_endpoint_auth_method": "attest_jwt_client_auth",
- "response_types": ["code"],
- "grant_types": ["authorization_code"]
- }
-]
+```diff
+ ...
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ]
++ },
++ {
++ "client_id": "haip-wallet",
++ "client_name": "HAIP Wallet",
++ "redirect_uris": ["https://wallet.example.org/callback"],
++ "scope": "openid GeantIncubatorDiploma_HAIP GeantIncubatorDiploma_MDOC",
++ "audience": "credentials",
++ "token_endpoint_auth_method": "attest_jwt_client_auth",
++ "response_types": ["code"],
++ "grant_types": ["authorization_code"]
+ }
+ ]
```
+Full file: [metadata/oidc-client.json](docs/example/step-5/metadata/oidc-client.json)
+
</details>
In *conf/relying-party.xml* PKCE and PAR are required, `iss` is returned in the authorization
@@ -1787,30 +1963,35 @@ response, the authorization code lives one minute, see
and both token and PAR endpoints take the Wallet Attestation:
<details>
-<summary>Example snippet of conf/relying-party.xml</summary>
+<summary>conf/relying-party.xml</summary>
-```xml
-<util:list id="shibboleth.RelyingPartyOverrides">
- ...
- <bean parent="RelyingPartyByName"
- c:relyingPartyIds="#{{'haip-wallet'}}">
- <property name="profileConfigurations">
- <list>
- <bean parent="OIDC.SSO"
- p:authorizationCodeClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:forcePKCE="true" p:requirePushedAuthorizationRequest="true"
- p:includeIssuerInResponse="true" p:authorizeCodeLifetime="PT1M" />
- <bean parent="OpenID.VCI.Token"
- p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}" />
- <bean parent="OpenID.VCI.Credentials" />
- <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
- </list>
- </property>
- </bean>
-</util:list>
-```
+```diff
+ ...
+ </bean>
+
++ <bean parent="RelyingPartyByName"
++ c:relyingPartyIds="#{{'haip-wallet'}}">
++ <property name="profileConfigurations">
++ <list>
++ <bean parent="OIDC.SSO"
++ p:authorizationCodeClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
++ p:forcePKCE="true" p:requirePushedAuthorizationRequest="true"
++ p:includeIssuerInResponse="true" p:authorizeCodeLifetime="PT1M" />
++ <bean parent="OpenID.VCI.Token"
++ p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
++ p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}" />
++ <bean parent="OpenID.VCI.Credentials" />
++ <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
++ p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
++ </list>
++ </property>
++ </bean>
++
+ </util:list>
+
+```
+
+Full file: [conf/relying-party.xml](docs/example/step-5/conf/relying-party.xml)
</details>
@@ -1819,65 +2000,43 @@ DPoP settings are left to their defaults, so a DPoP nonce is required.
`openidvci.PushedAuthorizationDPoPJktManipulationStrategy` on **OAUTH2.PAR** is recommended with
DPoP, see [RFC 9449 10.1](https://www.rfc-editor.org/rfc/rfc9449#section-10.1).
-The whole *static/oauth-authorization-server.json* after this step. `token_endpoint_auth_methods_supported`
-lists the methods your `tokenEndpointAuthMethods` allow on the token and PAR endpoints, here `none`
-for Paradym and `attest_jwt_client_auth` for the HAIP wallet:
+`token_endpoint_auth_methods_supported` of *static/oauth-authorization-server.json* lists the
+methods your `tokenEndpointAuthMethods` allow on the token and PAR endpoints, here `none` for
+Paradym and `attest_jwt_client_auth` for the HAIP wallet:
<details>
-<summary>Example contents of static/oauth-authorization-server.json</summary>
+<summary>static/oauth-authorization-server.json</summary>
-```json
-{
- "issuer":"$issuer",
- "authorization_endpoint":"$baseUrl/idp/profile/oidc/authorize",
- "pushed_authorization_request_endpoint":"$baseUrl/idp/profile/oauth2/pushed-authorization",
- "require_pushed_authorization_requests":true,
- "token_endpoint":"$baseUrl/idp/profile/openid/vci/token",
- "jwks_uri":"$baseUrl/idp/profile/oidc/keyset",
- "token_endpoint_auth_methods_supported":[
- "none",
- "attest_jwt_client_auth"
- ],
- "client_attestation_signing_alg_values_supported":[
- "ES256"
- ],
- "client_attestation_pop_signing_alg_values_supported":[
- "ES256"
- ],
- "authorization_response_iss_parameter_supported":true,
- "response_types_supported":[
- "code"
- ],
- "grant_types_supported":[
- "authorization_code",
- "urn:ietf:params:oauth:grant-type:pre-authorized_code"
- ],
- "code_challenge_methods_supported":[
- "S256"
- ],
- "dpop_signing_alg_values_supported":[
- "ES256",
- "ES384",
- "ES512",
- "PS256",
- "PS384",
- "PS512"
- ],
- "scopes_supported":[
- "openid",
- "GeantIncubatorDiploma_SDJWT",
- "GeantIncubatorDiploma_W3C",
- "GeantIncubatorDiploma_DIDWEB",
- "GeantIncubatorDiploma_URL",
- "GeantIncubatorDiploma_HAIP",
- "GeantIncubatorDiploma_MDOC"
- ],
- "authorization_details_types_supported":[
- "openid_credential"
- ]
-}
+```diff
+ ...
+ "jwks_uri":"$baseUrl/idp/profile/oidc/keyset",
+ "token_endpoint_auth_methods_supported":[
+- "none"
++ "none",
++ "attest_jwt_client_auth"
+ ],
++ "client_attestation_signing_alg_values_supported":[
++ "ES256"
++ ],
++ "client_attestation_pop_signing_alg_values_supported":[
++ "ES256"
++ ],
++ "authorization_response_iss_parameter_supported":true,
+ "response_types_supported":[
+ "code"
+ ...
+ "GeantIncubatorDiploma_W3C",
+ "GeantIncubatorDiploma_DIDWEB",
+- "GeantIncubatorDiploma_URL"
++ "GeantIncubatorDiploma_URL",
++ "GeantIncubatorDiploma_HAIP",
++ "GeantIncubatorDiploma_MDOC"
+ ],
+ "authorization_details_types_supported":[
```
+Full file: [static/oauth-authorization-server.json](docs/example/step-5/static/oauth-authorization-server.json)
+
</details>
### Step 6: Rolling the signing key
@@ -1922,8 +2081,7 @@ them in that list too.
| `GeantIncubatorDiploma_HAIP` | `https://issuer.example.org` | `x5c` | no | HAIP wallet |
| `GeantIncubatorDiploma_MDOC` | none | `x5chain` | no | HAIP wallet |
-The whole *conf/relying-party.xml* is in [Relying party configuration](#relying-party-configuration)
-and the whole *etc/jetty-rewrite-rules.xml* in [Discovery documents](#discovery-documents).
+The files at the end of the story are in [docs/example/step-5](docs/example/step-5).
## Requesting a credential by configuration or by identifier
@@ -2554,196 +2712,12 @@ Add the credential configuration ids to `scopes_supported`. For Wallet Attestati
</details>
-<details>
-<summary>Example contents of etc/jetty-rewrite-rules.xml</summary>
-
-```xml
-<?xml version="1.0"?>
-<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
-<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
- <!-- Credential Issuer metadata comes from this plugin -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/openid-credential-issuer</Set>
- <Set name="replacement">/idp/profile/openid/vci/issuer-metadata</Set>
- </New>
- </Arg>
- </Call>
- <!-- OP metadata comes from the OP plugin -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/openid-configuration</Set>
- <Set name="replacement">/idp/profile/oidc/configuration</Set>
- </New>
- </Arg>
- </Call>
- <!-- These two come from this plugin as well -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
- <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
- </New>
- </Arg>
- </Call>
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/oauth-authorization-server</Set>
- <Set name="replacement">/idp/profile/oauth/authorization-server</Set>
- </New>
- </Arg>
- </Call>
- <!-- Needed only for did:web credentials -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/did.json</Set>
- <Set name="replacement">/idp/profile/did/document</Set>
- </New>
- </Arg>
- </Call>
- <!-- Needed only when the federation plugin publishes an Entity Configuration -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/.well-known/openid-federation</Set>
- <Set name="replacement">/idp/profile/oidfed/entity-configuration</Set>
- </New>
- </Arg>
- </Call>
- <!-- The JSON-LD context of the vc+sd-jwt credential is a static file of Jetty -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
- <Set name="pattern">/credentials/GeantIncubatorDiploma_W3C</Set>
- <Set name="replacement">/credentials/GeantIncubatorDiploma_W3C.json</Set>
- </New>
- </Arg>
- </Call>
- <!-- Wallets running in a browser need this -->
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
- <Set name="pattern">/*</Set>
- <Set name="headerName">Access-Control-Allow-Origin</Set>
- <Set name="headerValue">*</Set>
- </New>
- </Arg>
- </Call>
-</Configure>
-```
-
-</details>
+The whole *etc/jetty-rewrite-rules.xml* at the end of the story: [etc/jetty-rewrite-rules.xml](docs/example/step-5/etc/jetty-rewrite-rules.xml).
### Relying party configuration
**File(s):** *conf/relying-party.xml*
**Format:** Native Spring
-The file as it is at the end of [First installation, by example](#first-installation-by-example).
-
-<details>
-<summary>Example contents of conf/relying-party.xml</summary>
-
-```xml
-...
- <bean id="shibboleth.UnverifiedRelyingParty" parent="RelyingParty">
- <property name="profileConfigurations">
- <list>
- <ref bean="OIDC.Keyset" />
- <ref bean="OIDC.Configuration" />
- <ref bean="OAUTH2.TokenAudience" />
- <!-- Anyone may read the Credential Issuer metadata -->
- <ref bean="OpenID.VCI.IssuerMetadata" />
- <!-- Anyone may read the keys that signed a credential -->
- <ref bean="OAuth.JWTVCIssuer" />
- <!-- Anyone may read the did:web document, needed for did:web credentials -->
- <ref bean="DID.Document" />
- <!-- Anyone may read the authorization server metadata -->
- <ref bean="OAuth.AuthorizationServer" />
- <!-- Anyone may ask for a nonce -->
- <ref bean="OpenID.VCI.Nonce" />
- <!-- Anyone may read a Status List -->
- <ref bean="OAuth.StatusList" />
- <!-- Anyone may read the Entity Configuration, of the federation plugin -->
- <ref bean="OIDFED.Configuration" />
- </list>
- </property>
- </bean>
-
- <!-- This plugin does not need anything of shibboleth.DefaultRelyingParty -->
-
- <util:list id="shibboleth.RelyingPartyOverrides">
-
- <!-- The caller that creates Credential Offers and revokes what was issued -->
- <bean parent="RelyingPartyByName" c:relyingPartyIds="offer-caller">
- <property name="profileConfigurations">
- <list>
- <bean parent="OpenID.VCI.CredentialOffer"
- p:tokenEndpointAuthMethods="#{{'client_secret_basic'}}" />
- <bean parent="OAuth.StatusList.Revoke"
- p:tokenEndpointAuthMethods="#{{'client_secret_basic'}}" />
- <bean parent="OAuth.StatusList.Assignments"
- p:tokenEndpointAuthMethods="#{{'client_secret_basic'}}" />
- </list>
- </property>
- </bean>
-
- <!-- Wallets that redeem a pre-authorized code created by offer-caller -->
- <bean parent="RelyingPartyByName" c:relyingPartyIds="offer-caller/wallet">
- <property name="profileConfigurations">
- <list>
- <bean parent="OpenID.VCI.Token"
- p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:tokenEndpointAuthMethods="#{{'none'}}" />
- <bean parent="OpenID.VCI.Credentials" />
- </list>
- </property>
- </bean>
-
- <!-- A registered wallet running the authorization code flow -->
- <bean parent="RelyingPartyByName"
- c:relyingPartyIds="#{{'id.animo.paradym'}}">
- <property name="profileConfigurations">
- <list>
- <bean parent="OIDC.SSO"
- p:authorizationCodeClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:forcePKCE="true"
- p:requirePushedAuthorizationRequest="true" />
- <bean parent="OpenID.VCI.Token"
- p:tokenEndpointAuthMethods="#{{'none'}}"
- p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
- <bean parent="OpenID.VCI.Credentials" />
- <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
- </list>
- </property>
- </bean>
-
- <!-- HAIP wallets, authenticating with a Wallet Attestation -->
- <bean parent="RelyingPartyByName"
- c:relyingPartyIds="#{{'haip-wallet'}}">
- <property name="profileConfigurations">
- <list>
- <bean parent="OIDC.SSO"
- p:authorizationCodeClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:forcePKCE="true" p:requirePushedAuthorizationRequest="true"
- p:includeIssuerInResponse="true" p:authorizeCodeLifetime="PT1M" />
- <bean parent="OpenID.VCI.Token"
- p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
- p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}" />
- <bean parent="OpenID.VCI.Credentials" />
- <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
- </list>
- </property>
- </bean>
-
- </util:list>
-...
-```
-
-</details>
+The file as it is at the end of [First installation, by example](#first-installation-by-example):
+[conf/relying-party.xml](docs/example/step-5/conf/relying-party.xml).
diff --git a/docs/example/step-1/conf/relying-party.xml b/docs/example/step-1/conf/relying-party.xml
index 87e6a74..017cf48 100644
--- a/docs/example/step-1/conf/relying-party.xml
+++ b/docs/example/step-1/conf/relying-party.xml
@@ -35,10 +35,6 @@
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
- <ref bean="SAML2.SSO" />
- <ref bean="SAML2.ECP" />
- <ref bean="SAML2.Logout" />
- <ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
diff --git a/docs/example/step-2/conf/relying-party.xml b/docs/example/step-2/conf/relying-party.xml
index c042fdb..df0e9ac 100644
--- a/docs/example/step-2/conf/relying-party.xml
+++ b/docs/example/step-2/conf/relying-party.xml
@@ -37,10 +37,6 @@
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
- <ref bean="SAML2.SSO" />
- <ref bean="SAML2.ECP" />
- <ref bean="SAML2.Logout" />
- <ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
diff --git a/docs/example/step-3/conf/attribute-filter.xml b/docs/example/step-3/conf/attribute-filter.xml
new file mode 100644
index 0000000..b10fb12
--- /dev/null
+++ b/docs/example/step-3/conf/attribute-filter.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AttributeFilterPolicyGroup id="ShibbolethFilterPolicy"
+ xmlns="urn:mace:shibboleth:2.0:afp"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
+
+ <AttributeFilterPolicy id="GeantIncubatorDiplomaToWallets">
+ <PolicyRequirementRule xsi:type="Requester"
+ value="id.animo.paradym" />
+ <AttributeRule attributeID="GeantIncubatorDiploma">
+ <PermitValueRule xsi:type="ANY" />
+ </AttributeRule>
+ </AttributeFilterPolicy>
+
+</AttributeFilterPolicyGroup>
diff --git a/docs/example/step-3/conf/openid-vci-credentials.xml b/docs/example/step-3/conf/openid-vci-credentials.xml
new file mode 100644
index 0000000..09dbf29
--- /dev/null
+++ b/docs/example/step-3/conf/openid-vci-credentials.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <bean id="openidvci.OPESSigningCredential" parent="shibboleth.JWKCredential"
+ p:resource="%{idp.signing.oidc.es.key}" />
+
+ <util:list id="openidvci.SigningCredentials">
+ <ref bean="openidvci.OPESSigningCredential" />
+ </util:list>
+
+</beans>
diff --git a/docs/example/step-2/conf/relying-party.xml b/docs/example/step-3/conf/relying-party.xml
similarity index 96%
copy from docs/example/step-2/conf/relying-party.xml
copy to docs/example/step-3/conf/relying-party.xml
index c042fdb..6bf8645 100644
--- a/docs/example/step-2/conf/relying-party.xml
+++ b/docs/example/step-3/conf/relying-party.xml
@@ -30,6 +30,8 @@
<ref bean="OAUTH2.TokenAudience" />
<!-- A verifier resolves the did:web identifier from this -->
<ref bean="DID.Document" />
+ <!-- A verifier finds the key of a url issuer through this -->
+ <ref bean="OAuth.JWTVCIssuer" />
</list>
</property>
</bean>
@@ -37,10 +39,6 @@
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
- <ref bean="SAML2.SSO" />
- <ref bean="SAML2.ECP" />
- <ref bean="SAML2.Logout" />
- <ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
diff --git a/docs/example/step-3/etc/jetty-rewrite-rules.xml b/docs/example/step-3/etc/jetty-rewrite-rules.xml
new file mode 100644
index 0000000..b5b91fa
--- /dev/null
+++ b/docs/example/step-3/etc/jetty-rewrite-rules.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
+<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
+ <!-- Credential Issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-credential-issuer</Set>
+ <Set name="replacement">/idp/profile/openid/vci/issuer-metadata</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Authorization server metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/oauth-authorization-server</Set>
+ <Set name="replacement">/idp/profile/oauth/authorization-server</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- OP metadata, of the OP plugin -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-configuration</Set>
+ <Set name="replacement">/idp/profile/oidc/configuration</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The JSON-LD context of the vc+sd-jwt credential is a static file of Jetty -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/credentials/GeantIncubatorDiploma_W3C</Set>
+ <Set name="replacement">/credentials/GeantIncubatorDiploma_W3C.json</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The did:web document -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/did.json</Set>
+ <Set name="replacement">/idp/profile/did/document</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- JWT VC issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
+ <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Wallets running in a browser need this -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
+ <Set name="pattern">/*</Set>
+ <Set name="headerName">Access-Control-Allow-Origin</Set>
+ <Set name="headerValue">*</Set>
+ </New>
+ </Arg>
+ </Call>
+</Configure>
diff --git a/docs/example/step-3/metadata/oidc-client.json b/docs/example/step-3/metadata/oidc-client.json
new file mode 100644
index 0000000..57f62c0
--- /dev/null
+++ b/docs/example/step-3/metadata/oidc-client.json
@@ -0,0 +1,29 @@
+[
+ {
+ "client_id": "offer-caller",
+ "client_name": "Credential offer caller",
+ "client_secret": "CHANGE-ME"
+ },
+ {
+ "client_id": "offer-caller/wallet",
+ "client_name": "Wallets redeeming a pre-authorized code minted by offer-caller",
+ "token_endpoint_auth_method": "none",
+ "grant_types": ["urn:ietf:params:oauth:grant-type:pre-authorized_code"]
+ },
+ {
+ "client_id": "id.animo.paradym",
+ "client_name": "Paradym Wallet",
+ "redirect_uris": ["https://paradym.id/invitation/redirect"],
+ "scope": "openid GeantIncubatorDiploma_SDJWT GeantIncubatorDiploma_W3C GeantIncubatorDiploma_DIDWEB GeantIncubatorDiploma_URL",
+ "audience": [
+ "credentials",
+ "urn:shibboleth.oidc.op.resource:https://issuer.example.org"
+ ],
+ "token_endpoint_auth_method": "none",
+ "response_types": ["code"],
+ "grant_types": [
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ]
+ }
+]
diff --git a/docs/example/step-3/metadata/verifiable-credentials.json b/docs/example/step-3/metadata/verifiable-credentials.json
new file mode 100644
index 0000000..d76ae38
--- /dev/null
+++ b/docs/example/step-3/metadata/verifiable-credentials.json
@@ -0,0 +1,123 @@
+{
+ "credential_configurations_supported": {
+ "GeantIncubatorDiploma_SDJWT": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_SDJWT",
+ "scope": "GeantIncubatorDiploma_SDJWT",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_W3C": {
+ "format": "vc+sd-jwt",
+ "scope": "GeantIncubatorDiploma_W3C",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_definition": {
+ "@context": [
+ "https://www.w3.org/ns/credentials/v2",
+ "https://issuer.example.org/credentials/GeantIncubatorDiploma_W3C"
+ ],
+ "type": [
+ "VerifiableCredential",
+ "GeantIncubatorDiploma_W3C"
+ ]
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma, W3C",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["credentialSubject","diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["credentialSubject","mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["credentialSubject","eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["credentialSubject","givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["credentialSubject","familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["credentialSubject","affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_DIDWEB": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_DIDWEB",
+ "scope": "GeantIncubatorDiploma_DIDWEB",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "did:web"
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, did:web", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_URL": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_URL",
+ "scope": "GeantIncubatorDiploma_URL",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "url"
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, url", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ }
+ }
+}
diff --git a/docs/example/step-3/static/oauth-authorization-server.json b/docs/example/step-3/static/oauth-authorization-server.json
new file mode 100644
index 0000000..6f5ae89
--- /dev/null
+++ b/docs/example/step-3/static/oauth-authorization-server.json
@@ -0,0 +1,39 @@
+{
+ "issuer":"$issuer",
+ "authorization_endpoint":"$baseUrl/idp/profile/oidc/authorize",
+ "pushed_authorization_request_endpoint":"$baseUrl/idp/profile/oauth2/pushed-authorization",
+ "require_pushed_authorization_requests":true,
+ "token_endpoint":"$baseUrl/idp/profile/openid/vci/token",
+ "jwks_uri":"$baseUrl/idp/profile/oidc/keyset",
+ "token_endpoint_auth_methods_supported":[
+ "none"
+ ],
+ "response_types_supported":[
+ "code"
+ ],
+ "grant_types_supported":[
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ],
+ "code_challenge_methods_supported":[
+ "S256"
+ ],
+ "dpop_signing_alg_values_supported":[
+ "ES256",
+ "ES384",
+ "ES512",
+ "PS256",
+ "PS384",
+ "PS512"
+ ],
+ "scopes_supported":[
+ "openid",
+ "GeantIncubatorDiploma_SDJWT",
+ "GeantIncubatorDiploma_W3C",
+ "GeantIncubatorDiploma_DIDWEB",
+ "GeantIncubatorDiploma_URL"
+ ],
+ "authorization_details_types_supported":[
+ "openid_credential"
+ ]
+}
diff --git a/docs/example/step-3/webapps/root/credentials/GeantIncubatorDiploma_W3C.json b/docs/example/step-3/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
new file mode 100644
index 0000000..3f2fb9a
--- /dev/null
+++ b/docs/example/step-3/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
@@ -0,0 +1,10 @@
+{
+ "@context": {
+ "diploma": "https://vocabulary.example.org/terms#diploma",
+ "mail": "https://vocabulary.example.org/terms#mail",
+ "eppn": "https://vocabulary.example.org/terms#eppn",
+ "givenName": "https://vocabulary.example.org/terms#givenName",
+ "familyName": "https://vocabulary.example.org/terms#familyName",
+ "affiliation": "https://vocabulary.example.org/terms#affiliation"
+ }
+}
diff --git a/docs/example/step-4/conf/attribute-filter.xml b/docs/example/step-4/conf/attribute-filter.xml
new file mode 100644
index 0000000..b10fb12
--- /dev/null
+++ b/docs/example/step-4/conf/attribute-filter.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AttributeFilterPolicyGroup id="ShibbolethFilterPolicy"
+ xmlns="urn:mace:shibboleth:2.0:afp"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
+
+ <AttributeFilterPolicy id="GeantIncubatorDiplomaToWallets">
+ <PolicyRequirementRule xsi:type="Requester"
+ value="id.animo.paradym" />
+ <AttributeRule attributeID="GeantIncubatorDiploma">
+ <PermitValueRule xsi:type="ANY" />
+ </AttributeRule>
+ </AttributeFilterPolicy>
+
+</AttributeFilterPolicyGroup>
diff --git a/docs/example/step-4/conf/openid-vci-credentials.xml b/docs/example/step-4/conf/openid-vci-credentials.xml
new file mode 100644
index 0000000..09dbf29
--- /dev/null
+++ b/docs/example/step-4/conf/openid-vci-credentials.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <bean id="openidvci.OPESSigningCredential" parent="shibboleth.JWKCredential"
+ p:resource="%{idp.signing.oidc.es.key}" />
+
+ <util:list id="openidvci.SigningCredentials">
+ <ref bean="openidvci.OPESSigningCredential" />
+ </util:list>
+
+</beans>
diff --git a/docs/example/step-2/conf/relying-party.xml b/docs/example/step-4/conf/relying-party.xml
similarity index 94%
copy from docs/example/step-2/conf/relying-party.xml
copy to docs/example/step-4/conf/relying-party.xml
index c042fdb..667d8b1 100644
--- a/docs/example/step-2/conf/relying-party.xml
+++ b/docs/example/step-4/conf/relying-party.xml
@@ -30,6 +30,10 @@
<ref bean="OAUTH2.TokenAudience" />
<!-- A verifier resolves the did:web identifier from this -->
<ref bean="DID.Document" />
+ <!-- A verifier finds the key of a url issuer through this -->
+ <ref bean="OAuth.JWTVCIssuer" />
+ <!-- Of the federation plugin. Serves the Entity Configuration -->
+ <ref bean="OIDFED.Configuration" />
</list>
</property>
</bean>
@@ -37,10 +41,6 @@
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
- <ref bean="SAML2.SSO" />
- <ref bean="SAML2.ECP" />
- <ref bean="SAML2.Logout" />
- <ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
diff --git a/docs/example/step-4/etc/jetty-rewrite-rules.xml b/docs/example/step-4/etc/jetty-rewrite-rules.xml
new file mode 100644
index 0000000..9b8ed66
--- /dev/null
+++ b/docs/example/step-4/etc/jetty-rewrite-rules.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
+<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
+ <!-- Credential Issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-credential-issuer</Set>
+ <Set name="replacement">/idp/profile/openid/vci/issuer-metadata</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Authorization server metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/oauth-authorization-server</Set>
+ <Set name="replacement">/idp/profile/oauth/authorization-server</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- OP metadata, of the OP plugin -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-configuration</Set>
+ <Set name="replacement">/idp/profile/oidc/configuration</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The JSON-LD context of the vc+sd-jwt credential is a static file of Jetty -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/credentials/GeantIncubatorDiploma_W3C</Set>
+ <Set name="replacement">/credentials/GeantIncubatorDiploma_W3C.json</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The did:web document -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/did.json</Set>
+ <Set name="replacement">/idp/profile/did/document</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- JWT VC issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
+ <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Entity Configuration, of the federation plugin -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-federation</Set>
+ <Set name="replacement">/idp/profile/oidfed/entity-configuration</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Wallets running in a browser need this -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
+ <Set name="pattern">/*</Set>
+ <Set name="headerName">Access-Control-Allow-Origin</Set>
+ <Set name="headerValue">*</Set>
+ </New>
+ </Arg>
+ </Call>
+</Configure>
diff --git a/docs/example/step-4/metadata/oidc-client.json b/docs/example/step-4/metadata/oidc-client.json
new file mode 100644
index 0000000..57f62c0
--- /dev/null
+++ b/docs/example/step-4/metadata/oidc-client.json
@@ -0,0 +1,29 @@
+[
+ {
+ "client_id": "offer-caller",
+ "client_name": "Credential offer caller",
+ "client_secret": "CHANGE-ME"
+ },
+ {
+ "client_id": "offer-caller/wallet",
+ "client_name": "Wallets redeeming a pre-authorized code minted by offer-caller",
+ "token_endpoint_auth_method": "none",
+ "grant_types": ["urn:ietf:params:oauth:grant-type:pre-authorized_code"]
+ },
+ {
+ "client_id": "id.animo.paradym",
+ "client_name": "Paradym Wallet",
+ "redirect_uris": ["https://paradym.id/invitation/redirect"],
+ "scope": "openid GeantIncubatorDiploma_SDJWT GeantIncubatorDiploma_W3C GeantIncubatorDiploma_DIDWEB GeantIncubatorDiploma_URL",
+ "audience": [
+ "credentials",
+ "urn:shibboleth.oidc.op.resource:https://issuer.example.org"
+ ],
+ "token_endpoint_auth_method": "none",
+ "response_types": ["code"],
+ "grant_types": [
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ]
+ }
+]
diff --git a/docs/example/step-4/metadata/verifiable-credentials.json b/docs/example/step-4/metadata/verifiable-credentials.json
new file mode 100644
index 0000000..b13d335
--- /dev/null
+++ b/docs/example/step-4/metadata/verifiable-credentials.json
@@ -0,0 +1,130 @@
+{
+ "credential_configurations_supported": {
+ "GeantIncubatorDiploma_SDJWT": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_SDJWT",
+ "scope": "GeantIncubatorDiploma_SDJWT",
+ "_issuance_configuration": {
+ "oidfed": false
+ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_W3C": {
+ "format": "vc+sd-jwt",
+ "scope": "GeantIncubatorDiploma_W3C",
+ "_issuance_configuration": {
+ "oidfed": false
+ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_definition": {
+ "@context": [
+ "https://www.w3.org/ns/credentials/v2",
+ "https://issuer.example.org/credentials/GeantIncubatorDiploma_W3C"
+ ],
+ "type": [
+ "VerifiableCredential",
+ "GeantIncubatorDiploma_W3C"
+ ]
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma, W3C",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["credentialSubject","diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["credentialSubject","mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["credentialSubject","eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["credentialSubject","givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["credentialSubject","familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["credentialSubject","affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_DIDWEB": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_DIDWEB",
+ "scope": "GeantIncubatorDiploma_DIDWEB",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "did:web",
+ "oidfed": false
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, did:web", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_URL": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_URL",
+ "scope": "GeantIncubatorDiploma_URL",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "url"
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, url", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ }
+ }
+}
diff --git a/docs/example/step-4/static/oauth-authorization-server.json b/docs/example/step-4/static/oauth-authorization-server.json
new file mode 100644
index 0000000..6f5ae89
--- /dev/null
+++ b/docs/example/step-4/static/oauth-authorization-server.json
@@ -0,0 +1,39 @@
+{
+ "issuer":"$issuer",
+ "authorization_endpoint":"$baseUrl/idp/profile/oidc/authorize",
+ "pushed_authorization_request_endpoint":"$baseUrl/idp/profile/oauth2/pushed-authorization",
+ "require_pushed_authorization_requests":true,
+ "token_endpoint":"$baseUrl/idp/profile/openid/vci/token",
+ "jwks_uri":"$baseUrl/idp/profile/oidc/keyset",
+ "token_endpoint_auth_methods_supported":[
+ "none"
+ ],
+ "response_types_supported":[
+ "code"
+ ],
+ "grant_types_supported":[
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ],
+ "code_challenge_methods_supported":[
+ "S256"
+ ],
+ "dpop_signing_alg_values_supported":[
+ "ES256",
+ "ES384",
+ "ES512",
+ "PS256",
+ "PS384",
+ "PS512"
+ ],
+ "scopes_supported":[
+ "openid",
+ "GeantIncubatorDiploma_SDJWT",
+ "GeantIncubatorDiploma_W3C",
+ "GeantIncubatorDiploma_DIDWEB",
+ "GeantIncubatorDiploma_URL"
+ ],
+ "authorization_details_types_supported":[
+ "openid_credential"
+ ]
+}
diff --git a/docs/example/step-4/webapps/root/credentials/GeantIncubatorDiploma_W3C.json b/docs/example/step-4/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
new file mode 100644
index 0000000..3f2fb9a
--- /dev/null
+++ b/docs/example/step-4/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
@@ -0,0 +1,10 @@
+{
+ "@context": {
+ "diploma": "https://vocabulary.example.org/terms#diploma",
+ "mail": "https://vocabulary.example.org/terms#mail",
+ "eppn": "https://vocabulary.example.org/terms#eppn",
+ "givenName": "https://vocabulary.example.org/terms#givenName",
+ "familyName": "https://vocabulary.example.org/terms#familyName",
+ "affiliation": "https://vocabulary.example.org/terms#affiliation"
+ }
+}
diff --git a/docs/example/step-5/conf/attribute-filter.xml b/docs/example/step-5/conf/attribute-filter.xml
new file mode 100644
index 0000000..b767242
--- /dev/null
+++ b/docs/example/step-5/conf/attribute-filter.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AttributeFilterPolicyGroup id="ShibbolethFilterPolicy"
+ xmlns="urn:mace:shibboleth:2.0:afp"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
+
+ <AttributeFilterPolicy id="GeantIncubatorDiplomaToWallets">
+ <PolicyRequirementRule xsi:type="Requester"
+ value="id.animo.paradym" />
+ <AttributeRule attributeID="GeantIncubatorDiploma">
+ <PermitValueRule xsi:type="ANY" />
+ </AttributeRule>
+ </AttributeFilterPolicy>
+
+ <AttributeFilterPolicy id="GeantIncubatorDiplomaToHAIPWallets">
+ <PolicyRequirementRule xsi:type="Requester"
+ value="haip-wallet" />
+ <AttributeRule attributeID="GeantIncubatorDiploma">
+ <PermitValueRule xsi:type="ANY" />
+ </AttributeRule>
+ <AttributeRule attributeID="GeantIncubatorDiplomaMdoc">
+ <PermitValueRule xsi:type="ANY" />
+ </AttributeRule>
+ </AttributeFilterPolicy>
+
+</AttributeFilterPolicyGroup>
diff --git a/docs/example/step-5/conf/openid-vci-credentials.xml b/docs/example/step-5/conf/openid-vci-credentials.xml
new file mode 100644
index 0000000..66f03b8
--- /dev/null
+++ b/docs/example/step-5/conf/openid-vci-credentials.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <bean id="openidvci.OPESSigningCredential" parent="shibboleth.JWKCredential"
+ p:resource="%{idp.signing.oidc.es.key}" />
+
+ <bean id="openidvci.X509ESSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
+ p:privateKey="%{idp.home}/credentials/vci-signing.key" p:keyNames="signingvci-x509">
+ <property name="certificates">
+ <list>
+ <value>%{idp.home}/credentials/vci-signing.crt</value>
+ </list>
+ </property>
+ </bean>
+
+ <bean id="openidvci.X509StatusListSigningCredential" parent="shibboleth.BasicX509CredentialFactoryBean"
+ p:privateKey="%{idp.home}/credentials/vci-status-list.key" p:keyNames="statuslist-x509">
+ <property name="certificates">
+ <list>
+ <value>%{idp.home}/credentials/vci-status-list.crt</value>
+ </list>
+ </property>
+ </bean>
+
+ <util:list id="openidvci.SigningCredentials">
+ <ref bean="openidvci.X509ESSigningCredential" />
+ <ref bean="openidvci.OPESSigningCredential" />
+ </util:list>
+
+ <util:list id="openidvci.status-list.SigningCredentials">
+ <ref bean="openidvci.X509StatusListSigningCredential" />
+ <ref bean="openidvci.OPESSigningCredential" />
+ </util:list>
+
+ <util:list id="openidvci.issuerMetadata.SigningCredentials">
+ <ref bean="openidvci.X509ESSigningCredential" />
+ </util:list>
+
+</beans>
diff --git a/docs/example/step-2/conf/relying-party.xml b/docs/example/step-5/conf/relying-party.xml
similarity index 76%
copy from docs/example/step-2/conf/relying-party.xml
copy to docs/example/step-5/conf/relying-party.xml
index c042fdb..9391e3f 100644
--- a/docs/example/step-2/conf/relying-party.xml
+++ b/docs/example/step-5/conf/relying-party.xml
@@ -30,6 +30,10 @@
<ref bean="OAUTH2.TokenAudience" />
<!-- A verifier resolves the did:web identifier from this -->
<ref bean="DID.Document" />
+ <!-- A verifier finds the key of a url issuer through this -->
+ <ref bean="OAuth.JWTVCIssuer" />
+ <!-- Of the federation plugin. Serves the Entity Configuration -->
+ <ref bean="OIDFED.Configuration" />
</list>
</property>
</bean>
@@ -37,10 +41,6 @@
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
- <ref bean="SAML2.SSO" />
- <ref bean="SAML2.ECP" />
- <ref bean="SAML2.Logout" />
- <ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean>
@@ -89,6 +89,24 @@
</property>
</bean>
+ <bean parent="RelyingPartyByName"
+ c:relyingPartyIds="#{{'haip-wallet'}}">
+ <property name="profileConfigurations">
+ <list>
+ <bean parent="OIDC.SSO"
+ p:authorizationCodeClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
+ p:forcePKCE="true" p:requirePushedAuthorizationRequest="true"
+ p:includeIssuerInResponse="true" p:authorizeCodeLifetime="PT1M" />
+ <bean parent="OpenID.VCI.Token"
+ p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy"
+ p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}" />
+ <bean parent="OpenID.VCI.Credentials" />
+ <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
+ p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ </list>
+ </property>
+ </bean>
+
</util:list>
</beans>
diff --git a/docs/example/step-5/etc/jetty-rewrite-rules.xml b/docs/example/step-5/etc/jetty-rewrite-rules.xml
new file mode 100644
index 0000000..9b8ed66
--- /dev/null
+++ b/docs/example/step-5/etc/jetty-rewrite-rules.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
+<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
+ <!-- Credential Issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-credential-issuer</Set>
+ <Set name="replacement">/idp/profile/openid/vci/issuer-metadata</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Authorization server metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/oauth-authorization-server</Set>
+ <Set name="replacement">/idp/profile/oauth/authorization-server</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- OP metadata, of the OP plugin -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-configuration</Set>
+ <Set name="replacement">/idp/profile/oidc/configuration</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The JSON-LD context of the vc+sd-jwt credential is a static file of Jetty -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/credentials/GeantIncubatorDiploma_W3C</Set>
+ <Set name="replacement">/credentials/GeantIncubatorDiploma_W3C.json</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- The did:web document -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/did.json</Set>
+ <Set name="replacement">/idp/profile/did/document</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- JWT VC issuer metadata -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/jwt-vc-issuer</Set>
+ <Set name="replacement">/idp/profile/oauth/jwt-vc-issuer</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Entity Configuration, of the federation plugin -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+ <Set name="pattern">/.well-known/openid-federation</Set>
+ <Set name="replacement">/idp/profile/oidfed/entity-configuration</Set>
+ </New>
+ </Arg>
+ </Call>
+ <!-- Wallets running in a browser need this -->
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
+ <Set name="pattern">/*</Set>
+ <Set name="headerName">Access-Control-Allow-Origin</Set>
+ <Set name="headerValue">*</Set>
+ </New>
+ </Arg>
+ </Call>
+</Configure>
diff --git a/docs/example/step-5/metadata/oidc-client.json b/docs/example/step-5/metadata/oidc-client.json
new file mode 100644
index 0000000..76a3a9c
--- /dev/null
+++ b/docs/example/step-5/metadata/oidc-client.json
@@ -0,0 +1,39 @@
+[
+ {
+ "client_id": "offer-caller",
+ "client_name": "Credential offer caller",
+ "client_secret": "CHANGE-ME"
+ },
+ {
+ "client_id": "offer-caller/wallet",
+ "client_name": "Wallets redeeming a pre-authorized code minted by offer-caller",
+ "token_endpoint_auth_method": "none",
+ "grant_types": ["urn:ietf:params:oauth:grant-type:pre-authorized_code"]
+ },
+ {
+ "client_id": "id.animo.paradym",
+ "client_name": "Paradym Wallet",
+ "redirect_uris": ["https://paradym.id/invitation/redirect"],
+ "scope": "openid GeantIncubatorDiploma_SDJWT GeantIncubatorDiploma_W3C GeantIncubatorDiploma_DIDWEB GeantIncubatorDiploma_URL",
+ "audience": [
+ "credentials",
+ "urn:shibboleth.oidc.op.resource:https://issuer.example.org"
+ ],
+ "token_endpoint_auth_method": "none",
+ "response_types": ["code"],
+ "grant_types": [
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ]
+ },
+ {
+ "client_id": "haip-wallet",
+ "client_name": "HAIP Wallet",
+ "redirect_uris": ["https://wallet.example.org/callback"],
+ "scope": "openid GeantIncubatorDiploma_HAIP GeantIncubatorDiploma_MDOC",
+ "audience": "credentials",
+ "token_endpoint_auth_method": "attest_jwt_client_auth",
+ "response_types": ["code"],
+ "grant_types": ["authorization_code"]
+ }
+]
diff --git a/docs/example/step-5/metadata/verifiable-credentials.json b/docs/example/step-5/metadata/verifiable-credentials.json
new file mode 100644
index 0000000..8fcfaf1
--- /dev/null
+++ b/docs/example/step-5/metadata/verifiable-credentials.json
@@ -0,0 +1,197 @@
+{
+ "credential_configurations_supported": {
+ "GeantIncubatorDiploma_SDJWT": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_SDJWT",
+ "scope": "GeantIncubatorDiploma_SDJWT",
+ "_issuance_configuration": {
+ "oidfed": false
+ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_W3C": {
+ "format": "vc+sd-jwt",
+ "scope": "GeantIncubatorDiploma_W3C",
+ "_issuance_configuration": {
+ "oidfed": false
+ },
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "credential_definition": {
+ "@context": [
+ "https://www.w3.org/ns/credentials/v2",
+ "https://issuer.example.org/credentials/GeantIncubatorDiploma_W3C"
+ ],
+ "type": [
+ "VerifiableCredential",
+ "GeantIncubatorDiploma_W3C"
+ ]
+ },
+ "credential_metadata": {
+ "display": [
+ {
+ "name": "Incubator Diploma, W3C",
+ "locale": "en",
+ "description": "Diploma issued by the Trust and Identity Incubator"
+ }
+ ],
+ "claims": [
+ { "path": ["credentialSubject","diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["credentialSubject","mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["credentialSubject","eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["credentialSubject","givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["credentialSubject","familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["credentialSubject","affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_DIDWEB": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_DIDWEB",
+ "scope": "GeantIncubatorDiploma_DIDWEB",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "did:web",
+ "oidfed": false
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, did:web", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_URL": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_URL",
+ "scope": "GeantIncubatorDiploma_URL",
+ "cryptographic_binding_methods_supported": ["did:jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"]
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "url"
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, url", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_HAIP": {
+ "format": "dc+sd-jwt",
+ "vct": "GeantIncubatorDiploma_HAIP",
+ "scope": "GeantIncubatorDiploma_HAIP",
+ "cryptographic_binding_methods_supported": ["jwk"],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"],
+ "key_attestations_required": {
+ "key_storage": ["iso_18045_moderate"],
+ "user_authentication": ["iso_18045_moderate"]
+ }
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "url",
+ "oidfed": false
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, HAIP", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["eppn"], "display": [{ "name": "Principal name", "locale": "en" }] },
+ { "path": ["givenName"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["familyName"], "display": [{ "name": "Family name", "locale": "en" }] },
+ { "path": ["affiliation"], "display": [{ "name": "Affiliation", "locale": "en" }] }
+ ]
+ }
+ },
+ "GeantIncubatorDiploma_MDOC": {
+ "format": "mso_mdoc",
+ "doctype": "org.geant.incubator.diploma.1",
+ "scope": "GeantIncubatorDiploma_MDOC",
+ "cryptographic_binding_methods_supported": ["cose_key"],
+ "credential_signing_alg_values_supported": [-7],
+ "proof_types_supported": {
+ "jwt": {
+ "proof_signing_alg_values_supported": ["ES256"],
+ "key_attestations_required": {
+ "key_storage": ["iso_18045_moderate"],
+ "user_authentication": ["iso_18045_moderate"]
+ }
+ }
+ },
+ "_issuance_configuration": {
+ "issuer": "url",
+ "lifetime": "P45D",
+ "claim_types": [
+ { "path": ["org.geant.incubator.1", "issue_date"], "type": "full-date" }
+ ]
+ },
+ "credential_metadata": {
+ "display": [
+ { "name": "Incubator Diploma, mdoc", "locale": "en" }
+ ],
+ "claims": [
+ { "path": ["org.geant.incubator.1", "diploma"], "display": [{ "name": "Diploma", "locale": "en" }] },
+ { "path": ["org.geant.incubator.1", "issue_date"], "display": [{ "name": "Issued", "locale": "en" }] },
+ { "path": ["org.geant.incubator.1", "mail"], "display": [{ "name": "Email", "locale": "en" }] },
+ { "path": ["org.geant.incubator.1", "given_name"], "display": [{ "name": "Given names", "locale": "en" }] },
+ { "path": ["org.geant.incubator.1", "family_name"], "display": [{ "name": "Family name", "locale": "en" }] }
+ ]
+ }
+ }
+ }
+}
diff --git a/docs/example/step-5/static/oauth-authorization-server.json b/docs/example/step-5/static/oauth-authorization-server.json
new file mode 100644
index 0000000..1aa03ac
--- /dev/null
+++ b/docs/example/step-5/static/oauth-authorization-server.json
@@ -0,0 +1,49 @@
+{
+ "issuer":"$issuer",
+ "authorization_endpoint":"$baseUrl/idp/profile/oidc/authorize",
+ "pushed_authorization_request_endpoint":"$baseUrl/idp/profile/oauth2/pushed-authorization",
+ "require_pushed_authorization_requests":true,
+ "token_endpoint":"$baseUrl/idp/profile/openid/vci/token",
+ "jwks_uri":"$baseUrl/idp/profile/oidc/keyset",
+ "token_endpoint_auth_methods_supported":[
+ "none",
+ "attest_jwt_client_auth"
+ ],
+ "client_attestation_signing_alg_values_supported":[
+ "ES256"
+ ],
+ "client_attestation_pop_signing_alg_values_supported":[
+ "ES256"
+ ],
+ "authorization_response_iss_parameter_supported":true,
+ "response_types_supported":[
+ "code"
+ ],
+ "grant_types_supported":[
+ "authorization_code",
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"
+ ],
+ "code_challenge_methods_supported":[
+ "S256"
+ ],
+ "dpop_signing_alg_values_supported":[
+ "ES256",
+ "ES384",
+ "ES512",
+ "PS256",
+ "PS384",
+ "PS512"
+ ],
+ "scopes_supported":[
+ "openid",
+ "GeantIncubatorDiploma_SDJWT",
+ "GeantIncubatorDiploma_W3C",
+ "GeantIncubatorDiploma_DIDWEB",
+ "GeantIncubatorDiploma_URL",
+ "GeantIncubatorDiploma_HAIP",
+ "GeantIncubatorDiploma_MDOC"
+ ],
+ "authorization_details_types_supported":[
+ "openid_credential"
+ ]
+}
diff --git a/docs/example/step-5/webapps/root/credentials/GeantIncubatorDiploma_W3C.json b/docs/example/step-5/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
new file mode 100644
index 0000000..3f2fb9a
--- /dev/null
+++ b/docs/example/step-5/webapps/root/credentials/GeantIncubatorDiploma_W3C.json
@@ -0,0 +1,10 @@
+{
+ "@context": {
+ "diploma": "https://vocabulary.example.org/terms#diploma",
+ "mail": "https://vocabulary.example.org/terms#mail",
+ "eppn": "https://vocabulary.example.org/terms#eppn",
+ "givenName": "https://vocabulary.example.org/terms#givenName",
+ "familyName": "https://vocabulary.example.org/terms#familyName",
+ "affiliation": "https://vocabulary.example.org/terms#affiliation"
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list