Skip to main content

04 Advanced Credential Interactions

In this guide, we'll look at credential interactions involving e.g. presentation of a different credential by a holder before a credential issuance.

Interactions with EUDI Reference Wallet

EUDI Reference Wallet does not support the OpenID extension of OAuth 2.0 Authorization. Therefore, the openid authentication with either id_token or vp_token is unsupported.
To issue credentials into an EUDI Wallet, you need to set the authorization field of wallet config to one of these values:

  • none: Use this value when the issuer will only issue credentials with Pre-Authorized Credential Offers
  • oauthNoAuth: This option uses a mock authorization server that always authorizes the caller. Use only for testing
  • oauthCustom: Use this option with a custom authentication provider. More information here.

Here are examples of issuer definitions using the first two options. The oauthCustom option is explained in more details here.

{
"name": "Identity Card",
"id": "id_card",
"signingKeyIdentifier": "x509",
"authorization": "none",
"credentialFormat": "sd_jwt_vc",
"credentialIssuer": "IssuanceQueue",
"credentialType": "urn:identity_card",
"disclosableClaims": [
"$.family_name",
"$.given_name"
]
}

Filling credential claims from a presentation

For this use-case, we'll use a wallet with the following configuration:

{
"trustFramework": "NOOP",
"walletKeyIdentifier": "did",
"credentialIssuers": [
{
"id": "poa",
"name": "Power of Attorney",
"credentialFormat": "jwtimport { Mermaid } from 'mdx-mermaid/Mermaid'_vc_vcdm",
"credentialIssuer": "VPDriven",
"credentialType": "VerifiableCredential,PowerOfAttorney",
"issuerConfiguration": {
"credentialVerifierId": "national_id_identity_disclosure",
"mapping": {
"$.[0].firstName": "$.firstName",
"$.[0].familyName": "$.familyName",
"$.[0].dateOfBirth": "$.dateOfBirth"
}
}
}
],
"credentialVerifiers": [
{
"id": "passport_full_disclosure",
"name": "Passport Full Disclosure",
"presentationDefinition": {
"id": "national_id_identity_disclosure",
"name": "National ID",
"presentationDefinition": {
"format": {
"jwt_vc": {
"alg": ["ES256"]
},
"jwt_vp": {
"alg": ["ES256"]
}
},
"id": "national_id_identity_disclosure_presentation",
"input_descriptors": [
{
"id": "national_id",
"constraints": {
"fields": [
{
"name": "National ID",
"filter": {
"const": "urn:national_id",
"type": "string"
},
"path": ["$.vct"]
},
{
"name": "Photo",
"path": ["$.photo"]
},
{
"name": "First Name",
"path": ["$.firstName"]
},
{
"name": "Family Name",
"path": ["$.familyName"]
},
{
"name": "Date of Birth",
"path": ["$.dateOfBirth"]
},
{
"name": "Nationality",
"path": ["$.nationality"]
}
]
}
}
]
}
}
}
],
"oidcRevision": {
"oidc4vci": "Draft15",
"oidc4vp": "Draft23"
}
}

The credential verifier uses a DIF Presentation Exchange Presentation Definition, which will match a credential of type urn:national_id and disclose the claims photo, firstName, familyName, dateOfBirth and nationality.
The credential issuer will map the values firstName, familyName and dateOfBirth of the presented credential to the credential that will be issued. In this case, the code stays the same as in a typical interaction.
Note: This interaction (and all following interactions) only work with in-time authorized credential issuance flow.

Verifying a Credential Presentation before Issuing a Credential

For this use-case, we'll consider the following wallet configuration:

{
"trustFramework": "NOOP",
"walletKeyIdentifier": "did",
"credentialIssuers": [
{
"id": "poa",
"name": "Power of Attorney",
"credentialFormat": "jwt_vc_vcdm",
"credentialIssuer": "CredentialRequirements",
"credentialType": "VerifiableCredential,PowerOfAttorney",
"issuerConfiguration": {
"credentialVerifierId": "national_id_identity_disclosure"
}
}
],
"credentialVerifiers": [
{
"id": "passport_full_disclosure",
"name": "Passport Full Disclosure",
"presentationDefinition": {
"id": "national_id_identity_disclosure",
"name": "National ID",
"presentationDefinition": {
"format": {
"jwt_vc": {
"alg": ["ES256"]
},
"jwt_vp": {
"alg": ["ES256"]
}
},
"id": "national_id_identity_disclosure_presentation",
"input_descriptors": [
{
"id": "national_id",
"constraints": {
"fields": [
{
"name": "National ID",
"filter": {
"const": "urn:national_id",
"type": "string"
},
"path": ["$.vct"]
},
{
"name": "Photo",
"path": ["$.photo"]
},
{
"name": "First Name",
"path": ["$.firstName"]
},
{
"name": "Family Name",
"path": ["$.familyName"]
},
{
"name": "Date of Birth",
"path": ["$.dateOfBirth"]
},
{
"name": "Nationality",
"path": ["$.nationality"]
}
]
}
}
]
}
}
}
],
"oidcRevision": {
"oidc4vci": "Draft15",
"oidc4vp": "Draft23"
}
}

