Sample App with Redirect Implementation
Redirect mode enables a consent-driven data-sharing flow without requiring Affinidi Login. It uses a Redirect URL to generate a signed request token and initiate data sharing.
Before you begin
Ensure you have:
An Affinidi Vault account. Follow this guide if you haven’t set it up.
Affinidi CLI installed. Follow the guide below if not nstalled.
Git installed on your machine. Follow this guide on how to install Git.
Node.js installed on your machine - NodeJS.
Download the Application
Download the sample code as a ZIP from the GitHub repo or generate it using Affinidi CLI:
Download the sample code as a ZIP from the GitHub Repo or generate it using Affinidi CLI:
affinidi generate app --provider=affinidi --framework=nextjs --library=nextauthjs --path=affinidi-sample-appSelect
nwhen prompted to Automatically configure sample app environment, we will configure it later.
The above command generates the code sample in the affinidi-sample-app directory.
Important Note
The downloadable sample application is provided only as a guide to quickly explore and learn how to integrate the components of Affinidi Trust Network into your application. This is NOT a Production-ready implementation. Do not deploy this to a production environment.Install App Dependencies
Navigate to the affinidi-sample-app directory and install dependencies:
npm installRun the Application
After installing the dependencies and setting up the required details in the application, run the following command to start the app locally:
npm run devOnce it is successfully started, visit the app using the link http://localhost:3000.
Integrate Affinidi Iota Framework
To integrate the Affinidi Iota Framework on your website to enable data-sharing flow using Redirect flow, you must:
- create the Affinidi Iota Framework Configuration
- define a Presentation Definition to query the user data from the Affinidi Vault.
To learn more about Affinidi Iota Framework Configuration and a guide on creating it, visit this page.
Create a Personal Access Token
After creating the Affinidi Iota Framework configuration and the Presentation Definition to query the data from the Affinidi Vault, we’ll create a Personal Access Token (PAT), a machine user that authenticates to the services and performs actions on your behalf.
To create a Personal Access Token (PAT), run the following Affinidi CLI command:
affinidi token create-token -n IotaToken --auto-generate-key --passphrase "MySecretPassphrase" --with-permissionsThe above command will create a Personal Access Token (PAT).
The PAT includes:
- Private/public keys (protected with a passphrase)
- A defined policy granting permissions to call Affinidi Iota Framework
Copy the output and we will configure it later in the sample app’s environment variables.
Sample output of the command:
{
"tokenId": "086b64ce-oau0-huy7-9876-65d76e84fa36",
"projectId": "cba41b90-34bg-jhg9-acgf-800baa9a40a0",
"privateKey": "...",
"passphrase": "MySecretPassphrase"
}Important Note
Keep your private key and passphrase secure. They are shown only once.Configure the App to Integrate with Affinidi TDK
Let’s configure the Personal Access Token (PAT) details into the environment variables of the sample application.
Set the following variables with the values provided by the Personal Access Token (PAT):
PROJECT_ID="<PAT.projectId>"
TOKEN_ID="<PAT.tokenId>"
PRIVATE_KEY="<PAT.privateKey>"
PASSPHRASE="<PAT.passphrase>"Keep the
PUBLIC_KEYandKEY_IDinenvfile empty.
Try the Data Sharing Flow
After setting up PAT and running the app:
- Go to http://localhost:3000.
- Click Receive Credentials.

