Skip to main content

VC Presentation

Background

Once you have a TrueCredential Verified ID, you can call Entra APIs directly to request presentation of it. To do that, you'll need to use your token that you configured in the Microsoft Setup Page

Prerequisites

  1. Your Entra tenant must be set up with Verified ID
  2. An app registration that will allow you to get a token against the Verified ID service

Please follow the app registration instructions to acquire a token for this next call.

Presentation Request

POST https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/createPresentationRequest
Authorization: Bearer {token}

{
"includeQRCode": true,
"authority": "{{your-did-authority}}",
"callback": {
"url": "{{your-callbackUrl}}",
"state": "{{your-callback-state}}"
},
"registration": {
"clientName": "{{your-app-name}}",
"purpose": "{your-request-purpose}"
},
"requestedCredentials": [
{
"type": "VerifiedIdentity",
"acceptedIssuers": [
"did:web:vc.true-credential.com"
]
}
]
}
Field NameExplaination
your-did-authoritythe did:web: prefixed value for your Entra Tenant
your-app-nameName of your app that will be shown to the user in Microsoft Authenticator during presentation.
your-request-purposePurpose for the request that will be shown to the user in Microsoft Authenticator
your-callback-urlEndpoint of your application or debug function that can consume verified ID lifecycle events
your-callback-statewill be passed back to your application in the lifecycle event payload. Useful for things like correlation ID

If successful, the API will respond with 201 Created and a payload like the following:

{
"requestId": "4bfbeb94-afd2-417a-8845-0c9dc169b1af",
"url": "openid-vc://?request_uri=https://verifiedid.did.msidentity.com/v1.0/tenants/4c60afc2-709d-4546-bce3-e64926a53379/verifiableCredentials/presentationRequests/4bfbeb94-afd2-417a-8845-0c9dc169b1af",
"expiry": 1763764862,
"qrCode": "data:image/png;base64,<snip>"
}

If you're using Postman, you can add the following to the post-response script to use the visualizer to render the QR code for you:

let template = `
<img src='{{img}}'/>
`;
pm.visualizer.set(template, {
img: pm.response.json()["qrCode"]
});

Alternatively, you can enter the value from the url field into your favorite QR code renderer.

Using the presentation request

  1. Scan the QR code using Microsoft Authenticator
  2. You'll be prompted to share your credential
  3. Share your credential

App Integration

To be of any use, an application needs to be notified of the status of the presentation request. This is where callbacks come in. Entra Verified ID will send lifecycle events to the URL that you specify, and can even pass in headers for things like authorization: https://learn.microsoft.com/en-us/entra/verified-id/presentation-request-api#http-request

As an easy sample and for debugging, you can spin up an Azure function (or your other favorite cloud FaaS platform) to view Verified ID callback events. In production, your application would need to expose an endpoint to consume these events, much like a webhook.

Debug callback function code

#r "Newtonsoft.Json"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
try
{
log.LogInformation("******** Incoming Request ********");
log.LogInformation($"Method: {req.Method}");
log.LogInformation($"Path: {req.Path}");
log.LogInformation($"Query String: {req.QueryString}");

// Log headers
//LogRequestHeaders(req, log);

// Log request body
await LogRequestBody(req, log);

log.LogInformation("******** Request Logging Complete ********\n");

return new OkResult();
}
catch (Exception ex)
{
log.LogError(ex, "An error occurred while processing the request.");
return new ObjectResult("An internal error occurred.") { StatusCode = 500 };
}
}

private static async Task LogRequestBody(HttpRequest request, ILogger log)
{
string requestBody = string.Empty;
log.LogInformation($"Body Content Length: {request.ContentLength ?? 0}");
log.LogInformation($"Body Content Can Read: {request.Body.CanRead}");

if ((request.ContentLength ?? 0) > 0 && request.Body.CanRead)
{
using var reader = new StreamReader(request.Body);
requestBody = await reader.ReadToEndAsync();

if (!string.IsNullOrWhiteSpace(requestBody))
{
log.LogInformation("Request Body = " + requestBody);
}
else
{
log.LogWarning("Request body is empty.");
}
}
else
{
log.LogWarning("Request body is not readable or empty (ContentLength = 0).");
}
}

private static void LogRequestHeaders(HttpRequest request, ILogger log)
{
var headerList = request.Headers.ToList();
foreach (var header in headerList)
{
var values = string.Join(", ", header.Value.ToArray());
log.LogInformation($"Header - {header.Key}: {values}");
}

log.LogInformation("******** Header Logging completed ********");
}

Resources