In order to specify required values of credential fields, the call to CredentialIssuanceInit needs to be modified. The following example will require field firstName to have the value John and field familyName to have the value Doe.

firstNameVal := wallet.FieldsToVerify_Value{}
_ = firstNameVal.FromFieldToVerifyString("John")

familyNameVal := wallet.FieldsToVerify_Value{}
_ = familyNameVal.FromFieldToVerifyString("Doe")

requirements := &wallet.IssuanceRequirementsToVerify{
VerifiedFields: []wallet.FieldsToVerify{
{
Path: "$.firstName",
Value: firstNameVal,
},
{
Path: "$.familyName",
Value: familyNameVal,
},
},
}

issInitResp, err := client.CredentialIssuanceInitWithResponse(ctx, credentialId,
&wallet.CredentialIssuanceInitParams{WalletId: walletId},
wallet.CredentialIssuanceInit{IssuerId: "id_card", IssuanceRequirementsToVerify: requirements})
if err != nil {
log.Panicf("failed to init issuance: %v", err)
}

if issInitResp.StatusCode() != 200 {
log.Panicf("error while issuance init: %d %s", credCreateResp.StatusCode(), credCreateResp.Body)
}

Putting Both flows together

Adding verification requirements to issuers of VPDriven type is possible in the same way as in the case of CredentialRequirements type. In this case, the wallet configuration is the same as in case of filling credential claims from a presentation.

Custom Authorization Provider/Authentic Source

Custom authentication flow can be plugged into the Credential Issuance process. This is done by creating a web service exposing 2 endpoints:

  • Authentication redirect endpoint with 2 query parameters:
    • redirect_uri - URI to which the Authentication provider redirects to after completing authentication
    • state - A unique state used to track requests
  • Authentication result endpoint

After accepting a credential offer, the holder is redirected to the Authentication redirect endpoint. This request contains a query parameter called redirect_uri.
After completing authentication, the Authentication service redirects the holder to the URL specified in this query parameter. Before redirecting, the authentication service appends a new query parameter called authn_result, which specifies an URL, where the Issuer's Authorization Server is able to fetch the authentication result.
The flow is described by the following diagram:

sequenceDiagram participant Issuer actor Holder participant Authn Issuer ->> Holder: Offer URL Holder ->> Issuer: Authorization Request Issuer ->> Holder: Authn redirect Holder -->> Authn: Follow Authn redirect activate Authn Authn ->> Holder: Authn Response redirect <br> (contains authn_result) Holder -->> Issuer: Follow Authn Response redirect Issuer ->> Authn: Query authn_result Authn ->> Issuer: Send authn result + data Issuer <<-->> Holder: The rest of issuance interaction deactivate Authn

Authentication result is a JSON with the following structure:

{
"authnStatus": "ok/error",
"data": [
{
"given_name": "Jonatan",
"family_name": "Cesto"
...
},
{
"date_of_birth": "01-01-2000",
...
},
]
}

The data sent from the Authentication Service can be used to issue a Credential using the VPDriven credential issuer. Claims from the Authentic Source are accessed in the same way as claims from Credential Presentations.
The following example shows configuration of a VPDriven issuer that uses a custom authentication provider with data mappings to the previous data object.

{
"trustFramework": "NOOP",
"walletKeyIdentifier": "did",
"credentialIssuers": [
{
"id": "poa",
"name": "Power of Attorney",
"credentialFormat": "jwtimport { Mermaid } from 'mdx-mermaid/Mermaid'_vc_vcdm",
"credentialIssuer": "VPDriven",
"credentialType": "VerifiableCredential,PowerOfAttorney",
"authorization": "openidCustom",
"authnProviderUrl": "https://my.authn.provider.com/handle_request"
"issuerConfiguration": {
"credentialVerifierId": "national_id_identity_disclosure",
"mapping": {
"$.[0].given_name": "$.firstName",
"$.[0].family_name": "$.familyName",
"$.[2].date_of_birth": "$.dateOfBirth"
}
}
}
],
"oidcRevision": {
"oidc4vci": "Draft15",
"oidc4vp": "Draft23"
}
}

For issuers that are using a custom authentication provider, the authorization field of the issuer definition must be set to one of these values:

  • oauthCustom: Use this value in the case you need to have interactions compatible with the EUDI Wallet
  • openidCustom: Use this value otherwise