- On the Receive Credentials page, you will see a dropdown field listing all the available Affinidi Iota Framework configurations created on your project. Select:
- The Affinidi Iota Framework configuration.
- The Redirect URL for Redirect mode.
- The Presentation Definition you created to query data.
- Click Share. This will show the consent screen of the Affinidi Vault, which displays the data being requested and asks if you would like to allow access to this data.
How the Integration Works
The sample app uses Affinidi TDK modules, let’s review these:
- Auth Provider: Authenticates and authorises the PAT.
- Iota client: Calls Affinidi Iota Framework service to load configurations.
- Common: Builds the share link and redirects the user to Affinidi Vault.
Read more about Affinidi TDK here.
Generating a Signed Request Token
The backend exposes an API endpoint that generates the signed request token. This token is used by the frontend to request and receive data from Affinidi Vault.
It uses two modules from Affinidi TDK:
Auth Provider: Authenticates and authorises the Personal Access Token (PAT) configured earlier. Calls the Iota client to initiate the data-sharing request. The request requires:
Affinidi Iota Framework Configuration ID
Redirect URL
Iota client: Initiates the data-sharing request and parses the signed request token. This token is used to redirect the user to Affinidi Vault.
Source Path: src/lib/clients/iota.ts
export async function initiateDataSharingRequest(
configurationId: string,
queryId: string,
redirectUri: string,
nonce: string
) {
const authProvider = getAuthProvider();
const api = new IotaApi(
new Configuration({
apiKey: authProvider.fetchProjectScopedToken.bind(authProvider),
basePath: `${apiGatewayUrl}/ais`,
})
);
const { data: dataSharingRequestResponse } =
await api.initiateDataSharingRequest({
configurationId,
mode: IotaConfigurationDtoModeEnum.Redirect,
queryId,
correlationId: uuidv4(),
nonce,
redirectUri,
});
const { correlationId, transactionId, jwt } =
dataSharingRequestResponse.data as InitiateDataSharingRequestOKData;
return { correlationId, transactionId, jwt };
}Initialising Data Sharing Request
After setting up the API endpoint:
- Initialise the page in the frontend.
- Fetch the signed request token.
- Redirect the user to Affinidi Vault.
In the sample app:
- The
handleRedirectFlowSharefunction runs when you select an Affinidi Iota Framework configuration from the dropdown.
Refer to the
configurationsQueryquery or sample code to load configurations using the Iota client of the Affinidi TDK.
Source Path: src/components/iota/RedirectFlowPage.tsx
async function handleRedirectFlowShare(queryId: string) {
setIsFormDisabled(true);
const response = await fetch("/api/iota/init-share", {
method: "POST",
body: JSON.stringify({
configurationId: selectedConfigId,
queryId,
redirectUri: selectedRedirectUri,
nonce,
}),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
const data = await response.json();
const toStore = {
nonce,
configurationId: selectedConfigId,
correlationId: data.correlationId,
transactionId: data.transactionId,
};
localStorage.setItem("iotaRedirect", JSON.stringify(toStore));
const vaultLink = getShareLink(data.jwt, "client_id");
router.push(vaultLink);
}After fetching the signed request token:
- Build the share link using VaultUtils from Affinidi TDK’s
commonpackage. - Attach handleRedirectFlowShare to the Share button.
It activates when a configuration is selected.
function getShareLink(jwt: string, clientId: string) {
if (typeof window !== "undefined" && window.localStorage) {
const vaultUrl = window.localStorage.getItem("affinidiVaultUrl");
if (vaultUrl) {
return buildShareLinkInternal(vaultUrl, jwt, clientId);
}
}
return VaultUtils.buildShareLink(jwt, clientId);
}The handleRedirectFlowShare is attached to the Share button and is enabled when selecting a Configuration from the dropdown list.
{selectedQuery && (
<>
<h1>Generated nonce: {nonce}</h1>
<br />
<Button
disabled={isFormDisabled}
onClick={() => handleRedirectFlowShare(selectedQuery)}
>
Share
</Button>
</>
)}The user is then redirected to Affinidi Vault:
- The consent screen displays requested data.
- The user can approve or deny access.
Source Path: src/components/iota/RedirectFlowCallbackPage.tsx
const getIotaResponse = async (params: GetIotaResponseParams) => {
const response = await fetch("/api/iota/iota-response", {
method: "POST",
body: JSON.stringify(params),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
return await response.json();
};The Iota Response is fetched from the backend using the response_code sent from the Affinidi Vault.
Source Path: src/lib/clients/iota.ts
export async function fetchIotaVpResponse(
configurationId: string,
correlationId: string,
transactionId: string,
responseCode: string
) {
const authProvider = getAuthProvider();
const api = new IotaApi(
new Configuration({
apiKey: authProvider.fetchProjectScopedToken.bind(authProvider),
basePath: `${apiGatewayUrl}/ais`,
})
);
const iotaVpResponse: FetchIOTAVPResponseOK = await api.fetchIotaVpResponse({
configurationId,
correlationId,
transactionId,
responseCode,
});
const vp = JSON.parse((iotaVpResponse.data as any).vpToken);
return { vp: vp, nonce: iotaVpResponse.data.nonce };
}The Verifiable Presentation token vpToken is parsed from the Iota Response, including the nonce value, to verify the request and response. The response is a JSON format, like the sample below:
{
"correlationId": "ab0a1309-bec8-4c28-828f-8a1c7e8ce606",
"presentationSubmission": {
"descriptor_map": [
{
"id": "email_vc",
"path": "$.verifiableCredential[0]",
"format": "ldp_vc"
}
],
"id": "Ub2yNyMEFlW8ziKMKwVzk",
"definition_id": "token_with_email_vc"
},
"nonce": "6c877dd0-a844-4981-be15-aceed1d2fd5c",
"vpToken": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiablePresentation"
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://schema.affinidi.com/EmailV1-0.jsonld"
],
"id": "claimId:0ecab5796c9f160b",
"type": [
"VerifiableCredential",
"Email"
],
"holder": {
"id": "did:key:zQ3shq75QjFhLLeVCC5MATFdSQPrfkDwHoXBUPKGScHSdWi8p"
},
"credentialSubject": {
"email": "info@affinidi.com"
},
"credentialSchema": {
"id": "https://schema.affinidi.com/EmailV1-0.json",
"type": "JsonSchemaValidator2018"
},
"issuanceDate": "2024-07-29T05:25:57.982Z",
"issuer": "did:key:zQ3shXLA2cHanJgCUsDfXxBi2BGnMLArHVz5NWoC9axr8pEy7",
"proof": {
...
}
}
],
"holder": {
"id": "did:key:zQ3shq75QjFhLLeVCC5MATFdSQPrfkDwHoXBUPKGScHSdWi8p"
},
"id": "claimId:O81O9IXOs-uKQcwctMpEb",
"proof": {
...
}
}
}The verifiableCredential can have multiple entries depending on the Presentation Definition you defined and the Verifiable Credentials shared by the user from their Affinidi Vault.
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.


