Implement a Seamless Data Sharing Flow with Affinidi Iota Framework

Enable your website to request and receive user data with Affinidi Iota Framework securely.

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.

You can easily do this using the Affinidi Portal:

  1. Go to  Affinidi Portal and click on the Affinidi Iota Framework page.

  2. 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.

  1. After setting the fields, including the Wallet used for signing and additional options, click Create.
Create Affinidi Iota Framework
  1. 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
  1. 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

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.

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.

  1. Install the auth-provider and iota-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
  1. 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
  1. Generate the Iota Credentials using the auth-provider and iota-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 the keyId 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.

  1. Install the required libraries to integrate the Affinidi Iota Framework.
npm install -S @affinidi-tdk/iota-browser
  1. 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'
  1. 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;

}
  1. 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()
},[])
  1. 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 the onClick event of a button. We display the button once the iotaSession 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.