Skip to main content

Wallet workflow

This page provides a walkthrouth of a simple wallet workflow with the Procivis One SDK.

Process overview

Simple wallet workflow

Prerequisites

The SDK is installed and initialized.

Initialize SDK

Initialize core

import { initializeHolderCore } from '@procivis/react-native-one-core';

const core = await initializeHolderCore();

A. Become an agent

Before the wallet can accept credentials and present proofs, it first needs to become an agent:

Create an organizationGenerate a keyCreate a DID = Agency

  1. Create an organization

The fundamental unit of agency in the Procivis One Core is the organization: all entities created and actions taken belong to only one organization. Though the system supports the creation of as many organizations as is needed, the typical digital credentials wallet requires only one organization as it will only act as a single agent.

const organisationId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';

await core.createOrganisation(organisationId);
  1. Generate a key

The goal is to create a decentralized identifier (DID) for the wallet to use in interactions with issuers and verifiers, and DIDs rely on asymmetric encryption. Generating a key creates a public/private key pair that will later be associated with a DID and used for authentication of the DID subject.

Only one key pair needs to be generated.

const keyId = await core.generateKey({
keyParams: {},
keyType: 'ES256',
name: 'holder-key-hw',
organisationId: organisationId,
storageParams: {},
storageType: 'SECURE_ELEMENT',
});
  1. Create a DID

Create a DID by which the wallet will identify itself as an agent. Choose a DID method and assign the keyId from (2) for all verification methods in the keys object. The system supports the creation of as many DIDs as is needed, but only one DID is needed to start accepting credentials and submitting proofs.

const didId = await core.createDid({
didMethod: 'KEY',
keys: {
assertionMethod: [keyId],
authentication: [keyId],
capabilityDelegation: [keyId],
capabilityInvocation: [keyId],
keyAgreement: [keyId],
},
name: 'holder-did-hw-key',
organisationId: organisationId,
params: {},
});

B. Accept a credential

Now that the wallet is an agent, it can take part in an issuance workflow:

Handle invitationGet credentialAccept the issuance

  1. Handle invitation

Whether responding to an offer of credentials or a request for proof, the interaction starts with handling the invitation. Use the URL from the credential offer, typically encoded as a QR code or a deep link, and the didId value from (3). The interactionId is returned along with the offered credentials.

const invitationUrl = 'https://example.com/invitation' // Get invitation URL from QR code or deep link
const invitation = await core.handleInvitation(invitationUrl, organisationId),
  1. Get credential

Get the credential details so the wallet holder can decide whether to accept the credential or reject it.

const { interactionId, credentialIds } = invitation;
const credentialId = credentialIds[0];
const credential = await core.getCredential(credentialId);
  1. Accept the credential

Use this function to accept the offered credential.

await core.holderAcceptCredential(interactionId, didId, keyId);

Of course, the holder can also reject an offered credential.

await core.holderRejectCredential(interactionId);

C. Submit a proof

Now that the wallet has a credential, it can submit a proof to a verifier:

Handle invitationGet proof requestGet presentation definitionSubmit proof

  1. Handle invitation

Use the URL from the request, typically encoded as a QR code or a deep link, and the didId value from (3). The interactionId is returned along with the proof request.

const invitationUrl = 'https://example.com/invitation' // Get invitation URL from QR code or deep link
const invitation = await core.handleInvitation(invitationUrl, organisationId),
  1. Get proof request

Get the proof request details so the wallet holder can see what is being requested.

const { interactionId, proofId } = invitation;
const proofDetails = await core.getProof(proofId);
  1. Get presentation definition

Pass the proofId as a parameter for this function to filter the wallet, returning credentials which qualify to be submitted to the request.

const presentationDefinition = await core.getPresentationDefinition(proofId);

// refresh revocation status of the applicable credentials
const credentialIds = new Set<string>(
definition.requestGroups.flatMap(({ requestedCredentials }) =>
requestedCredentials.flatMap(
({ applicableCredentials }) => applicableCredentials,
),
),
);
await core.checkRevocation(Array.from(credentialIds));
  1. Submit proof

Use this function to submit the chosen set of credentials to the verifier.

const credentials: Record<
PresentationDefinitionRequestedCredential['id'],
PresentationSubmitCredentialRequest | undefined
> = {};
presentationDefinition.requestGroups.forEach((group) =>
group.requestedCredentials.forEach((credential) => {
const credentialId =
allCredentials.find(
({ id, state }) =>
state === CredentialStateEnum.ACCEPTED &&
credential.applicableCredentials.includes(id),
)?.id ?? credential.applicableCredentials[0];
if (!credentialId) {
credentials[credential.id] = undefined;
return;
}

const requiredClaims = credential.fields
.filter((field) => field.required)
.map((field) => field.id);
credentials[credential.id] = {
credentialId,
submitClaims: requiredClaims,
};
}),
);
await core.holderSubmitProof(interactionId, credentials, didId, keyId);

Of course, the holder can also reject the proof request.

await core.holderRejectProof(interactionId);