Guides
Credential issuance
OID4VCI

How to issue a credential via the OID4VCI workflow

To issue a credential via the OID4VCI workflow you must create a Credential offer. This offer specifies the Credential configurations that will be used to issue the credential, as well as additional request parameters to support the authentication workflow.

Once the offer is created, it must be shared with the credential's intended holder so that they can claim the credential.

Prerequisites

  • The id identifier of one or more Credential configurations you wish to include in this offer. This is obtained when you create a Credential configuration.
  • DIDs:
    • Issuer DID: This is a did:web that identifies the issuer who attests the claims in the credential are accurate.
    • Subject DID: This is a did:key that identifies the intended holder of the credential. This DID is usually retrieved from the intended holder's digital wallet.
      • Refer to Create a did:key if you need assistance in creating one for testing this feature.
      • In production environments you must have a secure way to obtain the holder's digital wallet DID:
        • Use DID Auth for any new interactions.
        • Ask the user to share their wallet DID.
        • Request an existing credential as part of a verification workflow, and extract the DID from that interaction.

Overview

Issuing a credential via the OID4VCI workflow comprises the following steps:

  1. Generate an offer URI.
  2. Send an offer URI.
  3. Accept a Credential offer.

Generate an offer URI

Request

Make a request of the following structure to generate a new credential offer:

HTTP
POST /v1/openid/offers
Request body
JSON
{
    "credentials": ["20d6bbe6-a978-447c-b5bd-f33b6dca19e2"],
    "request_parameters": {
        "login_hint": "user@example.com",
        "prompt": "login"
    }
}
  • credentials : This array includes a list of identifiers for credential configurations that will be included in the credential offer. These identifiers are the id elements returned in the response when you create a Credential configuration.

    To issue multiple credential profiles of the same credential in a single flow, include all the required credential configuration id elements in the request payload. For example, you could issue a Web, Compact and/or Mobile Credential using the same data in a single user journey.

  • request_parameters (optional): Specifies a list of additional request parameters that are included in the credential offer and can be used by the wallet as part of the authentication workflow:

    • login_hint : Login hints are included in the authentication flow the holder is redirected to after accepting the credential offer. For example, you can include the user's e-mail so that it is already populated in the login screen.
    • prompt : Prompts are sent to the Authentication provider to control the authentication flow. For example, using login would always require the user to authenticate, even if they had already completed login on the same device.

Response

JSON
{
    "uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftenant.vii.mattr.global%22%2C%22credentials%22%3A%5B%2220d6bbe6-a978-447c-b5bd-f33b6dca19e2%22%5D%2C%22request_parameters%22%3A%7B%22login_hint%22%3A%22user%40example.com%22%2C%22prompt%22%3A%22login%22%7D%7D"
}
  • uri : This is the URI that should be used by the intended holder to claim the credential.

When a multi-format credential offer is created, this uri is used to issue all the credential profiles in a single workflow.

Send an offer URI

Once a credential offer URI is generated, you can send it to the intended holder in one of the following methods:

  • Send via a DID message
  • Send via a QR code.
  • Send via a Deeplink.

Send a Credential offer via a DID message

To send the Credential offer as a DID message you must first encrypt it and then send it as an encrypted message.

Step 1: Encrypt a Credential offer

Make a request of the following structure to encrypt the Credential offer:

Request
HTTP
POST /v1/messaging/encrypt

Request body

JSON
{
    "senderDidUrl": "did:web:learn.vii.au01.mattr.global#z6LShWb1DVC2gkxoQ91VwHmNhci2A4NdVH4srFvLiTP6ETBK",
    "recipientDidUrls": ["did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d"],
    "payload": {
        "id": "731961f2-bdc3-4f1e-8d59-cc308fd60ec8",
        "type": "https://mattr.global/schemas/verifiable-credential/offer/OidcCredentialProvider",
        "from": "did:web:organization.com",
        "created_time": 1616466734,
        "body": {
            "uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftenant.vii.mattr.global%22%2C%22credentials%22%3A%5B%2220d6bbe6-a978-447c-b5bd-f33b6dca19e2%22%5D%2C%22request_parameters%22%3A%7B%22login_hint%22%3A%22user%40example.com%22%2C%22prompt%22%3A%22login%22%7D%7D"
        }
    }
}
  • senderDidUrl : Use the Issuer DID.
  • recipientDidUrls : Use the Subject DID.
  • payload :
    • id : Use the Credential configuration id.
    • type : Use the https://mattr.global/schemas/verifiable-credential/offer/OidcCredentialProvider to indicate that this message includes a Credential offer.
    • from : Use the Issuer DID.
    • created_time : Time of creation (Epoch Unix timestamp). The value must be a number and not a string, otherwise the holder will not be able to accept the Credential offer.
  • body :
    • uri : Use the credential offer URI, obtained when the offer was created.
