Sample Application with Credential Issuance

Use this guide to setup and run the sample application using NextJS that integrates with Credential Issuance service to get you started quickly.
Before you begin
  1. Set up Affinidi Vault account. Follow the guide below if you haven’t set it up yet.
Set up Affinidi Vault

Set up an Affinidi Vault account using the Web Vault or install the Mobile Vault (for Android).

The same setup steps for Mobile Vault.

  1. Click on Get started if you are creating a new account, or click on Restore from Backup if you have an existing backup of your Affinidi Vault. Provide the Passphrase to secure your Affinidi Vault.

You have the option to enable Biometrics to unlock your Affinidi Vault easily instead of using Passphrase.

Affinidi Vault Setup
  1. Enter your email address to register with the Affinidi Vault. An OTP will be sent to this email for verification.
Affinidi Vault Passphrase
  1. Enter the OTP sent to the email you have provided for verification to complete the setup.
Affinidi Vault Email Verification

After successfully providing the OTP, you are redirected to the Affinidi Vault dashboard.

  1. Install the Affinidi CLI. Follow the guide below if it hasn’t been installed.
Set up Affinidi CLI
  1. Download and install NodeJS on your machine if you haven’t set it up yet.
  1. Install Affinidi CLI using Node Package Manager (npm).
npm install -g @affinidi/cli
  1. Verify that the installation is successful.
affinidi --version
  1. Make sure you have Git installed on your machine. Follow this guide on how to install Git.

  2. Install the NodeJS on your machine if you haven’t set it up yet.

Download the Application

You can download as ZIP file the code sample from the GitHub Repo or generate it using Affinidi CLI with the following command:

affinidi generate app --provider=affinidi --framework=nextjs --library=nextauthjs --path=affinidi-sample-app

Select n when prompted to Automatically configure sample app environment, we will configure it later.

The above command will generate the code sample in the affinidi-sample-app directory.

Install Dependencies

After successfully generating the sample app, go to the affinidi-sample-app directory from your terminal and install the required dependencies using the following commands:

npm install

Integrate Affinidi Login

The Credential Issuance requires the Decentralised Identifier (DID) of the user to set the Holder DID (holderDid) as a parameter when creating a Credential Offer. To get the User DID, we will implement Affinidi Login to authenticate and extract the user DID value from the ID Token provided.

Create Login Configuration

Name: Affinidi Sample App

Redirect URIs: http://localhost:3000/api/auth/callback/affinidi

Using Affinidi CLI
  1. Log in to Affinidi CLI by running:
affinidi start
  1. Once you have successfully logged in, create the Login Configuration by running:
affinidi login create-config --name='Affinidi Sample App' --redirect-uris='http://localhost:3000/api/auth/callback/affinidi'
  • --name is what you want your login configuration to be called.
  • --redirect-uris is the URL on your application where the user gets redirected after the successful authentication.

Learn more on how to manage your Login Configurations using Affinidi CLI.

Using Affinidi Portal
Create new Login Configuratioin
  1. Go to  Affinidi Login under the Services section.

  2. Click on the Create Login Configuration and provide the required details.

  • Name is the string that describes your login configuration.
  • Redirect URIs is the URL on your application where the user gets redirected after the successful authentication.
  1. Click on create and confirm if all the details are correct.
Login Configuratation new client
  1. After confirming the details, another popup shows the Client ID and Client Secret for your Login Configuration. Copy the generated Client Credentials and use them to integrate with Affinidi Login.

  2. After copying the Client ID and Client Secret and closing the popup, you are redirected back to the Affinidi Login page.

Login Configuration uses the default Presentation Definition (presentationDefinition) and ID Token Mapping (idTokenMapping) that is used to request the user’s email address during the authentication flow.

Learn more about customising the Presentation Definition and ID Token using this guide.

Configure the App to Enable Affinidi Login

Once the Login Configuration is created, set up the client credentials provided to integrate Affinidi Login.

Copy and set up the environment variables:

cp .env.example .env

