light-mode-image
Learn
Credential offer

How to create an OID4VCI credential offer

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 parameters to support the issuance workflow.

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

The process is similar for both the Authorization Code flow and the Pre-authorized Code flow. The main difference is the type of credential offer you need to create, depending on which workflow you are using.

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 (Only required when sharing the offer as a DID message):
    • 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.
      • 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

The OID4VCI credential offer lifecycle comprises the following steps:

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

Generate an offer URI

Generate the offer URI by making one of the following requests based on the issuance flow you are using:

The offer URI can be created either via the MATTR Portal or the MATTR VII Platform APIs.

  1. In the navigation panel on the left-hand side, expand the Credential Issuance menu.
  2. Select Credential offer.
  3. Select the Select button.
  4. Check the checkbox next to the credential configuration you wish to include in the credential offer.
  5. Select the Apply button.
  6. Select the Generate button.
  7. Download the generated QR code by selecting the Download button.

Make a request of the following structure to generate a new credential offer for an OID4VCI Authorization Code flow:

Request
POST /v1/openid/offers
Request body
{
  "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 formats 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 CWT, JSON and/or mDocs 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

Response 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"
}
  • uri : This is the URI that should be used by the intended holder to claim the credential. For an Authorization Code flow credential offers this URI includes the parameters required to redirect the user to the configured Authentication provider to complete authentication before issuing the credential.

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

The Pre-authorized Code flow is only supported for mDocs.

Make a request of the following structure to generate a new credential offer for an OID4VCI Pre-authorized Code flow:

Request
POST /v1/openid/offers/pre-authorized
Request body
{
  "credentials": ["707e920a-f342-443b-ae24-6946b7b5033e"],
  "userId": "b7e2c8f4-1a2b-4c3d-9e5f-8a7b6c5d4e3f",
  "transactionCodeConfiguration": {
    "inputMode": "numeric",
    "description": "Please enter the one-time code that was sent to you via email."
  },
  "claims": {
    "givenName": "John",
    "familyName": "Doe",
    "email": "john.doe@example.com",
    "userId": "e7b8c2f1-4a3d-4e6b-9c2a-1f5d8e7a9b3c"
  },
  "claimsToPersist": ["userId"],
  "expiresIn": {
    "minutes": 5,
    "seconds": 0
  }
}
  • credentials : This array includes a list of identifiers for mDoc 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 formats of the same credential in a single flow, include all the required credential configuration id elements in the request payload.

    Providing an identifier of a non-mDoc credential configuration will result in an error.

  • userId : Unique system generated identifier to reference the user for this offer. This can be obtained by searching for a user. If not provided, a new user entity will be created.

  • transactionCodeConfiguration : This object contains the configuration for the transaction code that will be associated with this credential offer and must be provided by the holder when claiming the credential. It includes the following properties:

    • inputMode : The input mode for the transaction code. Currently only numeric is supported.
    • description : A description of the transaction code that will be sent to the user as part of the credential offer.
  • claims : This object contains the claims that will be included in the issued credential. The claims must match the mapping defined in the Credential configuration. By default, claims data is only stored for the lifetime of the offer, unless you specify them in the optional claimsToPersist array.

  • claimsToPersist : This array includes a list of claims that will be persisted in the MATTR VII database. By default no claims are persisted.

  • expiresIn : Specifies when the offer will expire. Once the offer expires, the user can no longer use it to claim a credential, and a new offer must be generated. The expiration period can include any combination of minutes and seconds. By default, the offer expires in 5 minutes, and the maximum allowed duration is 10 minutes.

Response

Response body
{
  "id": "b1a7c9e2-4d5f-4a6b-9e8c-2f3d4b5a6c7e", 
  "userId": "b7e2c8f4-1a2b-4c3d-9e5f-8a7b6c5d4e3f",
  "uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%2C%22credentials%22%3A%5B%222edaf985-fcc2-4448-9c8e-a04c6c7351c2%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22stukD6lg9c9tQ3jUCa32wVi1HI%2BQIVsFK%2FQPvC2CHRs%3D%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%2C%22description%22%3A%22Please%20provide%20the%20one-time%20code%20that%20was%20sent%20via%20e-mail%22%7D%7D%7D%7D", 
  "expiresAt": "2025-05-01T00:01:00.000Z", 
  "transactionCode": 493536
}
  • id : Unique system generated identifier to reference this offer.
  • uri : This is the URI that should be used by the intended holder to claim the credential. For a Pre-authorized Code flow credential offers this URI includes the pre-authorized code which enables the wallet to retrieve the credential without requiring the user to provide any additional authentication.
  • expiresAt : The date and time when the offer will expire. Once the offer expires, the user can no longer use it to claim a credential, and a new offer must be generated.
  • transactionCode : This is the transaction code that must be provided by the user when claiming the credential. This code must be shared by the issuer with the holder via a separate and secure channel.

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 QR code.
  • Send via a Deeplink.
  • Send via a DID message.

You can use any of these methods regardless of the issuance flow you are using (Authorization Code or Pre-authorized Code).

A common way to allow a digital wallet user to claim a credential is to encode the offer URI into a QR code. You could even print out the QR code.

You can use a tool similar to the following to convert the URI to a QR code (make sure you use the Plain text option where available):

Once the QR code is created, you can send it to the intended holder via your preferred communication channel.

Best practices

  • Make sure the QR code is large enough in size to be resolvable by a phone camera; 200px square is generally sufficient.

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
POST /v1/messaging/encrypt
Request body
{
  "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

Response body
{
  "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

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

Request
POST /v1/messaging/send
Request body
{
  "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.

How would you rate this page?