This article shows how to implement identity verification in a solution using ASP.NET Core and trinsic.id, built using an id-tech solution based on self sovereign identity principals. The credential issuer uses OpenID Connect to authenticate, implemented using Microsoft Entra ID. The edge or web wallet authenticates using trinsic.id based on a single factor email code. The verifier needs no authentication, it only verifies that the verifiable credential is authentic and valid. The verifiable credentials uses JSON-LD ZKP with BBS+ Signatures and selective disclosure to verify.
Code: https://github.com/swiss-ssi-group/TrinsicV2AspNetCore
As a university administrator, I want to create BBS+ verifiable credentials templates for university diplomas.
As a student, I want to authenticate using my university account (OpenID Connect Code flow with PKCE), create my verifiable credential and download this credential to my SSI Wallet.
As an HR employee, I want to verify the job candidate has a degree from this university. The HR employee needs verification but does not require to see the data.
The ASP.NET Core issuer application can create a new issuer web (edge) wallet, create templates and issue credentials using the template. The trinsic.id .NET SDK is used to implement the SSI or id-tech integration. How and where trinsic.id store the data is unknown, and you must trust them to do this correctly if you use their solution. Implementing your own ledger or identity layer is at present too much effort and so the best option is to use one of the integration solutions. The UI is implemented using ASP.NET Core Razor pages and the Microsoft.Identity.Web Nuget package with Microsoft Entra ID for the authentication. The required issuer template is used to create the credential offer.
var diploma = await _universityServices.GetUniversityDiploma(DiplomaTemplateId);
var tid = Convert.ToInt32(DiplomaTemplateId, CultureInfo.InvariantCulture);
var response = await _universityServices
.IssuerStudentDiplomaCredentialOffer(diploma, tid);
CredentialOfferUrl = response!.ShareUrl;
The IssuerStudentDiplomaCredentialOffer method uses the University eco system and issuer a new offer using it’s template.
public async Task<CreateCredentialOfferResponse?> IssuerStudentDiplomaCredentialOffer
(Diploma diploma, int universityDiplomaTemplateId)
{
var templateId = await
GetUniversityDiplomaTemplateId(universityDiplomaTemplateId);
// get the template from the id-tech solution
var templateResponse = await GetUniversityDiplomaTemplate(templateId!);
// Auth token from University issuer wallet
_trinsicService.Options.AuthToken =
_configuration["TrinsicOptions:IssuerAuthToken"];
var response = await _trinsicService
.Credential.CreateCredentialOfferAsync(
new CreateCredentialOfferRequest
{
TemplateId = templateResponse.Template.Id,
ValuesJson = JsonSerializer.Serialize(diploma),
GenerateShareUrl = true
});
return response;
}
The UI returns the offer and displays this as a QR code which can be opened and starts to process to add the credential to the web wallet. This is a magic link and not an SSI OpenID for Verifiable Credential Issuance flow or the starting point for a Didcomm V2 flow. Starting any flow using a QR Code is unsafe and further protection is required. Using an authenticated application with a phishing resistant authentication reduces this risk or using OpenID for Verifiable Credential Issuance VC issuing.
Once the credential is in the holders wallet, a proof can be created from it. The proof can be used in a verifier application. The wallet authentication of the user is implemented using a single factor email code and creates a selective proof which can be copied to the verifier web application. OpenID for Verifiable Presentations for verifiers cannot be used because there is no connection between the issuer and the verifier. This could be possible if the process was delegated to the wallet using some type of magic URL, string etc. The wallet can authenticate against any known eco systems.
public class GenerateProofService
{
private readonly TrinsicService _trinsicService;
private readonly IConfiguration _configuration;
public List<SelectListItem> Universities = new();
public GenerateProofService(TrinsicService trinsicService, IConfiguration configuration)
{
_trinsicService = trinsicService;
_configuration = configuration;
Universities = _configuration.GetSection("Universities")!.Get<List<SelectListItem>>()!;
}
public async Task<List<SelectListItem>> GetItemsInWallet(string userAuthToken)
{
var results = new List<SelectListItem>();
// Auth token from user
_trinsicService.Options.AuthToken = userAuthToken;
// get all items
var items = await _trinsicService.Wallet.SearchWalletAsync(new SearchRequest());
foreach (var item in items.Items)
{
var jsonObject = JsonNode.Parse(item)!;
var id = jsonObject["id"];
var vcArray = jsonObject["data"]!["type"];
var vc = string.Empty;
foreach (var i in vcArray!.AsArray())
{
var val = i!.ToString();
if (val != "VerifiableCredential")
{
vc = val!.ToString();
break;
}
}
results.Add(new SelectListItem(vc, id!.ToString()));
}
return results;
}
public async Task<CreateProofResponse> CreateProof(string userAuthToken, string credentialItemId)
{
// Auth token from user
_trinsicService.Options.AuthToken = userAuthToken;
var selectiveProof = await _trinsicService.Credential.CreateProofAsync(new()
{
ItemId = credentialItemId,
RevealTemplate = new()
{
TemplateAttributes = { "firstName", "lastName", "dateOfBirth", "diplomaTitle" }
}
});
return selectiveProof;
}
public AuthenticateInitResponse AuthenticateInit(string userId, string universityEcosystemId)
{
var requestInit = new AuthenticateInitRequest
{
Identity = userId,
Provider = IdentityProvider.Email,
EcosystemId = universityEcosystemId
};
var authenticateInitResponse = _trinsicService.Wallet.AuthenticateInit(requestInit);
return authenticateInitResponse;
}
public AuthenticateConfirmResponse AuthenticateConfirm(string code, string challenge)
{
var requestConfirm = new AuthenticateConfirmRequest
{
Challenge = challenge,
Response = code
};
var authenticateConfirmResponse = _trinsicService.Wallet.AuthenticateConfirm(requestConfirm);
return authenticateConfirmResponse;
}
}
The wallet connect screen can look some like this:
The proof can be created using one of the credentials and the proof can be copied.
The verifier can use the proof to validate the required information. The verifier has no connection to the issuer, it is in a different eco system. Because of this, the verifier must validate the issuer DID. This must be a trusted issuer (university).
public class DiplomaVerifyService
{
private readonly TrinsicService _trinsicService;
private readonly IConfiguration _configuration;
public List<SelectListItem> TrustedUniversities = new();
public List<SelectListItem> TrustedCredentials = new();
public DiplomaVerifyService(TrinsicService trinsicService, IConfiguration configuration)
{
_trinsicService = trinsicService;
_configuration = configuration;
TrustedUniversities = _configuration.GetSection("TrustedUniversities")!.Get<List<SelectListItem>>()!;
TrustedCredentials = _configuration.GetSection("TrustedCredentials")!.Get<List<SelectListItem>>()!;
}
public async Task<(VerifyProofResponse? Proof, bool IsValid)> Verify(string studentProof, string universityIssuer)
{
// Verifiers auth token
// Auth token from trinsic.id root API KEY provider
_trinsicService.Options.AuthToken = _configuration["TrinsicCompanyXHumanResourcesOptions:ApiKey"];
var verifyProofResponse = await _trinsicService.Credential.VerifyProofAsync(new VerifyProofRequest
{
ProofDocumentJson = studentProof,
});
var jsonObject = JsonNode.Parse(studentProof)!;
var issuer = jsonObject["issuer"];
// check issuer
if (universityIssuer != issuer!.ToString())
{
return (null, false);
}
return (verifyProofResponse, true);
}
}
The ASP.NET Core UI could look something like this:
Using a SSI based solution to share data securely across domains or eco systems can be very useful and opens up many business possibilities. SSI or id-tech is a good solution for identity checks and credential checks, it is not a good solution for authentication. Phishing is hard to solve in cross device environments. Passkeys or FIDO2 is the way forward for user authentication. The biggest problem for SSI and id-tech is the interoperability between solutions. For example, the following credential types exist and are only useable on specific solutions:
There are multiple standards, multiple solutions, multiple networks and multiple ledgers. No two systems seem to work with each other. In a closed eco system, it will work, but SSI has few advantages over existing solutions in a closed eco system. Interoperability needs to be solved.
https://dashboard.trinsic.id/ecosystem
https://github.com/trinsic-id/sdk
https://docs.trinsic.id/dotnet/
Integrating Verifiable Credentials in 2023 – Trinsic Platform Walkthrough
https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
https://openid.net/specs/openid-connect-self-issued-v2-1_0.html
https://datatracker.ietf.org/doc/draft-ietf-oauth-selective-disclosure-jwt/
Published 590 days ago
Login to Continue, We will bring you back to this content 0