Presentation Definition and ID Token Mapping
Presentation Definition is an implementation of the Presentation Exchange protocol published by Decentralized Identity Foundation (DIF). Presentation Definition is a JSON formatted query that defines the data required from the Affinidi Vault user to prove their identity during the authentication flow.
Affinidi Login has a default Presentation Definition that requests Email VC stored on the user’s Affinidi Vault, generated after successful registration.
{
"id": "vp_token_with_email_vc",
"input_descriptors": [
{
"id": "email_vc",
"name": "Email VC",
"purpose": "Check if data contains necessary fields",
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "Email"
}
}
},
{
"path": [
"$.credentialSubject.email"
],
"purpose": "Check if VC contains email field",
"filter": {
"type": "string"
}
},
{
"path": [
"$.issuer"
],
"purpose": "Check if VC Issuer is Trusted",
"filter": {
"type": "string",
"pattern": "^did:key:zQ3shtMGCU89kb2RMknNZcYGUcHW8P6Cq3CoQyvoDs7Qqh33N"
}
}
]
}
}
]
}
Defining a Presentation Definition
To define the presentation definition, you have to be familiar with Presentation Exchange Query (PEX Query) structure.
For example, suppose you want to request more details from the end-user, like the address. In that case, you must define the following structure in your presentation definition.
{
"id": "vp_combined_email_user_profile_combined",
"input_descriptors": [
{
"id": "email_vc",
"name": "Email VC",
"purpose": "Check if data contains necessary fields",
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "^Email$"
}
}
},
{
"path": [
"$.credentialSubject.email"
],
"purpose": "Check if VC contains email field",
"filter": {
"type": "string"
}
},
{
"path": [
"$.issuer"
],
"purpose": "Check if VC Issuer is Trusted",
"filter": {
"type": "string",
"pattern": "^did:key:zQ3shtMGCU89kb2RMknNZcYGUcHW8P6Cq3CoQyvoDs7Qqh33N"
}
}
]
}
},
{
"id": "profile_vc",
"name": "Country VC",
"purpose": "Check if data contains necessary fields",
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "^HITCountry$"
}
}
},
{
"path": ["$.credentialSubject.country"]
}
]
}
}
]
}
In the above structure, we have added a new block based on the default presentation definition to be able to request additional data from the Affinidi Vault; in this case, we ask for the country of the user.
Input Descriptors
Input Descriptors are where we define the data requirements that the user must satisfy to authenticate on your application successfully. It contains the Verifiable Credentials (VCs) that must be available in the Affinidi Vault and the fields that must exist on each VCs.
{
"id": "email_vc",
"name": "Email VC",
"purpose": "Check if data contains necessary fields",
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "^Email$"
}
}
},
{
"path": [
"$.credentialSubject.email"
],
"purpose": "Check if VC contains email field",
"filter": {
"type": "string"
}
},
{
"path": [
"$.issuer"
],
"purpose": "Check if VC Issuer is Trusted",
"filter": {
"type": "string",
"pattern": "^did:key:zQ3shtMGCU89kb2RMknNZcYGUcHW8P6Cq3CoQyvoDs7Qqh33N"
}
}
]
}
}
From the default presentation definition, we have added additional 2 input descriptors:
VC Type: In this input descriptor, we are requesting the Affinidi Vault to provide the Email VC. Please note that in this input descriptor, we are describing from which field the schema definition used can be found from the Email VC:
$.credentialSubject.email
.Email field: In this input descriptor, we request the Affinidi Vault to provide the email information of the user that can be found on the specified path from the Email VC:
$.credentialSubject.email
.Issuer: In this input descriptor, we specified the DID of the issuer to ensure that we only accept VC that was issued by a trusted issuer.
Suppose both descriptors are not present on their specific paths, and the condition defined in the pattern
field is not satisfied. In that case, the Affinidi Login flow does not proceed and displays an error that the VC is unavailable to the end user.
Mapping ID Token from Presentation Definition
Suppose you have modified the default presentation definition and requested additional data from the user’s Affinidi Vault. In that case, you must change the idTokenMapping
of the Login Configuration to include the other data from the VP Token generated by the Affinidi Vault to the ID Token sent to the application after successful authentication.
Below is the default ID Token Mapping defined in the Login Configuration:
[
{
"sourceField": "$.type",
"idTokenClaim": "$.custom[0].type",
"inputDescriptorId": "email_vc"
},
{
"sourceField": "$.credentialSubject.email",
"idTokenClaim": "$.custom[1].email",
"inputDescriptorId": "email_vc"
}
]
It is an array of JSON objects that defines the source and destination field used to generate the ID Token for the application.
sourceField: is the field path defined in the Verifiable Presentation generated by the Affinidi Vault. This is what is defined in the
path
property of the presentation definition.idTokenClaim: is the field representation of the sourceField in the ID Token that is generated by the Affinidi Login service and sent back to the application. All these fields are defined under the
custom
property of the ID Token or define a top-level claim. Check this guide to learn more about customising ID Token claims.inputDescriptorId: is the ID of the
inputDescriptor
from the Presentation Definition where the field value will be parsed.
In the above example, we have requested the address data of the user. To parse and include this data in the ID Token, we have to modify the ID Token Mapping:
[
{
"sourceField": "$.type",
"idTokenClaim": "$.custom[0].type",
"inputDescriptorId": "email_vc"
},
{
"sourceField": "$.credentialSubject.email",
"idTokenClaim": "$.custom[1].email",
"inputDescriptorId": "email_vc"
},
{
"sourceField": "$.credentialSubject.country",
"idTokenClaim": "$.custom[2].country",
"inputDescriptorId": "profile_vc"
}
]
We have added mapping whereby the sourceField
is based on the path defined in the presentation definition, and the idTokenClaim
field name to use is the country
.
Important Note
Avoid mapping multiple fields with the same path from the Presentation Definition into the ID Token Mapping, as it will result in failed login attempts. In our example Presentation Definition above, we have defined twice the$.type
to ensure we get the correct type of VCs from the Affinidi Vault. To prevent the conflict for ID Token Mapping fields, we have used the property inputDescriptorId
to tell the system from which inputDescriptor
in the Presentation Definition the field value should be parsed.Presentation Definition and ID Token Mapping:
For reference, here is the full Presentation Definition and ID Token Mapping Definition based on our example above:
"presentationDefinition": {
"id": "vp_combined_email_user_profile_combined",
"submission_requirements": [
{
"rule": "pick",
"min": 1,
"from": "A"
}
],
"input_descriptors": [
{
"id": "email_vc",
"name": "Email VC",
"purpose": "Check if data contains necessary fields",
"group": ["A"],
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "^Email$"
}
}
},
{
"path": [
"$.credentialSubject.email"
],
"purpose": "Check if VC contains email field",
"filter": {
"type": "string"
}
},
{
"path": [
"$.issuer"
],
"purpose": "Check if VC Issuer is Trusted",
"filter": {
"type": "string",
"pattern": "^did:key:zQ3shtMGCU89kb2RMknNZcYGUcHW8P6Cq3CoQyvoDs7Qqh33N"
}
}
]
}
},
{
"id": "profile_vc",
"name": "Country VC",
"purpose": "Check if data contains necessary fields",
"group": ["A"],
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"purpose": "Check if VC type is correct",
"filter": {
"type": "array",
"contains": {
"type": "string",
"pattern": "^HITCountry$"
}
}
},
{
"path": ["$.credentialSubject.country"]
}
]
}
}
]
},
"idTokenMapping": [
{
"sourceField": "$.type",
"idTokenClaim": "$.custom[0].type",
"inputDescriptorId": "email_vc"
},
{
"sourceField": "$.credentialSubject.email",
"idTokenClaim": "$.custom[1].email",
"inputDescriptorId": "email_vc"
},
{
"sourceField": "$.credentialSubject.country",
"idTokenClaim": "$.custom[2].country",
"inputDescriptorId": "profile_vc"
}
]
Sample ID Token Details
Below is the resulting ID Token for this example:
{
"acr": "0",
"at_hash": "R8YwamO1HFMT3Nf-hBMg-w",
"aud": [
"e7e54cff-1640-4f9b-878u-d8b294a2267c"
],
"auth_time": 1698815469,
"custom": [
{
"type": [
"VerifiableCredential",
"Email"
]
},
{
"email": "email@email.com"
},
{
"country": "Singapore"
},
{
"did": "did:key..."
}
],
"exp": 1698816371,
"iat": 1698815471,
"iss": "https://<PROJECT_ID>.apse1.login.affinidi.io",
"jti": "c77b819d-f4cf-4771-8977-c4c482287569",
"rat": 1698815462,
"sid": "2e86778d-83af-47d9-jai8-1f928e2743bc",
"sub": "did:key..."
}
Updating Presentation Definition and ID Token Mapping
You can modify and extend the default Presentation Definition and ID Token Mapping defined in the Login Configuration by adding more fields or setting specific conditions to the data requested by your application to the user’s Affinidi Vault.
You can customise ID Token following the standards claims depending on the requirements of your application.
Note
Suppose the user previously selected toAllow access automatically next time
, and the Presentation Definition is modified to request additional info from the user’s Affinidi Vault. In that case, the automatic consent will reset, and the user needs to consent again to share their data on the website.Presentation Definition through PEX Query is a powerful tool that allows you to define your data requirements that must be satisfied by the user to be able to authenticate to your application. At the same time, ID Token Mapping allows you to extend the user claims without modifying the logic of your application.
What’s next
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.