Issuance exercises
OID4VCI Issuance exercises and explanation
Prerequisites
In the below exercises we will be creating credential offers in different ways. Please read the credential offers chapter first that explains the concept of credential offers.
Credential offers and session initiation
In the below exercises you will learn how to issue credentials by value
and by reference
. You will actually get them in a mobile wallet!
Exercise 1: Credential offer by value, using pre-authorized_code
Please use the create-credential-offer
API (docs) or
API (swaggerhub) to create a simple so-called credential offer by value using a pre-authorized_code
grant.
This means using an offerMode
of "VALUE"
and a grants
object containing an object of urn:ietf:params:oauth:grant-type:pre-authorized_code
with a pre-authorized_code
value of your choosing. Use a credential_configuration_id
with value "PensionSdJwt"
.
pre-authorized_code
value of your choosing. Make sure to keep this value at hand because you will
need it later. You can re-use
this value during the create-offer phase to overwrite any existing sessionpre-authorized_code
. Since we allow overwriting of sessions with this value, do
not use the default values, as
others might be creating new sessions whilst you are performing the exercise!The expected outcome should be an HTTP response with something like:
The above is the actual URI value that can be used in a link or QR code (more on that later). A wallet is using that value to start the issuance process with the Issuer. This is what is called an issuer initiated flow, instead of a wallet initiated flow. Reason is that the issuer had to create the offer first. For more info see the credential offers chapter.
Exercise 2: Credential offer by value, using pre-authorized_code and a different base URI
In a so-called same device flow the wallet lives on the same device (phone, desktop) as where the credential-offer is displayed. This would happen in a browser on the device. In that case putting the above credential URI value in a link is enough to trigger the wallet when clicking on it. This is because the deeplink openid-credential-offer://
typically is registered by a wallet that can handle OID4VCI in your mobile phone. On a web-wallet it doesn’t work like that. Hence why the API also supports creating a credential offer using a different base URI (or scheme).
Let’s pretend we have web-wallet at http://localhost/my-wallet. We are not going to use a real web wallet in this exercise!
Now create the above credential-offer with the web-wallet base URI using the baseUri
parameter
Please use the create-credential-offer
API (docs) or
API (swaggerhub) to create a simple so-called credential offer by value using a pre-authorized_code
grant.
This means using an offerMode
of "VALUE"
and a grants
object containing an object of urn:ietf:params:oauth:grant-type:pre-authorized_code
with a pre-authorized_code
value used before (we will simply overwrite the session). Use a credential_configuration_id
with value "PensionSdJwt"
and use a baseUri
with your web-wallet address.
Note that the baseUri
is a top-level property in the payload!
The expected outcome should be an HTTP response with something like:
If you had a real web based wallet that can interpret the above link, you would have to authenticate first (if not already authenticated), afterwards the wallet would guide you through the issuance process.
openid-credential-offer://?credential-offer=
schema into a specific form in the wallet. This relies on an issuer allowing you to copy the
URL.Exercise 3: Create a QR code from the URI and scan it with a mobile wallet
In a lot of situations your wallet is on a mobile device, while you might be visiting the issuer website using another device like a tablet or desktop computer. This is the so called cross-device flow. AAlthough new mechanisms are being developer whereby your mobile wallet can register with your browser, right now a credential offer is being conveyed using a QR code.
This is a short exercise to show that all you really need to do is display the credential offer value in a QR code to really start the interaction with the issuer from a mobile device. Meaning that if you are not interested in tracking the progress of the issuance process, you only need the create-credential-offer
endpoint.
Take the outcome of Exercise 1 and copy the value of the uri
field from the JSON response. So do not copy the whole JSON response. You should only copy the value that starts with openid-credential-offer://.....
also minus the quotes in front and at the end.
Go to an online QR code generator like: https://www.qr-code-generator.com/ and past the credential offer value. You should get a QR code back. Below is an example QR code. Obviously yours will differ.
Now take you mobile wallet application and make sure you are logged in. Typically the wallet app will have a QR code scanning functionality. You can use that to initiate the issuance process. Most wallets will also register with the Operating System, meaning you can also scan the QR code using your regular photo/camera app instead. Then depending on your brand the wallet will open, or you will get a choice which wallet app to open. Finish the issuance process on your mobile wallet, to see that all it took was creating a credential-offer and displaying that as a QR code.
Exercise 4: Let the API create a QR code and scan it with a mobile wallet
The issuer has built-in support to generate QR codes and return them in the API. The QR images are returned as so-called data URIs. This means they are base64 encoded. Your browser can actually display these. Simply open a seperate tab and paste the data URI value from the response minus the quotes into your address bar at the top.
Please use the create-credential-offer
API (docs) or
API (swaggerhub) to create a simple so-called credential offer by value using a pre-authorized_code
grant and this time with qrCodeOpts
.
This means using an offerMode
of "VALUE"
and a grants
object containing an object of urn:ietf:params:oauth:grant-type:pre-authorized_code
with a pre-authorized_code
value of your choosing. Use a credential_configuration_id
with value "PensionSdJwt"
and add a "qrCodeOpts"
object with value {"size": 400}
.
Now you should get a response that looks like below:
Copy the entire value between quotes (excluding the quotes) of the qrCodeDataUri
and paste that into your address bar in a new browser tab. You now should see a QR code.
You can now scan the QR code with your wallet or mobile device and you should get an additional credential issued to your wallet.
Exercise 5: Protect the QR code and interaction with a transaction code
As explained in the issuer introduction documentation, a QR code isn’t the most safe method of interacting, especially not with a pre-authorized code like we are using right now. The issuer API has support for transaction/pin codes. This mitigates someone taking a picture over your sholder, and impersonating you. Remember with a pre-authorized code flow, you have already authenticated on a website for instance. Before you started scanning the QR code. By using a transaction/pin code you have an additional value that needs to be entered in the wallet. The idea is that the pincode is send out of bound, so that an attacker that can see the QR code doesn’t see it. So a text-message/SMS or e-mail for instance. Using hidden toggle on the website also works, but obviously is less secure.
Please use the create-credential-offer
API (docs) or
API (swaggerhub) to create a simple so-called credential offer by value using a pre-authorized_code
grant and this time with qrCodeOpts
and with “.
This means using an offerMode
of "VALUE"
and a grants
object containing an object of urn:ietf:params:oauth:grant-type:pre-authorized_code
with a pre-authorized_code
value of your choosing. Use a credential_configuration_id
with value "PensionSdJwt"
and add a "qrCodeOpts"
object with value {"size": 400}
and a new transaction code object called tx_code
and value
The tx_code
needs to be part of the urn:ietf:params:oauth:grant-type:pre-authorized_code
object.
Now you should get a response that looks like below:
Note the addition of the txCode
in the response, that confirms a numeric
transaction code of length 4 is being used. Also note that the API returns the random pin of that length.
Scan the QR code with the wallet. During the issuance process you should now be asked to enter the pin code in the wallet.
text
input mode and will still use numericExercise 6: Create an offer by reference
Up until now we have been creating offers by value. It also has the option to create them by reference. Please read the
credential offers chapter again if you do not know the difference anymore.
In the previous exercise we created a credential that was pretty large. If you need to incorporate that into a website this might not be the most appealing. You will notice that if you create the same QR code with for instance a size of 150
instead of 400
it becomes nicer to incorporate, but at the same time a lot of devices now might have problems in actually properly scanning the QR code. The reason is that there is quite a bit of information in the QR code. Let’s fix that by using a Credential Offer by reference instead of value. This means the Issuer will provide a URL called the credential_offer_uri
in the credential-offer object. That URI will host the actual offer and the wallet will resolve that URI to fetch the offer using a GET
request.
correlationId
as well. This
correlation Id is only used in this
URL. In subsequent steps the wallet will not use or see this value. It will rely on other state and nonce values. You backend can use the
correlationId
to track progress for
instance, without ever knowing the state values.Please use the create-credential-offer
API (docs) or
API (swaggerhub) to create a simple so-called credential offer by reference using a pre-authorized_code
grant and this time with qrCodeOpts
.
This means using an offerMode
of "REFERENCE"
and a grants
object containing an object of urn:ietf:params:oauth:grant-type:pre-authorized_code
with a pre-authorized_code
value of your choosing. Use a credential_configuration_id
with value "PensionSdJwt"
and add a "qrCodeOpts"
object with value {"size": 400}
.
Now you should get a response that looks like below:
Notice the credential_offer_uri
in the response instead of credential_offer
. Also notice that the string length is shorter than before. If you look at the QR code you should see there is less data in it. This allows you to use it in a smaller sized QR code, without having issues with users not being able to scan them!
Credential offer session status
Up until now we only have been using one endpoint to start the issuance process from the API and then we received the credentials in our wallet. Typically you will have a portal or web-app that is guiding the user. This is the case both in the same-device as cross-device flow. When you create web frontend for the issuance process, you will want your frontend to react to states changes in the issuance process, to inform the user about errors, success etc.
The create-credential-offer
endpoint we used below internally creates a session. The session contains all kind of information needed by the issuer; one of the important properties it has in the session is the state
. The state can have the following values:
state | Description |
---|---|
OFFER_CREATED | An offer is created. This is the initial state |
ACCESS_TOKEN_REQUESTED | Optional state, given the token endpoint could also be on a separate AS |
ACCESS_TOKEN_CREATED | Optional state, given the token endpoint could also be on a separate AS |
CREDENTIAL_REQUEST_RECEIVED | Credential request received. Next state would either be error or issued |
CREDENTIAL_ISSUED | The credential iss issued from the issuer’s perspective |
ERROR | An error occurred |
NOTIFICATION_CREDENTIAL_ACCEPTED | The holder/user stored the credential in the wallet (If notifications are enabled) |
NOTIFICATION_CREDENTIAL_DELETED | The holder/user did not store the credential in the wallet (If notifications are enabled) |
NOTIFICATION_CREDENTIAL_FAILURE | The holder/user encountered an error (If notifications are enabled) |
Exercise 7: Get the initial status
In this exercise we will retrieve the initial status of the credential offer. Without a wallet involved. The state will be OFFER_CREATED
Please use the credential-offer-status
API (docs) or
API (swaggerhub) to check the progress of the session using the pre-authorized_code
value you supplied during credential offer creation. You could also create a new offer as explained in the above exercises. It is wise to create the QR code using the API
You should get a response like this:
The status
is from the table above.
Exercise 8: Check the status whilst accepting the credential in the wallet
In this exercise we will continue where the previous exercise ended. We will use the wallet to scan the QR code. Make sure that you create the QR code from the offer, or use the QR code the API supplied (see earlier exercises in case of doubt).
Scan the QR code with the mobile wallet. Once the wallet is in the accept/decline screen, you should see the state as CREDENTIAL_ISSUED
Please use the credential-offer-status
API (docs) or
API (swaggerhub) to check the progress of the session using the pre-authorized_code
value you supplied during credential offer creation.
You should get a response like this:
The status
is from the table above.
The reason the status is CREDENTIAL_ISSUED is because it actually is issued from the perspective of the Issuer at this point. The wallet does not know (yet) whether the holder will accept or decline the credential.
If you now accept the credential in the wallet the status value will move to NOTIFICATION_CREDENTIAL_ACCEPTED
if you call the API, but only if the notification endpoint is enabled and only if the wallet has both implemented that feature and enabled it.
Advanced Exercise 9: Add the previously designed credential to the issuer
In the credential design we have thought about a new credential. If you are running the agent locally and via ngrok, then you can attempt to perform this exercise.
Open the file packages/agent/conf/exercises/oid4vci_metadata/example-issuer.json
. Go to the credential_configurations_supported
JSON structure. Now copy the entire “PensionSdJwt” object including the key. Add it above or below the PensionSdJwt. Name it after what your credential represents.
Now adjust the values, mainly in the display section. If you want to use images and logo’s, make sure to have uploaded them to a URL somewhere. For instance use imagebb.com
Make sure to also update the vct
value to the name of your credential. This should be the same as the key you used. Then update the claims
object so that it contains the correct values for your credential.
After you are done with that part go to the bottom of the file to the template_mappings
section. Copy
To a new object. Rename PensionSDJwt for both the credential_types
and template_path
into the name of your credential.
Now copy the file packages/agent/conf/exercises/templates/PensionSdJwt.hbs.json
into a file YourCredentialName.hbs.json
that you referenced above
Update the hbs file and make sure you are putting the correct claims in there that you designed before and that match the claims in the metadata above.
Now stop the agent and restart the agent again. This imports the new definition/metadata and enables the new template file.
You should now be able to create a new offer. Make sure to provide the correct configuration_id
in the payload instead of the PensionSdJwt
. Also make sure that the credentialDataSupplier
values are updated to reflect the values you would like to see in the issued credential.
If everything went well, you should get your branded credential in your wallet.