Response
JSON
{
    "jwe": {
        "protected": "eyJhbGciOiJYQzIwUCJ9",
        "recipients": [
            {
                "header": {
                    "alg": "ECDH-1PU+A256KW",
                    "kid": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d#z6LSsvqSJkBvVEsDC8cxMHuQ3sKoLRMXB1MdtoLrMUq6A8Rg",
                    "epk": {
                        "kty": "OKP",
                        "crv": "X25519",
                        "x": "JOLnYaD7L-Rszz7fczPhn6MkNre25PUsztzB1RHoz14"
                    },
                    "skid": "did:key:z6MkreuqFq6WrwozTeGKuUDz8bniTFRNAg8f3ZB862YdLp7v#z6LScyz3YLToyoKwZE6Tfq65hgZUkZdHrC4ZqohcUH9X6Twx"
                },
                "encryption_key": "ag5iKzjJOth9Wa68dCVKJW_vnO_Ga0zSJgQp5rIUg69HCzIjuNYhDg"
            }
        ],
        "ciphertext": "xpW-D6sDPpWc_jk87nEyxPX7JQV8_OZpaQft7ySQ5XmNhoj-lQyDkXDncOCyhB7yMSdZrRBNQjKxlEbpY_WLk1hBoWfsTeszVSAuFbX_VKUSJ7GR6rcnWGVNgDfKS8GsyC_owtswXatkF_65_mzFOygctkUmd2eI5bcpQpWjhw2vqnvnWkb7l2J27aWFF_c9cu52dB559j8lwLYyYC9oSMgV5piB6ppfrWBGo_DigjxvJcAYcjFYqFcT6A1nphPhwVTQ2HNfJodbQoseHub8UQdG4qAOcggq5DI84tbqor1SU9rdPH03jPkLgoO_aeXyJg5meITXoFSiu_tRfvf8QQ6vKq6pkTTXs8zKXcBCGhGIyKBNBG4R4RIY1UffTMnJQQQGBble3P06pGOnsnSop0BtygelB9M0ZEwnAUSAQqN1RR4AQwWcn9nH6hHEu1pMhSvhCuFNAPWS-hg24JGGw8Xe3EEZlLH0PM8qpUAfksPq",
        "iv": "FJq5zKvuPiUQIdRcMtiChHCJByuY8XK9",
        "tag": "u8kT0VAAtTswjGXxNpuX0g=="
    }
}
Step 2: Send an encrypted Credential offer
Request

Make a request of the following structure to send the encrypted Credential offer:

HTTP
POST /v1/messaging/send

Request body

JSON
{
    "to": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d",
    "message": {
        "protected": "eyJhbGciOiJYQzIwUCJ9",
        "recipients": [
            {
                "header": {
                    "alg": "ECDH-1PU+A256KW",
                    "kid": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d#z6LSsvqSJkBvVEsDC8cxMHuQ3sKoLRMXB1MdtoLrMUq6A8Rg",
                    "epk": {
                        "kty": "OKP",
                        "crv": "X25519",
                        "x": "JOLnYaD7L-Rszz7fczPhn6MkNre25PUsztzB1RHoz14"
                    },
                    "skid": "did:key:z6MkreuqFq6WrwozTeGKuUDz8bniTFRNAg8f3ZB862YdLp7v#z6LScyz3YLToyoKwZE6Tfq65hgZUkZdHrC4ZqohcUH9X6Twx"
                },
                "encryption_key": "ag5iKzjJOth9Wa68dCVKJW_vnO_Ga0zSJgQp5rIUg69HCzIjuNYhDg"
            }
        ],
        "ciphertext": "xpW-D6sDPpWc_jk87nEyxPX7JQV8_OZpaQft7ySQ5XmNhoj-lQyDkXDncOCyhB7yMSdZrRBNQjKxlEbpY_WLk1hBoWfsTeszVSAuFbX_VKUSJ7GR6rcnWGVNgDfKS8GsyC_owtswXatkF_65_mzFOygctkUmd2eI5bcpQpWjhw2vqnvnWkb7l2J27aWFF_c9cu52dB559j8lwLYyYC9oSMgV5piB6ppfrWBGo_DigjxvJcAYcjFYqFcT6A1nphPhwVTQ2HNfJodbQoseHub8UQdG4qAOcggq5DI84tbqor1SU9rdPH03jPkLgoO_aeXyJg5meITXoFSiu_tRfvf8QQ6vKq6pkTTXs8zKXcBCGhGIyKBNBG4R4RIY1UffTMnJQQQGBble3P06pGOnsnSop0BtygelB9M0ZEwnAUSAQqN1RR4AQwWcn9nH6hHEu1pMhSvhCuFNAPWS-hg24JGGw8Xe3EEZlLH0PM8qpUAfksPq",
        "iv": "FJq5zKvuPiUQIdRcMtiChHCJByuY8XK9",
        "tag": "u8kT0VAAtTswjGXxNpuX0g=="
    }
}
  • to : Use the intended holder's Subject DID.
  • message : Use the content of the jwe object from the previous step's response (do not include the jwe property name, just its content).
Response

A 200 response indicates that the message payload was sent to the service endpoint of the dereferenced DID Document (or the default MATTR service endpoint).

Accept a Credential offer

Once the Credential offer is shared with the intended holder, it is up to them to use their digital wallet to accept the offer and claim the credential.

The credential is only issued after the intended holder accepts the Credential offer.

Troubleshooting

Below are a few common problems and their solutions:

ProblemHow to solve
The app opens but does not show the credential being offeredMake sure the mobile device is connected to the internet
Scanning the QR code using the phone’s camera doesn’t open the app (opens Google search or tries to load in the browser and fails)
  • - Make sure you have the MATTR Wallet set up with a PIN.
  • - Make sure the QR is large enough to be read by your phone; try creating a larger QR Code (e.g., 300 x 300 px).