[java-idp-plugin-vci] branch main updated: Improve readability of prose parts

Codeberg noreply at shibboleth.net
Sun Nov 1 09:49:39 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-idp-plugin-vci.

View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/eaaf421b98b07079acaaed2def673c3324edc9ba

The following commit(s) were added to refs/heads/main by this push:
     new eaaf421  Improve readability of prose parts
eaaf421 is described below

commit eaaf421b98b07079acaaed2def673c3324edc9ba
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Sun Nov 1 11:49:24 2026 +0200

    Improve readability of prose parts
---
 README.md | 319 +++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 161 insertions(+), 158 deletions(-)

diff --git a/README.md b/README.md
index 92ced4f..a6ce905 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 > **Note**
 > This plugin is under construction and all the features are still subject to change.
-> This README pretends to be a wiki that it is not. It is very much WIP and a challenging read.
+> This README is still a work in progress. It is not yet a complete wiki, and some sections may be difficult to follow.
 
 ## Contents
 
@@ -24,8 +24,8 @@
     - [Authorization code flow](#authorization-code-flow)
     - [Status List](#status-list)
   - [Step 2: Add a did:web credential](#step-2-add-a-did-web-credential)
-  - [Step 3: Add a plain url credential](#step-3-add-a-plain-url-credential)
-  - [Step 4: Make the url credential an OpenID Federation credential](#step-4-make-the-url-credential-an-openid-federation-credential)
+  - [Step 3: Add a plain URL-based credential](#step-3-add-a-plain-url-credential)
+  - [Step 4: Make the URL-based credential an OpenID Federation credential](#step-4-make-the-url-credential-an-openid-federation-credential)
   - [What we have now](#what-we-have-now)
   - [Step 5: A HAIP setup](#step-5-a-haip-setup)
   - [Step 6: Rolling the signing key](#step-6-rolling-the-signing-key)
@@ -49,27 +49,28 @@
 
 ## Overview
 
-Plugin that implements [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
-for Shibboleth Identity Provider. It makes the IdP a Credential Issuer. A wallet gets an access
-token from the IdP and exchanges it for a Verifiable Credential built of IdP attributes.
+This plugin implements [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
+for Shibboleth Identity Provider (IdP). It enables the IdP to act as a Credential Issuer. A wallet
+gets an access token from the IdP and exchanges it for a Verifiable Credential containing IdP attributes.
 
-We target first the [Decentralized Identity Interop Profile 5](https://fidescommunity.github.io/DIIP/).
-Lately we have worked also on
+Our first target is the [Decentralized Identity Interop Profile 5](https://fidescommunity.github.io/DIIP/).
+We have also worked on
 [OpenID4VC High Assurance Interoperability Profile 1.0](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-final.html).
-What is supported and what not is listed per specification in [Capabilities](#capabilities).
+The [Capabilities](#capabilities) section lists the supported and unsupported features of each specification.
 
 Both issuance flows are implemented:
 
-- the [Pre-Authorized Code Flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow),
-  where something in your deployment creates a Credential Offer for a user that has already
-  authenticated and the wallet redeems the code of that offer
-- the [Authorization Code Flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow),
-  where the wallet makes a normal OAuth2 authorization request and the credential
-  content comes in an IdP attribute
+- The [Pre-Authorized Code Flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow):
+  a component in your deployment creates a Credential Offer for an already authenticated user.
+  The wallet redeems the code in that offer.
+- The [Authorization Code Flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow):
+  the wallet sends a standard OAuth 2.0 authorization request. The credential content comes from
+  an IdP attribute.
 
-Three credential formats are issued, `dc+sd-jwt` (IETF SD-JWT VC), `vc+sd-jwt` ([W3C VCDM 2.0](https://www.w3.org/TR/vc-data-model-2.0/)
-enveloped in SD-JWT) and `mso_mdoc` (ISO mdoc). All three do selective disclosure. Revocation is
-published as [IETF Token Status List](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/).
+The plugin issues credentials in three formats: `dc+sd-jwt` (IETF SD-JWT VC), `vc+sd-jwt`
+([W3C VCDM 2.0](https://www.w3.org/TR/vc-data-model-2.0/) wrapped in SD-JWT), and `mso_mdoc`
+(ISO mdoc). All three support selective disclosure. Revocation information is published through
+the [IETF Token Status List](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/).
 
 Things you need to know about this add-on:
 
@@ -78,12 +79,13 @@ Things you need to know about this add-on:
 
 ## Capabilities
 
-Issuer side only. **Yes** is built and tested with some wallet, **Partial** is built with
-limitations, **No** is not built and **No, by design** is left out on purpose.
+This table covers the issuer side only. **Yes** means the feature is implemented and has been
+tested with at least one wallet. **Partial** means it is implemented with limitations. **No** means
+it is not implemented. **No, by design** means the feature is intentionally not supported.
 
 ### [OpenID for Verifiable Credential Issuance 1.0](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
 
-Optional currently not supported features.
+The following optional features are not currently supported.
 
 | Feature | Status |
 |---|---|
@@ -160,7 +162,7 @@ Optional currently not supported features.
 > **Info**
 > This plugin is not released. Until it is released,
 > the distribution is in directory *download/* (only seldom updated) of this repository and you install it from
-> there by url, see below.
+> there by URL, see below.
 
 | Plugin | Plugin ID | Module(s) | Depends on | Bug Reporting |
 |---|---|---|---|---|
@@ -191,20 +193,19 @@ Requires IdP **5.2.3** or later.
 
 ### Installation example
 
-The dependencies are installed as the wiki page above says. What is left is this plugin, and
-it is not on the status page as it has no release. Its distribution, the signature of the
-distribution and the key that signed it are in directory *download/* of this repository:
+After installing the dependencies as described on the wiki page above, install this plugin.
+It has not been released yet, so it does not appear on the status page. The plugin archive, its
+signature, and the signing key are in this repository's *download/* directory:
 
 | File | Description |
 |---|---|
 | *shibboleth-idp-plugin-openid-vci-0.9.0-SNAPSHOT.tar.gz* | The distribution. |
-| *shibboleth-idp-plugin-openid-vci-0.9.0-SNAPSHOT.tar.gz.asc* | Signature of the distribution. `plugin.sh` fetches it from the same directory by itself. |
+| *shibboleth-idp-plugin-openid-vci-0.9.0-SNAPSHOT.tar.gz.asc* | Signature of the distribution. `plugin.sh` fetches it from the same directory automatically. |
 | *keys.txt* | The key that signed the distribution. It becomes the truststore of the plugin. |
 
-The distribution is uploaded there by hand and seldom. Nothing builds it from every commit and
-there is no nightly build of it, so what you install is the state of some point in the past and
-not the head of this repository. If you need something newer than that you build the plugin
-yourself.
+The archive is uploaded manually and is updated infrequently. It is not implemented for every commit,
+and there is no nightly build. Therefore, the archive may not contain the latest repository changes.
+If you need a newer version, build the plugin yourself.
 
 <details>
 <summary>Example installation of this plugin</summary>
@@ -237,54 +238,56 @@ Installing the plugin activates module `OpenID-VCI` and creates six files:
 | *static/jwt-vc-issuer.json* | Keys that sign the credentials, for a verifier. As a Velocity template. |
 | *static/oauth-authorization-server.json* | Authorization server metadata of the VCI endpoints, for a wallet. As a Velocity template. |
 
-One file you need to write yourself, *metadata/verifiable-credentials.json*, the Credential
-Configurations. There is no template for it. See [Credential Configurations](#credential-configurations).
+You must create one file yourself: *metadata/verifiable-credentials.json*. It contains the
+Credential Configurations, and the plugin does not provide a template. See
+[Credential Configurations](#credential-configurations).
 
 ## First installation, by example
 
-This chapter builds a DIIP deployment in Steps 1 to 4 and a HAIP deployment in Step 5. Steps 2
-to 4 each add something to the previous one and tell only what is new. Step 5 is not built on
-Steps 2 to 4, it starts again from Step 1. The examples are from a running test deployment so they
-fit together, but it is not guaranteed that they work for you as such.
+Steps 1–4 build a DIIP deployment. Step 5 describes a separate HAIP deployment. Each of Steps 2–4
+adds to the setup from the previous step and explains only the changes. Step 5 starts again from
+Step 1; it does not depend on Steps 2–4. The examples come from a working test deployment, so they
+are consistent with each other, but they may need changes for your environment.
 
-Before you start, the OP needs an EC signing key, see
+Before you start, make sure the OP has an EC signing key. See
 [Key Generation](https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376878976/OIDC+OP#Key-Generation)
-of the OIDC OP wiki page. A normal installation of the OP plugin has one,
-*credentials/idp-signing-es.jwk*. Steps 1 to 4 sign with it.
+in the OIDC OP wiki. A standard installation of the OP plugin includes
+*credentials/idp-signing-es.jwk*. Steps 1–4 use this key to sign credentials.
 
 1. A did:jwk credential with both flows and the Status List. Signed with the key of the OP.
 2. A second credential, did:web.
-3. A third credential, the issuer is the plain url.
+3. A third credential, the issuer is the plain URL.
 4. The third credential is made an OpenID Federation credential.
    [What we have now](#what-we-have-now) sums up the DIIP setup.
 5. A HAIP setup on its own, two credentials as SD-JWT VC and as mdoc. The signing key has a
    certificate chain.
 6. Rolling the signing key to the next one, in either setup.
 
-The wallet of steps 1 and 2 is [Paradym](https://paradym.id). Paradym accepts only did and x5c
-issuers, so the url credential of steps 3 and 4 goes to the TOPPAN wallet. The HAIP credentials of
-Step 5 go to the Hopae EUDI Wallet, and they are also tested with the OpenID conformance suite.
+Steps 1 and 2 use the [Paradym](https://paradym.id) wallet. Paradym accepts only `did` and `x5c`
+issuers, so use the TOPPAN wallet for the URL-based credential in Steps 3 and 4. Use the Hopae EUDI
+Wallet for the HAIP credentials in Step 5. Those credentials have also been tested with the OpenID
+conformance suite.
 
 `https://issuer.example.org` is your IdP's OIDC issuer everywhere below.
 
 ### Step 1: did:jwk credential, both flows
 
-The issuer of the first credential is `did:jwk`, which is the default. The issuer identifier is the key
-itself and it travels in the credential, so there is nothing to publish for a verifier. The
-credential is signed with the EC key of the OP, *credentials/idp-signing-es.jwk* of a normal
-installation, and nothing about keys needs to be configured yet.
+The first credential uses `did:jwk` as its issuer ID, which is the default. The identifier
+is derived from the key and is included in the credential, so you do not need to publish a separate
+issuer document for verifiers. The plugin signs the credential with the OP's EC key. In a standard
+installation, this is *credentials/idp-signing-es.jwk*. No additional key configuration is needed yet.
 
 #### Credential Configurations
 
 **File(s):** *metadata/verifiable-credentials.json*
 **Format:** JSON
 
-The file is published as `credential_configurations_supported` of your Credential Issuer
-metadata, and the plugin builds the credentials by it.
+The plugin publishes this file as `credential_configurations_supported` in the Credential Issuer
+metadata and uses it to build credentials.
 
-The content of the file is defined in
-[Credential Issuer Metadata](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-issuer-metadata)
-of the specification, per credential format. Below only the members that need explaining.
+The specification defines this file's structure in
+[Credential Issuer Metadata](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-issuer-metadata),
+with details that depend on the credential format. The notes below explain only the members that need clarification.
 
 <details>
 <summary>metadata/verifiable-credentials.json</summary>
@@ -398,13 +401,13 @@ Full file: [metadata/verifiable-credentials.json](docs/example/step-1/metadata/v
 
 </details>
 
-The configuration id, `GeantIncubatorDiploma_SDJWT` here, is used everywhere. A wallet uses it
-in a credential request, it is the `scope` value and the claim values in the access token are
-keyed with it. Use exactly the same string in all places.
+The configuration ID, `GeantIncubatorDiploma_SDJWT` in this example, is used in several places.
+The wallet includes it in credential requests, it is used as the `scope` value, and claim values in
+the access token are grouped under this ID. Use the exact same string everywhere.
 
-The example has the same credential in both formats, the one that is `dc+sd-jwt` and the one
-that is `vc+sd-jwt`. You do not need both, this step keeps them both around to show where the
-formats differ. The later steps add `dc+sd-jwt` credentials only.
+This example defines the same credential in two formats: `dc+sd-jwt` and `vc+sd-jwt`. You do not
+need both formats. Both are included here to show how their configurations differ. Later steps use
+only `dc+sd-jwt` credentials.
 
 ##### The W3C credential
 
@@ -413,9 +416,9 @@ formats differ. The later steps add `dc+sd-jwt` credentials only.
 > to [Settings of your own, per credential](#settings-of-your-own-per-credential)**. SD-JWT VC is
 > much simpler to set up, and it is what all the following steps use.
 
-This part is for `vc+sd-jwt` only, a [W3C VCDM 2.0](https://www.w3.org/TR/vc-data-model-2.0/)
-credential. If you issue `dc+sd-jwt` credentials only, skip it: none of the files or members
-below are needed.
+This section applies only to `vc+sd-jwt`, a [W3C VCDM 2.0](https://www.w3.org/TR/vc-data-model-2.0/)
+credential. If you issue only `dc+sd-jwt` credentials, skip this section. You do not need any of
+the files or members described below.
 
 The [`@context`](https://www.w3.org/TR/vc-data-model-2.0/#contexts) of a `vc+sd-jwt` credential
 has two entries and both have to resolve. The first is the
@@ -505,8 +508,8 @@ Full file: [webapps/root/credentials/GeantIncubatorDiploma_W3C.schema.json](docs
 
 #### Settings of your own, per credential
 
-Member `_issuance_configuration` is not published. It states how long a credential is valid,
-which issuer identifier it carries and whether it goes on a Status List.
+The `_issuance_configuration` member is not published. It controls the credential's validity
+period, issuer ID, and inclusion in a Status List.
 
 It goes inside the credential's own configuration, next to `format` and `vct`:
 
@@ -541,8 +544,8 @@ All of them are optional and so is the member itself.
 
 ##### The validity window
 
-The validity window is from `not_before` to `not_before` plus `lifetime`. With no `not_before`
-the window opens at issuance.
+The validity window starts at `not_before` and ends at `not_before` plus `lifetime`. If `not_before`
+is not set, the window starts when the credential is issued.
 
 Write `not_before` as one of these:
 
@@ -561,17 +564,16 @@ What ends up in the credential:
 | `vc+sd-jwt` | The same three, and [`validFrom` and `validUntil`](https://www.w3.org/TR/vc-data-model-2.0/#validity-period) as the same two ends of the window |
 | `mso_mdoc` | `validityInfo` of the mobile security object, `signed` is the instant of issuance, `validFrom` and `validUntil` the two ends of the window |
 
-A credential whose window opens at issuance carries no `nbf`. The instant of issuance is rounded
-down to the full hour. To round to something else you need to set
+If the validity window starts at issuance, the credential has no `nbf` claim. By default, the
+issuance timestamp is rounded down to the start of the hour. To use a different precision, set
 `openidvci.credentialTimestampPrecision` in *conf/openid-vci.properties*:
 
 ```properties
 openidvci.credentialTimestampPrecision = PT15M
 ```
 
-Keep the precision shorter than the
-shortest `lifetime` you issue. A longer one closes the window before the credential is issued and
-nothing is issued.
+Set this precision to a value shorter than the shortest `lifetime` you issue. Otherwise, the
+validity window may end before the credential is issued, and issuance will fail.
 
 ##### Whether a credential goes on a Status List
 
@@ -586,16 +588,16 @@ credential with `"status_list": true` goes on the list.
 
 `lifetime` here overrides `credentialLifetime` of the profile configuration, including a
 `credentialLifetime` you set on one wallet in *conf/relying-party.xml*. Leave `lifetime` out of
-a credential whose validity you want to keep steering per wallet.
+a credential whose validity you want to keep steering for each wallet.
 
 #### Metadata endpoints
 
 **File(s):** *conf/relying-party.xml*, *etc/jetty-rewrite-rules.xml*, *static/oauth-authorization-server.json*
 **Format:** Native Spring, Jetty XML, JSON
 
-A wallet reads the Credential Issuer metadata and the authorization server metadata, and asks
-for a nonce. A verifier reads the Status List. None of them is a registered client, so these
-endpoints go to the anonymous relying party. Both flows below need it:
+A wallet reads the Credential Issuer and authorization server metadata and requests a nonce.
+A verifier reads the Status List. These callers are not registered clients, so the endpoints must
+be available through the anonymous relying party. Both issuance flows need this configuration:
 
 <details>
 <summary>conf/relying-party.xml</summary>
@@ -653,10 +655,11 @@ 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`,
-`$baseUrl` and `$host`, and these are filled in from `idp.oidc.issuer` of *conf/oidc.properties*.
-One thing you need to add is the credential scopes in `scopes_supported` of
-*static/oauth-authorization-server.json*, a wallet of the code flow asks a credential by scope:
+You do not need to enter your host name in the templates under *static/*. The variables `$issuer`,
+`$baseUrl`, and `$host` are populated from `idp.oidc.issuer` in *conf/oidc.properties*.
+However, you must add the credential scopes to `scopes_supported` in
+*static/oauth-authorization-server.json*. In the authorization code flow, the wallet requests a
+credential by its scope:
 
 <details>
 <summary>static/oauth-authorization-server.json</summary>
@@ -706,8 +709,8 @@ Full file: [static/oauth-authorization-server.json](docs/example/step-1/static/o
 
 </details>
 
-The `/.well-known/` locations are not IdP paths, you wire them in Jetty, in
-*etc/jetty-rewrite-rules.xml*. The later steps add one rule each.
+The `/.well-known/` URLs are not IdP paths. Map them to the IdP endpoints in Jetty by editing
+*etc/jetty-rewrite-rules.xml*. Later steps add one rewrite rule each.
 
 <details>
 <summary>etc/jetty-rewrite-rules.xml</summary>
@@ -774,8 +777,8 @@ Full file: [etc/jetty-rewrite-rules.xml](docs/example/step-1/etc/jetty-rewrite-r
 **File(s):** *conf/authn/oauth2client-authn-config.xml*
 **Format:** Native Spring
 
-The wallets of both flows below are public clients, they authenticate at the token endpoint with
-`none`. You add the validator of public clients to the client validators:
+The wallets used in both flows are public clients. They use `none` as the authentication method
+at the token endpoint. Add the public-client validator to the list of client validators:
 
 <details>
 <summary>conf/authn/oauth2client-authn-config.xml</summary>
@@ -797,12 +800,13 @@ Full file: [conf/authn/oauth2client-authn-config.xml](docs/example/step-1/conf/a
 **File(s):** *metadata/oidc-client.json*, *conf/relying-party.xml*
 **Format:** JSON, Native Spring
 
-In this flow something in your deployment, a portal page, a registration system or a script,
-authenticates the user and calls the credential-offer endpoint for the user. The offer is then
-shown to the wallet, usually as a QR code. The credential content is decided at that moment.
+In this flow, a component in your deployment—such as a portal, registration system, or script—
+authenticates the user and calls the credential-offer endpoint on their behalf. The resulting offer
+is shown to the wallet, usually as a QR code. The credential's contents are specified when the
+offer is created.
 
-The caller is a confidential client. The wallet that redeems the code is a *second* and
-different identity. You register both:
+The component that creates the offer is a confidential client. The wallet that redeems the code
+uses a separate client IDentity. Register both clients:
 
 <details>
 <summary>metadata/oidc-client.json</summary>
@@ -827,9 +831,9 @@ Full file: [metadata/oidc-client.json](docs/example/step-1/metadata/oidc-client.
 
 </details>
 
-The `/wallet` suffix is fixed. A wallet that redeems a pre-authorized
-code does not authenticate, so the plugin resolves its client id by taking the client id of the
-creator of the offer and appending `/wallet` to it.
+The `/wallet` suffix is fixed. A wallet redeeming a pre-authorized code does not authenticate.
+The plugin finds its client ID by appending `/wallet` to the client ID of the client that created
+the offer.
 
 Then one relying party for the party that creates the content for credential and one for the
 wallet that redeems the code:
@@ -914,9 +918,9 @@ 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
-`/idp/profile/openid/vci/credential-offer` authenticated as `offer-caller`. Claim values are
-given literally and keyed with the credential configuration id:
+Send the offer request as a `POST` with `Content-Type: application/json` to
+`/idp/profile/openid/vci/credential-offer`, authenticated as `offer-caller`. Provide the claim
+values directly and group them by credential configuration ID:
 
 <details>
 <summary>Example of asking for a credential offer</summary>
@@ -953,9 +957,9 @@ curl -u 'offer-caller:CHANGE-ME' \
 }'
 ```
 
-The response is the Credential Offer as a deep link, in one line and with no whitespace. The
-offer JSON travels as a query parameter, so it is percent-encoded. This is what you put in
-the QR code. The `value` of the transaction code is not in it:
+The response contains the Credential Offer as a single-line deep link with no whitespace. The
+offer JSON is passed as a query parameter and is therefore percent-encoded. Put this link in the
+QR code. The transaction code's `value` is not included in the link:
 
 ```
 openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%22https://issuer.example.org%22,%22credential_configuration_ids%22:%5B%22GeantIncubatorDiploma_SDJWT%22,%22GeantIncubatorDiploma_W3C%22%5D,%22grants%22:%7B%22urn:ietf:params:oauth:grant-type:pre-authorized_code%22:%7B%22pre-authorized_code%22:%22AAdzZWNyZXQxWKPQ...%22,%22tx_code%22:%7B%22input_mode%22:%22numeric%22,%22length%22:6,%22description%22:%22The%20code%20we%20sent%20to%20your%20phone%22%7D%7D%7D%7D
@@ -963,29 +967,29 @@ openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%22https:
 
 </details>
 
-The offer in example asks for both credentials at once. Keep to one credential per offer, many
-wallets do not handle two.
+This example requests both credentials in one offer. In practice, include only one credential
+per offer because many wallets cannot handle multiple credentials in a single offer.
 
 Note that the paths are flat in both, also in the `vc+sd-jwt` one. The `credentialSubject`
 nesting of that format is done when the credential is built.
 
-Every configuration id of the body must exist in *metadata/verifiable-credentials.json* or the
-offer is rejected. There is no attribute resolution when the wallet later redeems the code. The
-values above are what is issued.
+Every configuration ID in the request must exist in *metadata/verifiable-credentials.json*;
+otherwise, the plugin rejects the offer. The plugin does not resolve attributes when the wallet
+redeems the code. It issues the values provided in the offer.
 
 #### Authorization code flow
 
 **File(s):** *metadata/oidc-client.json*, *conf/relying-party.xml*, *conf/attribute-resolver.xml*, *conf/attribute-filter.xml*, *conf/oidc.properties*
 **Format:** JSON, Native Spring, Property files
 
-This flow starts with a Credential Offer as well, but of a different kind. It has no claim
-values in it, only the ids of the credentials the wallet may ask for. After it the wallet makes
-a normal authorization request, the user authenticates at the IdP as usual and the credential
-content comes from an IdP attribute that is resolved for the user that just logged in.
+This flow also starts with a Credential Offer, but the offer contains no claim values. It lists
+only the IDs of the credentials the wallet may request. The wallet then sends a normal
+authorization request, and the user authenticates at the IdP as usual. The credential contents
+come from IdP attributes resolved for that user.
 
-Nothing in the offer is per user here, so you write it by hand. The credential offer endpoint
-does not produce this one. The same url serves everybody and you may print it once as a QR
-code:
+Because the offer contains no user-specific data, you create it manually. The credential-offer
+endpoint does not generate this type of offer. The same URL works for every user, so you can print
+it once as a QR code:
 
 ```
 openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%22https://issuer.example.org%22,%22credential_configuration_ids%22:%5B%22GeantIncubatorDiploma_SDJWT%22%5D,%22grants%22:%7B%22authorization_code%22:%7B%7D%7D%7D
@@ -995,9 +999,9 @@ The `authorization_code` grant is what tells the wallet to run this flow. It is
 `issuer_state` is not supported. The `credential_issuer` value has to be the Credential Issuer
 Identifier this deployment publishes, the same string as in *openid-credential-issuer.json*.
 
-The wallet is a normal OIDC client of one identity, there is no second one as in the
-pre-authorized code flow. You register it with the client metadata resolver of the OP plugin,
-in the same *metadata/oidc-client.json* as the clients above:
+The wallet is a normal OIDC client and uses a single client IDentity. Unlike the pre-authorized
+code flow, it does not need a second identity. Register it with the OP plugin's client metadata
+resolver in the same *metadata/oidc-client.json* file as the clients above:
 
 <details>
 <summary>metadata/oidc-client.json</summary>
@@ -1038,15 +1042,14 @@ Full file: [metadata/oidc-client.json](docs/example/step-1/metadata/oidc-client.
 
 </details>
 
-Note the `scope` value. Every configuration id you want to be requestable by scope needs to be
-here. A wallet needs the pre-authorized code grant in `grant_types` only if it sends its own
-`client_id` to the token endpoint in the pre-authorized code flow. The example has it to show
-that case. `audience` needs `credentials` for wallets that request
-without `openid` in `scope`, the authorization endpoint rejects those when the client has no
-audience.
+Check the `scope` value: it must include every configuration ID that wallets should be able to
+request by scope. A wallet needs the pre-authorized code grant in `grant_types` only if it sends
+its own `client_id` to the token endpoint during that flow. The example includes it to demonstrate
+this case. The `audience` must include `credentials` for wallets that omit `openid` from `scope`.
+Otherwise, the authorization endpoint rejects the request if the client has no audience configured.
 
-The relying party override needs four profiles, authorize, token, credential and pushed
-authorization request:
+The relying-party override needs four profile configurations: authorization, token, credential,
+and pushed authorization request (PAR):
 
 <details>
 <summary>conf/relying-party.xml</summary>
@@ -1148,14 +1151,14 @@ Full file: [conf/relying-party.xml](docs/example/step-1/conf/relying-party.xml)
 
 </details>
 
-Wallets usually leave `openid` out of `scope`, so their authorization request is a plain OAuth 2.0
-request. `requireAuthenticationRequest="false"` on `OIDC.SSO` and `OAUTH2.PAR` lets the wallet
-send one. Without it the PAR fails and the log says *The request did not match with the
-configured authorization request type requirements*.
+Wallets usually omit `openid` from `scope`, so they send a standard OAuth 2.0 authorization
+request. Setting `requireAuthenticationRequest="false"` on `OIDC.SSO` and `OAUTH2.PAR` allows
+this request type. Without these settings, PAR fails with the log message
+*The request did not match with the configured authorization request type requirements*.
 
-The *value* of the attribute is the claims as an array of `{"path": [...], "value": ...}`, the
-same array the Credential Offer API takes. You define it as a `ScriptedAttribute` so that it may
-read the real attributes of the user:
+The attribute value is an array of claims, each represented as `{"path": [...], "value": ...}`.
+This is the same structure used by the Credential Offer API. Define the attribute as a
+`ScriptedAttribute` so it can read the user's actual attributes:
 
 <details>
 <summary>conf/attribute-resolver.xml</summary>
@@ -1186,8 +1189,8 @@ read the real attributes of the user:
 </details>
 
 One attribute serves both credentials. Every `AttributeEncoder` of the definition becomes a
-claim of its own name, so the same claim list is found with either configuration id and the
-script is written once. The encoder names are credential configuration ids.
+claim of its own name, so the same claim list is found with either configuration ID and the
+script is written once. The encoder names are credential configuration IDs.
 `idp.oidc.encodedAttributes` and the filter policy use the attribute id.
 
 Then you tell the OP plugin to embed the attribute into the access token in
@@ -1265,7 +1268,7 @@ in follows from the month the credential **expires**, not from the month it was
 default `credentialLifetime` of `P180D` a credential that is issued in September 2026 belongs to
 the list of March 2027.
 
-The part after `~` tells under which issuer identifier the credential was issued. A Status List
+The part after `~` tells under which issuer ID the credential was issued. A Status List
 Token has to have the same issuer as the credential. For `did:jwk` the part is the thumbprint
 of the signing key, as the identifier is the key. For `did:web` it is `did-web` and for the plain
 url `url`. It is still one list per month, whatever the identifier.
@@ -1373,7 +1376,7 @@ Cache-Control: no-store
 </details>
 
 The `id` is the month. You may give it also with the `~` part copied from the credential, a slot
-belongs to the month whatever the issuer identifier of the credential.
+belongs to the month whatever the issuer ID of the credential.
 
 Revoking is idempotent and it goes one way only. Nothing in the plugin turns a slot back to
 valid. The revoke endpoint does **not** check that the caller issued the credential that holds
@@ -1934,7 +1937,7 @@ curl -u 'offer-caller:CHANGE-ME' \
 }'
 ```
 
-### Step 3: Add a plain url credential
+### Step 3: Add a plain URL-based credential
 
 Third credential has the value of `idp.oidc.issuer` as its issuer, `https://issuer.example.org`. A
 verifier finds the key from JWT VC issuer metadata, `/.well-known/jwt-vc-issuer`, which points to
@@ -1948,8 +1951,8 @@ What is new:
 - the Status List url of the credential ends with `~url`
 - the TOPPAN wallet is a second client, with a relying party and an attribute release of its own
 
-Paradym does not take a credential whose issuer is a plain url, it accepts did and x5c issuers
-only. The url credential is fetched with TOPPAN instead. TOPPAN sends the client id
+Paradym does not take a credential whose issuer is a plain URL, it accepts did and x5c issuers
+only. The URL-based credential is fetched with TOPPAN instead. TOPPAN sends the client ID
 `wallet-dev` and the redirect uri `walletapp-goid-openid4vci-callback://authorization`, and
 these are what the client entry has. TOPPAN binds the PAR to its DPoP key already, so the code
 and the access token are bound to that key.
@@ -2471,10 +2474,10 @@ the keys of the OP, which sign the credentials.
 
 Attribute encoder and offer as in Step 2, with `GeantIncubatorDiploma_URL`.
 
-### Step 4: Make the url credential an OpenID Federation credential
+### Step 4: Make the URL-based credential an OpenID Federation credential
 
 No new credential in this step. The deployment is stated as a Credential Issuer in OpenID
-Federation and the url credential of Step 3 carries its Entity Identifier. The federation itself, trust anchors,
+Federation and the URL-based credential of Step 3 carries its Entity Identifier. The federation itself, trust anchors,
 authority hints and so on, is configured in the plugins of [Dependencies](#dependencies), see
 [OIDFEDCommon](https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/5601296385/OIDFEDCommon).
 
@@ -2482,9 +2485,9 @@ What is new:
 
 - the Entity Configuration at `/.well-known/openid-federation` has `openid_credential_issuer`
   metadata and the credential signing keys
-- the url credential carries `"fed": "https://issuer.example.org"`, the other credentials not
+- the URL-based credential carries `"fed": "https://issuer.example.org"`, the other credentials not
 
-The url credential is still fetched with TOPPAN, the client of Step 3.
+The URL-based credential is still fetched with TOPPAN, the client of Step 3.
 
 The Entity Configuration publishes the keys of **openidvci.SigningCredentials** of Step 2. If
 the federation file is imported and the list is not defined, the IdP does not start.
@@ -2508,7 +2511,7 @@ Import the federation file into *conf/global.xml*, after the import of Step 2:
 ```
 
 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-based credential in the federation, so the three others get
 `"oidfed": false`:
 
 <details>
@@ -2903,7 +2906,7 @@ Full file: [etc/jetty-rewrite-rules.xml](docs/example/step-4/etc/jetty-rewrite-r
 
 </details>
 
-The url credential then carries:
+The URL-based credential then carries:
 
 ```json
 {
@@ -2967,7 +2970,7 @@ SD-JWT VC or as ISO mdoc, see
 [HAIP 1.0](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-final.html#vc-profiles).
 This step has one of each.
 
-It is not built on Steps 2 to 4. Start from Step 1 and keep its general parts: the
+It is not implemented on Steps 2 to 4. Start from Step 1 and keep its general parts: the
 installation, [Metadata endpoints](#metadata-endpoints),
 [Wallet authentication](#wallet-authentication) with the client `offer-caller`, the attribute
 definition and the [Status List](#status-list). Leave out its credentials, the Paradym client
@@ -2982,7 +2985,7 @@ credential, and wallets act on what is published, not on what is registered for
 - The authorization server metadata lists `attest_jwt_client_auth`. Some wallets switch to it as
   soon as it is listed, also for the DIIP credentials. Such a wallet works only if it is
   registered with `attest_jwt_client_auth` and the CA of its wallet provider is trusted.
-- The signing key with a certificate goes first in the list. A url credential is then signed with
+- The signing key with a certificate goes first in the list. A URL-based credential is then signed with
   it and carries `x5c` instead of `kid`, and its verifiers have to move to the chain.
 
 So run one setup or the other.
@@ -2996,7 +2999,7 @@ What you configure:
 
 - a signing key with a certificate
 - a Status List signing key with a certificate of its own
-- a url credential
+- a URL-based credential
 - an mdoc credential
 - key attestation required for the credentials, and the attester CA you trust
 - the Hopae EUDI Wallet as a client with `attest_jwt_client_auth`
@@ -3081,7 +3084,7 @@ https url, there is no switch for it.
 
 #### The SD-JWT VC credential
 
-`GeantIncubatorDiploma_HAIP` in *metadata/verifiable-credentials.json*, a url credential with key
+`GeantIncubatorDiploma_HAIP` in *metadata/verifiable-credentials.json*, a URL-based credential with key
 attestation required:
 
 <details>
@@ -3428,7 +3431,7 @@ Full file: [conf/attribute-filter.xml](docs/example/step-5/conf/attribute-filter
 
 </details>
 
-The Status List needs nothing more for the mdoc, it is also served as CWT, see
+The Status List needs no further configuration for the mdoc, it is also served as CWT, see
 [OAuth.StatusList](#oauth-statuslist).
 
 #### Attestations
@@ -3517,7 +3520,7 @@ idp.oauth2.authn.flows = OpenIDVCIClient
 
 #### The HAIP wallet as a client
 
-The client id is the `sub` of the Wallet Attestation of the wallet. The EUDI wallet provider gives
+The client ID is the `sub` of the Wallet Attestation of the wallet. The EUDI wallet provider gives
 the Hopae EUDI Wallet `eudiw-abca`. In *metadata/oidc-client.json*:
 
 <details>
@@ -3750,7 +3753,7 @@ Full file: [static/oauth-authorization-server.json](docs/example/step-5/static/o
 ### Step 6: Rolling the signing key
 
 This applies to both setups, the DIIP setup of Steps 1 to 4 and the HAIP setup of Step 5. What a
-key change means depends on the issuer identifier of the credential. The configuration is the
+key change means depends on the issuer ID of the credential. The configuration is the
 same for all:
 
 1. The new key goes **first** in **openidvci.SigningCredentials**. The first key that fits the
@@ -3769,7 +3772,7 @@ of the credentials it signed.
 | `did:jwk` | The identifier is the key, so it changes. New credentials have a new issuer. Old credentials verify on their own, the key is inside, but a verifier that trusts the issuer by its identifier has to be told the new one. The Status List url of an old credential has the thumbprint of the old key, and that list is signed with the old key. If the old key is not in the list anymore, the Status List Token of the old credentials is not served and they cannot be checked. |
 | `did:web` | The identifier stays. The did:web document lists every key of the list, each by its name, so the new key needs a name of its own. Old credentials have the `kid` of the old key and it stays in the document as long as it is in the list. |
 | url, no chain | The identifier stays. The key is found by `kid` from the keyset of the OP, so the new key goes to **shibboleth.oidc.SigningCredentialsToPublish** before it signs, and the old one stays there until its credentials have expired. |
-| url, with chain | The identifier stays. The credential carries its chain, a verifier trusts the root. A new leaf under the same CA needs nothing published. The Status List of url credentials is not tied to a key, it is signed with the first key of **openidvci.status-list.SigningCredentials**. |
+| url, with chain | The identifier stays. The credential carries its chain, a verifier trusts the root. A new leaf under the same CA needs nothing published. The Status List of URL-based credentials is not tied to a key, it is signed with the first key of **openidvci.status-list.SigningCredentials**. |
 | OpenID Federation | The Entity Configuration publishes the keys of the list. Same as did:web. |
 | mdoc | Same as url with chain. |
 
@@ -3823,7 +3826,7 @@ What you control:
   `credential_configuration_id`. To accept that, set
   `allowCredentialConfigurationIdWithIdentifiers` on the credentials profile of those wallets.
   This does not help when the user has several credentials of one configuration. Then the
-  configuration id does not tell which credential the wallet wants.
+  configuration ID does not tell which credential the wallet wants.
 
 ```xml
 <bean parent="RelyingPartyByName" c:relyingPartyIds="offer-caller/wallet">
@@ -3900,7 +3903,7 @@ authorization code flow.
 
 The token response has one identifier per dataset in `credential_identifiers`, see
 [Successful Token Response](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-successful-token-response).
-The identifier is the configuration id and the `dataset_id` joined with `#`:
+The identifier is the configuration ID and the `dataset_id` joined with `#`:
 
 ```json
 "authorization_details": [{
@@ -3917,7 +3920,7 @@ response has the identifiers also when the wallet did not ask with `authorizatio
 
 ### Pre-authorized code flow
 
-The value of a credential configuration id in the offer request is a list of datasets instead of
+The value of a credential configuration ID in the offer request is a list of datasets instead of
 the claims:
 
 ```json
@@ -4385,7 +4388,7 @@ the wallets in *conf/attribute-filter.xml*, as in Step 1:
 idp.oidc.encodedAttributes = ComplexCredential, ComplexCredentialMdoc
 ```
 
-The configuration ids go to the `scope` of the wallets in *metadata/oidc-client.json*.
+The configuration IDs go to the `scope` of the wallets in *metadata/oidc-client.json*.
 
 #### Pre-authorized offer
 
@@ -4445,8 +4448,8 @@ The places to configure, in order:
 **File(s):** *conf/relying-party.xml*
 **Format:** Native Spring
 
-An endpoint is enabled by adding its profile configuration bean to a relying party. Nothing is
-enabled by default.
+Enable an endpoint by adding its profile configuration bean to a relying party. No endpoints
+are enabled by default.
 
 #### OpenID.VCI.IssuerMetadata
 
@@ -4488,7 +4491,7 @@ Request, a `POST` of `application/json`:
 
 | Name | Type | Description |
 |---|---|---|
-| `preAuthorizedCredentials` | Object | Required. Keyed with credential configuration id, each an array of `{"path": [...], "value": ...}`, or a list of datasets `{"dataset_id": ..., "claims": [...]}`. See [Several credentials of one configuration](#several-credentials-of-one-configuration). |
+| `preAuthorizedCredentials` | Object | Required. Keyed with credential configuration ID, each an array of `{"path": [...], "value": ...}`, or a list of datasets `{"dataset_id": ..., "claims": [...]}`. See [Several credentials of one configuration](#several-credentials-of-one-configuration). |
 | `txCode` | Object | `value`, and hints `input_mode` (`numeric` or `text`), `length` and `description`. `value` is not in the offer. |
 | `client_id` | String | Only where unauthenticated callers are accepted. |
 
@@ -4626,7 +4629,7 @@ Members of a `vc+sd-jwt` credential, [W3C VCDM 2.0](https://www.w3.org/TR/vc-dat
 | [`relatedResource`](https://www.w3.org/TR/vc-data-model-2.0/#integrity-of-related-resources) | You | Optional | Integrity information of resources the credential refers to. |
 | [`evidence`](https://www.w3.org/TR/vc-data-model-2.0/#evidence) | You | Optional | Evidence of the claims. The same for every credential of the configuration. |
 | [`id`](https://www.w3.org/TR/vc-data-model-2.0/#identifiers) | The plugin | Not read | `urn:uuid:` and a random UUID, per credential. |
-| [`issuer`](https://www.w3.org/TR/vc-data-model-2.0/#issuer) | The plugin | Not read | The issuer identifier, see [Credential Issuer identifiers](#credential-issuer-identifiers). |
+| [`issuer`](https://www.w3.org/TR/vc-data-model-2.0/#issuer) | The plugin | Not read | The issuer ID, see [Credential Issuer identifiers](#credential-issuer-identifiers). |
 | [`validFrom`, `validUntil`](https://www.w3.org/TR/vc-data-model-2.0/#validity-period) | The plugin | Not read | The validity window, see `lifetime` and `not_before` above. |
 | [`credentialSubject`](https://www.w3.org/TR/vc-data-model-2.0/#credential-subject) | The plugin | Not read | The claims, from the IdP attribute. A wallet discloses each value you give on its own, see [Complex claims](#complex-claims). |
 
@@ -4645,8 +4648,8 @@ The Status List Token of a credential has the same issuer as the credential.
 **File(s):** *conf/openid-vci-credentials.xml*
 **Format:** Native Spring
 
-The file is installed but not imported. Import it into *conf/credentials.xml*, or into
-*conf/global.xml* when *conf/openid-vci-oidfed.xml* is imported.
+The plugin installs this file but does not import it automatically. Import it in
+*conf/credentials.xml*, or in *conf/global.xml* if you also import *conf/openid-vci-oidfed.xml*.
 
 | List | Signs | When undefined |
 |---|---|---|
@@ -4923,7 +4926,7 @@ overrides `batchSize` of the profile.
 }
 ```
 
-Add the credential configuration ids to `scopes_supported`. For Wallet Attestation add
+Add the credential configuration IDs to `scopes_supported`. For Wallet Attestation add
 `attest_jwt_client_auth` to `token_endpoint_auth_methods_supported`, and
 `client_attestation_signing_alg_values_supported`,
 `client_attestation_pop_signing_alg_values_supported` and

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list