Implement a Seamless Data Sharing Flow with Affinidi Iota Framework
To integrate the Affinidi Iota Framework on your website, you must create the Affinidi Iota Framework Configuration and define a Presentation Definition to query the user data from the Affinidi Vault.
Set up Affinidi Iota Framework Configuration
To enable Affinidi Iota Framework on your website, you must create an Affinidi Iota Framework configuration where you select the wallet used to sign the request token and the expiration of the request token, including the Presentation Definition to use to query the data from Vault.
Note
You can only create up to 10 Configurations per Project. If you wish to create more than 10 Configurations, you must create a new Project.You can easily do this using the Affinidi Portal:
Go to
Affinidi Portal and click on the Affinidi Iota Framework page.
Click on Create Configuration and set the following fields:
Signing Wallet: Create a new wallet and provide the new wallet name, or select an existing Wallet that will sign and issue the credentials to the user. Read more about Wallets here.
Lifetime of Request Token: Credential Offers have a limited lifetime to enhance security. Consumers must claim the offer within this timeframe.
Optionally, you can configure whether to enable:
Enable Credential Verification: To verify the credentials the user shares using the Credential Verification service.
Enable Consent Audit Log: To store the consent given by the user whenever they share data with the website.
Note
We recommend enabling these options for the additional benefit of Credential Verification, which verifies if the data shared is tampered-evident and stores the consent provided by the user when sharing data with your website.- After setting the fields, including the Wallet used for signing and additional options, click Create.
![Create Affinidi Iota Framework](/images/docs/affinidi-iota-framework-create-conf.png)
- After creating the configuration, define the Presentation Definitions to query specific data from the Affinidi Vault. We will define the Presentation Definition from the Presentation Exchange protocol to do this.
![Create Presentation Definition](/images/docs/iota-config-create-pex.png)
- Provide the name of the Presentation Definition and then select from the available templates to pre-populate the editor. You can modify the presentation definition template based on the required data you would like to request from the Affinidi Vault.
![Create Presentation Definition Page](/images/docs/iota-presentation-def-page.png)
Click on the + Add button to create additional presentation definitions for the current configuration. After adding the presentation definitions, click on the Create button.
Each Presentation Definition created will have a corresponding Query ID. The Query ID is used to get the presentation definition required to generate the request token to request data from the Affinidi Vault.
Implement the Affinidi Iota Framework
After setting up the Affinidi Iota Framework Configuration and the presentation definitions to query the data from Affinidi Vault, enable requesting data on your website using the Iota Browser library of the Affinidi TDK.
In this example, we will request the user’s Phone Number from the Affinidi Vault. Follow the sample code below using NextJS to implement the Affinidi Iota Framework on your website.
Generate the Iota Credentials for Signing Request Token
To generate the Iota Credentials required to sign the request token and initiate the Affinidi Vault request, implement a backend service that exposes an API endpoint and returns the Iota Credentials. You may use frameworks like Express and NextJS to implement the backend.
Important Note on Iota Credentials Endpoint
To protect users from phishing and forwarding attacks, secure the endpoint with authentication and authorisation, allowing only the user who has proven ownership of the Decentralised Identifier (DID) to access the Iota Credentials created for it. Also, the DID of the current user is a required parameter to generate the Iota Credentials to sign the Request Token. To resolve this, implement the Affinidi Login and extract the user’s DID value from the ID Token provided.
You can explore our Labs to learn how to integrate Affinidi Login into different programming languages and frameworks.
Let’s assume the API endpoint is {BACKEND_SERVICE_URL}/api/iota/get-credentials
, which the client side will later call using a GET method.
- Install the
auth-provider
andiota-core
modules in the backend.
npm install -S @affinidi-tdk/auth-provider @affinidi-tdk/iota-core
pip install affinidi_tdk_iota_core affinidi_tdk_auth_provider
- Import the libraries required to generate the Iota Credentials.
import { Iota } from "@affinidi-tdk/iota-core";
import { AuthProvider } from '@affinidi-tdk/auth-provider';
import affinidi_tdk_iota_core
import affinidi_tdk_auth_provider
import json
- Generate the Iota Credentials using the
auth-provider
andiota-core
modules. The backend API should return a JSON response containing the generated Iota Credentials.
Use the Affinidi CLI Token command to generate the Personal Access Token (PAT) required by the auth-provider
module.
If you have created the Personal Access Token (PAT) with the
--key-id
flag in Affinidi CLI, you must also set thekeyId
with the supplied value in the AuthProvider class.
Get the LOGGED_IN_USER_DID
value from the ID Token using the Affinidi Login.
// NOTE: set your variables for PAT
const privateKey = '<PAT_PRIVATE_KEY_STRING>'
const passphrase = '<PAT_PASSPHRASE>'
const tokenId = '<PAT_ID>'
const projectId = '<PROJECT_ID>'
const authProvider = new AuthProvider({
privateKey,
passphrase,
tokenId,
projectId
})
const iotaToken = authProvider.createIotaToken(
<IOTA_CONFIGURATION_ID>,
<LOGGED_IN_USER_DID>,
);
const iotaCredentials = await Iota.limitedTokenToIotaCredentials(
iotaToken.iotaJwt,
);
// return the API response with the Iota Credential
res.status(200).json(iotaCredentials);
stats = {
tokenId,
passphrase,
privateKey,
projectId,
}
authProvider = affinidi_tdk_auth_provider.AuthProvider(stats)
iotaConfigurationId = "<IOTA_CONFIGURATION_ID>"
userDid = "<LOGGED_IN_USER_DID>"
iotaToken = authProvider.create_iota_token(iotaConfigurationId, userDid)
iotaCredentials = affinidi_tdk_iota_core.Iota.limited_token_to_iota_credentials(iotaToken.iotaJwt)
return json.dumps(iotaCredentials)
The front end will use the generated Iota Credentials to create a connection (session) with the Affinidi Iota Framework service and sign the request token to the Affinidi Vault.
Initialise Iota Session and Handle Request
We will implement the Iota Browser library of Affinidi TDK on the client side to initiate the request using the Iota Credentials generated from the backend. You may use frontend frameworks like React and NextJS.
In this example, we will be creating a component that will be imported into a page of a React website.
Important Note
The Affinidi TDK’s Iota Browser library is a browser-only module that creates a WebSocket connection and requires access to the browser window object to initiate a data request for the Affinidi Vault.
If you are using a framework with a server-side rendering like NextJS, you can refer to this guide.
- Install the required libraries to integrate the Affinidi Iota Framework.
npm install -S @affinidi-tdk/iota-browser
- Import the
iota-browser
library into the code to create the session and prepare request token to initiate data request.
import {
IotaCredentials,
IotaError,
IotaRequest,
IotaResponse,
OpenMode,
Session,
} from "@affinidi-tdk/iota-browser";
import { useEffect, useState } from 'react'
- Create an async function to get the Iota Credentials through an API call from the backend API created from the previous steps.
async function getIotaCredentials() {
const response = await fetch(
"/api/iota/get-credentials",
{
method: "GET",
},
);
const iotaCredentials = (await response.json()) as IotaCredentials;
}
- Initialise the Iota Session after generating the Iota Credentials. The
iotaSession.initialize
will open a WebSocket connection and listen to the callback events.
You may initialise the
IotaSession
when loading the component or when the user clicks on the button or link. In our case, we are initialising the iotaSession upon loading the component.
const [iotaSession, setIotaSession] = useState<Session>();
useEffect(() => {
const initIota = async () => {
try {
const iotaCredentials = await getIotaCredentials();
const iotaSession = new Session({ credentials: iotaCredentials });
await iotaSession.initialize();
setIotaSession(iotaSession);
} catch (error) {
if (error instanceof IotaError) {
console.log(error.code);
}
}
}
initIota()
},[])
- Create an async function to initiate Affinidi Iota Framework request and open the Affinidi Vault based on Query ID when the user clicked on a link or button. The
IOTA_CONFIG_QUERY_ID
is the Presentation Definition identifier to query the data from the Affinidi Vault.
In our example, we are setting the
handleShareRequest
in theonClick
event of a button. We display the button once theiotaSession
is successfully initiated.
async function handleShareRequest() {
if (!iotaSession) {
throw new Error("IotaSession not initialized");
}
try {
const request = await iotaSession.prepareRequest({ queryId: '<IOTA_CONFIG_QUERY_ID>' });
request.openVault({ mode: OpenMode.Popup })
const response = await request.getResponse();
} catch (error) {
if (error instanceof IotaError) {
console.log(error.code);
}
}
}
return (
<>
{iotaSession && (
<Button
onClick={() => handleShareRequest()}
>
Share Phone Number
</Button>
)}
)
After successfully initiating the Iota session and generating the Request Token required for the Affinidi Vault, it will open a browser popup that loads the Affinidi Vault consent screen.
Once the user consents to allow access to the requested data, the request.getResponse()
will parse the data sent back to the client from the initialised WebSocket.
The sample response will be a JSON format that contains the requested data if the request is successful:
{
"correlationId": "b099967b-8736-44db-93fa-a741e8016848",
"verifiablePresentation": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"id": "claimId:gpf1UbjaUwwFtgKUbu_Yj",
"holder": {
"id": "did:key:zQ3shPh4UnmNgNza3TB2KkhKDrcEVVnntyjdNWYqDg8jKo6Gj"
},
"proof": {
...
},
"type": [
"VerifiablePresentation"
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://schema.affinidi.com/EmailV1-0.jsonld"
],
"credentialSchema": {
"id": "https://schema.affinidi.com/EmailV1-0.json",
"type": "JsonSchemaValidator2018"
},
"credentialSubject": {
"email": "info@affinidi.com"
},
"holder": {
"id": "did:key:zQ3shPh4UnmNgNza3TB2KkhKDrcEVVnntyjdNWYqDg8jKo6Gj"
},
"id": "claimId:31de43221eeb658e",
"issuanceDate": "2024-07-03T05:05:56.702Z",
"issuer": "did:key:zQ3shtMGCU89kb2RMknNZcYGUcHW8P6Cq3CoQyvoDs7Qqh33N",
"proof": {
...
},
"type": [
"VerifiableCredential",
"Email"
]
}
]
},
"presentationSubmission": {
"id": "NT080x7PwL3hG2MFxtCor",
"definition_id": "token_with_email_vc",
"descriptor_map": [
{
"id": "email_vc",
"path": "$.verifiableCredential[0]",
"format": "ldp_vc"
}
]
}
}
Parse the verifiablePresentation
to extract the requested data from the Affinidi Vault shared by the user. The verifiablePresentation
can have multiple verifiableCredential
entries if more than one Verifiable Credentials are requested and shared by the user.
If the request failed or if the user decline to share their data, an instance of the IotaError
will be thrown in a JSON format containing the following information:
{
"code": "DataRequestError",
"correlationId": "18e8aff3-bd29-49c9-9438-eb20c9f961c9",
"issue": "access_denied"
}
Learn more about the Affinidi Iota Framework and how the integration works using the sample application.
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.