Digital ID V2 SDK Migration
This acts as a short quick start guide for migrating from the original Digital ID SDK, to the new 'V2' SDK.
Scenarios created via the Hub are no longer compatible with the newly launched Digital ID SDK. To offer an improved user experience, we've transitioned to 'Sessions' which can provide granular session states throughout the entire user journey.
Should you require support on migrating from a scenario to the V2 SDK, or general support during the migration process, please get in touch through our support website.
Install the SDK
You will need to ensure the latest version of the Yoti backend SDK is installed.
// Get the Yoti Node SDK library via the NPM registry
npm install -S -E yoti
Client Initialisation
The Class in the V1 client is referred to as Client.
const Yoti = require('yoti')
const CLIENT_SDK_ID = 'YOTI_CLIENT_SDK_ID'
const PEM_PATH = 'YOTI_KEY_FILE_PATH'
const PEM_KEY = fs.readFileSync(PEM_PATH)
// For SDK version < 3
const yotiClient = new Yoti(CLIENT_SDK_ID, PEM)
// For SDK version >= 3
const yotiClient = new Yoti.Client(CLIENT_SDK_ID, PEM_KEY)
This has now been renamed to the Digital Identity Client. Your current PEM Key and SDK ID are still valid. There's no obligation to generate a new set of keys, but you might consider doing so if you wish to segregate different instances.
const Yoti = require('yoti')
const CLIENT_SDK_ID = 'YOTI_CLIENT_SDK_ID'
const PEM_PATH = 'YOTI_KEY_FILE_PATH'
const PEM_KEY = fs.readFileSync(PEM_PATH)
// For SDK version >= 3
const yotiClient = new Yoti.DigitalIdentityClient(CLIENT_SDK_ID, PEM_KEY)
Dynamic QR
The current SDK enables integrators to dynamically create policies, which are sets of requested attributes or schemes, during the sharing process. This offers a more flexible approach compared to having a static policy pre-configured in the Yoti Hub, or attempting to maintain multiple scenarios per policy.
Creating a policy
A policy is used to define what attributes are requested from the user. You can use the builder to define what attributes are needed.
In the V1 API, you would create a dynamic policy like this:
const requirements = {
trust_framework: 'UK_TFIDA',
scheme: {
type: 'DBS_RTW',
objective: 'ENHANCED',
}
};
const dynamicPolicy = new Yoti.DynamicPolicyBuilder()
// if you wish to carry out DBS and/or RTW check
.withIdentityProfileRequirements(requirements)
//using a predefined method to add an attribute
.withFullName()
.withEmail()
//if you wish the user to prove it's them by taking a selfie on share
.withSelfieAuthentication()
.build();
In the V2 API, you have to create a policy like this:
const requirements = {
trust_framework: 'UK_TFIDA',
scheme: {
type: 'DBS_RTW',
objective: 'ENHANCED',
}
};
const policy = new Yoti.PolicyBuilder()
// if you wish to carry out DBS and/or RTW check
.withIdentityProfileRequirements(requirements)
//using a predefined method to add an attribute
.withFullName()
.withEmail()
//if you wish the user to prove it's them by taking a selfie on share
.withSelfieAuthentication()
.build();
Specify a Scenario/Session request
The Scenario/Session request is built using:
- The policy.
- A callback/redirect URL for share completion. The provided URL is where the user will be redirected to after the share is completed. A token or receiptId will be added to the URL. You can use this to retrieve the user profile from the share.
- Any extensions.
In the V1 API, you would specify a scenario:
const subject = {
subject_id: 'subject_id_string',
};
const dynamicScenario = new Yoti.DynamicScenarioBuilder()
.withCallbackEndpoint("/your-callback")
.withPolicy(dynamicPolicy)
.withSubject(subject)
.build();
In the V2 API, you have to specify a session configuration instead of a scenario. It also introduces the capability to subscribe to webhook notifications:
const subject = {
subject_id: 'some_subject_id_string',
};
const notification = new Yoti.ShareSessionNotificationBuilder()
.withUrl("notification-webhook")
.withMethod("POST")
.build();
const shareSessionConfig = new Yoti.ShareSessionConfigurationBuilder()
.withRedirectUri("/your-callback")
.withPolicy(policy)
.withSubject(subject)
.withNotification(notification)
.build();
Create a Share URL/Session
Using the scenario/session request you need to get create a Share URL/Session which will be used by the Yoti scripts to generate a Yoti QR.
In the V1 API, you would create a Share URL like this:
yotiClient.createShareUrl(dynamicScenario)
.then((shareUrlResult) => {
const shareURL = shareUrlResult.getShareUrl();
}).catch((error) => {
console.error(error.message);
});
In the V2 API, you have to create a session instead of a Share URL like this:
yotiClient.createShareSession(shareSessionConfig)
.then((shareSessionResult) => {
const shareSession = shareSessionResult;
const shareSessionId = shareSession.getId();
}).catch((error) => {
console.error(error.message);
});
Client Side View
Yoti provides a client-side script that you can include in your html file to display a Yoti button or QR code.
In the V1 API, you would display a modal QR button using a Share URL:
<head>
<script src="https://www.yoti.com/share/client/"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
window.Yoti.Share.init({
elements: [
{
domId: "xxx",
shareUrl: "shareURL",
clientSdkId: "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
displayLearnMoreLink: true,
}
]
});
</script>
</body>
In the V2 API, you will display the same modal QR button using a Session ID:
<head>
<script src="https://www.yoti.com/share/client/v2"></script>
</head>
<body>
<!-- Yoti element will be rendered inside this DOM node -->
<div id="xxx"></div>
<!-- This script snippet will also be required in your HTML body -->
<script>
async function onSessionIdResolver() {
// Make a call to your backend, and return a 'sessionId'
const response = await fetch('http://localhost:3000/sessions', { method: 'POST' });
const data = await response.json();
return data.sessionId;
}
function onErrorListener(data) {
console.warn('onErrorListener:', data);
}
const loadYoti = async () => {
const { Yoti } = window;
if (Yoti) {
console.info('Waiting for Yoti...');
await Yoti.ready()
console.info('Yoti is now ready');
} else {
console.error('Yoti client was not found!');
}
}
const createYotiWebShare = async () => {
const { Yoti } = window;
if (Yoti) {
await Yoti.createWebShare({
name: 'test',
domId: 'xxx',
sdkId: 'xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
hooks: {
sessionIdResolver: onSessionIdResolver,
errorListener: onErrorListener
}
})
}
}
const start = async () => {
await loadYoti();
await createYotiWebShare();
}
start().catch((e) => console.error(`Could not create Yoti WebShare: `, e));
</script>
</body>
Retrieve Profile
This step involves using a one-time-use token (v1) or a receipt id (v2), and decrypting it to get a user profile.
In the V1 API, you would retrieve a user profile with a token:
yotiClient.getActivityDetails(oneTimeUseToken)
.then((activityDetails) => {
const rememberMeId = activityDetails.getRememberMeId();
const profile = activityDetails.getProfile();
const selfieImageData = profile.getSelfie().getValue();
const base64SelfieUri = profile.getSelfie().getValue().getBase64Content();
const fullName = profile.getFullName().getValue();
const familyName = profile.getFamilyName().getValue();
const givenNames = profile.getGivenNames().getValue();
const phoneNumber = profile.getPhoneNumber().getValue();
const emailAddress = profile.getEmailAddress().getValue();
const dateOfBirth = profile.getDateOfBirth().getValue();
const postalAddress = profile.getPostalAddress().getValue();
const structuredPostalAddress = profile.getStructuredPostalAddress().getValue();
const gender = profile.getGender().getValue();
const nationality = profile.getNationality().getValue();
const ageVerifications = profile.getAgeVerifications();
const ageVerified = profile.findAgeVerification('age_over:', 18).getValue(); // or 'age_under:'
const documentDetails = profile.getDocumentDetails().getValue();
})
In the V2 API, you will use the receipt id to retrieve a user profile:
yotiClient.getShareReceipt(receiptId)
.then((shareReceipt) => {
const sessionId = shareReceipt.getSessionId();
const rememberMeId = shareReceipt.getRememberMeId();
const parentRememberMeId = shareReceipt.getParentRememberMeId();
const profile = shareReceipt.getProfile();
const fullName = profile.getFullName().value;
const dateOfBirth = profile.getDateOfBirth().getValue();
const gender = profile.getGender().getValue();
const nationality = profile.getNationality().getValue();
const postalAddress = profile.getPostalAddress().getValue();
const emailAddress = profile.getEmailAddress().getValue();
const phoneNumber = profile.getPhoneNumber().getValue();
const selfieImageData = profile.getSelfie().getValue();
const base64SelfieUri = selfieImageData.getBase64Content();
const documentDetails = profile.getDocumentDetails().getValue();
})
New Functionality (v2 SDK)
In the Share v2 SDK, we have introduced new functionality to fetch the Session details and to configure Webhook notifications. The SDK includes the following relevant methods for these endpoints.
Fetch a Session
To retrieve a Share v2 session, use the following method:
yotiClient.getShareSession(sessionId)
.then((shareSession) => {
const sessionId = shareSession.getId();
const sessionStatus = shareSession.getStatus();
const sessionExpiry = shareSession.getExpiry();
const qrCodeId = shareSession.getScannedQrCodeId();
const receiptId = shareSession.getReceiptId();
}).catch((error) => {
console.error(error.message);
});
Each session has a status associated with it, that will be one of the following:
Status | Description |
---|---|
CREATED | Session created but no QR Code has been generated. |
QR_CODE_CREATED | At least one QR code has been created for the session and mobile app has not yet "scanned" it. |
QR_CODE_SCANNED | The mobile app has “scanned” the QR Code (i.e. a QR code was locked to a mobile app token) and system is waiting for user to complete (or cancel) the share. |
CANCELLED | Mobile app requested the session to be cancelled (before share was completed). |
EXPIRED | The share associated with the session was never completed (no receipt available). |
COMPLETED | The share associated with the session was completed (with success receipt available). |
FAILED | The share associated with the session was completed (with failure receipt available). |
ERROR | A "catch-all" status for unexpected/unrecoverable errors that might happen during execution (e.g. we get a receipt but the service fails to parse it, required parameter not present). |
Webhook notifications
Briefly described above, you can configure Webhook notifications for a Share v2 session as following:
const notification = new Yoti.ShareSessionNotificationBuilder()
.withUrl("webhook-url")
.withMethod("POST")
.withHeader("Authorization", "<Bearer_token>") // Optional
.withVerifiedTls(true) // Optional
.build();
Example notifications
The notifications will be sent in the following format:
COMPLETED:
{
"id": "ss.v2.ChZV8h5GZTlLT1JrSzc5QkVMc1F0a9JR3gkyLmxkNS5nYnI=",
"status": "COMPLETED",
"created": "2023-08-01T13:21:06.389Z",
"updated": "2023-08-01T13:22:48.284Z",
"receipt": {
"id": "RPA4ZvOZ/xIRhIeHh9Jw9HNpEK//610kEpCbZmQwNZlZ6H6w41VUlDQWn+4p7Lk"
}
}
FAILED:
{
"id": "ss.v2.ChYwYk5mSG9sdlJSbWNFQ1VJHw4kYlZ3EgkzLmxkNS5nYnI=",
"status": "FAILED",
"created": "2023-08-02T08:33:23.326Z",
"updated": "2023-08-02T08:34:40.885Z",
"receipt": {
"id": "FYWI8nE8GAo8xMBVprPCG78J4TaWsW+prX5DEj9WN0eEROlg7avAP/ZAlJmlP3C1",
"error": "MANDATORY_DOCUMENT_NOT_PROVIDED"
}
}