Set the following variables with the values provided by the Login Configuration:

PROVIDER_CLIENT_ID="<LoginConfig.auth.ClientID>"
PROVIDER_CLIENT_SECRET="<LoginConfig.auth.ClientSecret>"
PROVIDER_ISSUER="<LoginConfig.auth.Issuer>"

The <LoginConfig.auth.*> are values from Login Configuration.

Run 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 dev

Once it is successfully started, visit the app using the link http://localhost:3000 and click on the Affinidi Login button to test if the Affinidi Login flow is working.

Implement Credential Issuance

After successfully setting up the Affinidi Login, let’s configure the integration to enable credential issuance flow.

Set up Credential Issuance Configuration

To issue a Verifiable Credential, it is required to setup the Issuance Configuration on your project, where you select the issuing wallet and supported schema to create a credential offer that the application issue.

You can easily do this using the Affinidi Portal:

  1. Go to  Affinidi Portal and click on the Credential Issuance page.

  2. Click on Create Configuration and set the following fields:

Issuing 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 Credential Offer: Credential Offers have a limited lifetime to enhance security. Consumers must claim the offer within this timeframe.

Supported Schemas: List of allowed schemas for creating a credential offer. Issuance will validate the credential data based on the supported schema before creating the Credential Offer. Create the Schema through the Schema Builder.

The Credential Configuration ID specified on each supported schema is used as a reference when creating a Credential Offer.

  1. After setting the fields and providing the list of the supported schema, click Create.
Create Credential Issuance

Create a Personal Access Token

After creating the Credential Issuance configuration and the selecting the supported Schemas to issue credential to the 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 IssuanceToken --auto-generate-key --passphrase "MySecretPassphrase" --with-permissions

The above command will create a Personal Access Token (PAT), including private/public keys protected with a passphrase and a defined policy to grant the PAT permission to perform actions on your behalf, in this case, calling Credential Issuance service.

Copy the command output, and we’ll configure it later on the environment variables of the sample application — the sample output of the command:

{
  "tokenId": "086b64ce-oau0-huy7-9876-65d76e84fa36",
  "projectId": "cba41b90-34bg-jhg9-acgf-800baa9a40a0",
  "privateKey": "...",
  "passphrase": "MySecretPassphrase"
}

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_KEY and KEY_ID in env file empty.

Try the Credential Issuance Flow

After successfully setting up the Personal Access Token (PAT) details and running the application, go to the web page at http://localhost:3000 and click the Issue Credentials page. At this point, you should have already been logged in. If you are not yet logged in, click on the Affinidi Login button.

On the Issue Credentials page, you will see your Decentralised Identifier (DID) from the ID Token of Affinidi Login and a dropdown field listing all the available Credential Issuance configurations created on your project.

Credential Issuance - Issue Credentials

Select the Credential Issuance configuration, including the Claim Mode on how the user can claim the credential, whether a TX_CODE that generates a Transaction Code that users must enter to claim the credential or a normal mode without Transaction Code required. Also, select the Schema from the Supported Schema listed on your configuration to create a credential offer.

The page will dynamically render the fields based on the selected Schema to enter the details for the credential.

Credential Issuance - Create Offer

How the Integration Works

After configuring and testing the credential issuance flow from the sample app, let’s review the codes and how they work to enable this flow.

We are using Affinidi TDK and some of its modules to enable this.

  • Auth Provider package to authenticate and authorise the Personal Access Token (PAT) to perform actions on your behalf.

  • Credential Issuance client library to integrate with the Credential Issuance service of Affinidi Elements.

Read more about Affinidi TDK here.

Handle Credential Data Submission

The frontend implementation of the Credential Issuance calls a client library that triggers credential issuance based on the selected configuration, claim mode, and the Schema. The credential data is based on the list of fields of the selected Schema from the list of supported schemas.

Source Path: src/pages/credential-issuance.tsx


const handleSubmit = async (credentialData: any) => {
    console.log("credentialData:", credentialData);
    if (
      !holderDid &&
      claimMode == StartIssuanceInputClaimModeEnum.FixedHolder
    ) {
      setMessage({
        message: "Holder DID is required in FIXED_DID claim mode",
        type: "error",
      });
      return;
    }
    setIsFormDisabled(true);
    const response = await fetch("/api/issuance/start", {
      method: "POST",
      body: JSON.stringify({
        holderDid,
        credentialData,
        credentialTypeId: selectedTypeId,
        claimMode,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    });
    if (!response.ok) {
      clearIssuance();
      setMessage({
        message: "Error creating offer",
        type: "error",
      });
      return;
    }
    let dataResponse = await response.json();

    if (dataResponse.credentialOfferUri) {
      setOffer(dataResponse);
    }
    console.log("Offer", offer);
};

Prepare Credential Issuance Payload

A backend implementation of the Credential Issuance flow exposes an API endpoint that takes the selected configurations, including the credential data provided from the front end and prepares the payload. This endpoint verifies if an active user session was created from the Affinidi Login authentication flow.

The user session contains the Decentralised Identifier (DID) of the logged-in user and is added as part of the payload to set the recipient of the credential offer.

Source Path: src/pages/api/issuance/start.ts


try {
    const { credentialTypeId, credentialData, claimMode, holderDid } =
      issuanceStartSchema.parse(req.body);

    const apiData: StartIssuanceInput = {
      claimMode,
      ...(holderDid && { holderDid }),
      data: [
        {
          credentialTypeId,
          credentialData: {
            ...credentialData,
            // Add any additional data here
          },
        },
      ],
    };

    const issuanceResult = await startIssuance(apiData);
    res.status(200).json(issuanceResult);
} catch (error: any) {
    res.status(500).json({ message: "Unable to start issuance" });
    console.log(error);
}
  

Create a Credential Offer

After preparing the payload to create a credential offer, the endpoint from the previous section calls the Credential Issuance client to trigger the issuance flow and create a credential offer. In this client file, we have created an async function that generates an authorisation token through the AuthProvider package and initialises the Credential Issuance client of the Affinidi TDK.

Source Path: src/lib/clients/credential-issuance.ts


export async function startIssuance(apiData: StartIssuanceInput) {
    const authProvider = getAuthProvider();
    const api = new IssuanceApi(
        new Configuration({
        apiKey: authProvider.fetchProjectScopedToken.bind(authProvider),
        }),
    );
    const { data } = await api.startIssuance(projectId, apiData);
    return data;
}

The api.startIssuance will return the credential offer details in JSON format. This contains the credentialOfferUri, the txCode if you selected TX_CODE as claim mode, and the credential offer’s lifetime.


{
  "credentialOfferUri": "https://<PROJECT_ID>.apse1.issuance.affinidi.io/offer/3da42adc-5412-413b-986c-99c43cb87eba",
  "txCode": "123456",
  "issuanceId": "3da42adc-5412-413b-986c-99c43cb87eba"
  "expiresIn": 3600
}

The Affinidi Vault user must claim the credential offer before it expires; otherwise, you will have to create another credential offer.

After creating the credential offer, you can send the claim link via email or QR code to allow the recipient of the credential offer to claim the credential and store it on their Affinidi Vault. The claim link will be in the following format with the URL encoded <CREDENTIAL_OFFER_URI> value:

https://vault.affinidi.com/claim?credential_offer_uri=<CREDENTIAL_OFFER_URI>

If the Credential Offer was created with the Transaction Code (txCode), the developer must also send it to the user to claim the credentials.

In this example, we have created another component called Offer that generates the claim link using the VaultUtils of Affinidi TDK’s common package.

Source Path: src/components/issuance/Offer.tsx


const vaultLink = VaultUtils.buildClaimLink(offer.credentialOfferUri)
Credential Issuance - Claim Link

Click on the displayed link to open your Affinidi Vault to claim the credential and store it on your selected profile in the Affinidi Vault. Read more about Claimed credentials here.