Issue and verify BBS+ verifiable credentials using ASP.NET Core and trinsic.id

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

History

  • 2023-12-02 Updated .NET 8

Use Case

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 sample creates university diplomas as verifiable credentials with BBS+ signatures.
  • The credentials are issued using OIDC with strong authentication (If possible) to a candidate mobile wallet.
  • The issuer uses a trust registry so that the verifier can validate that the VC is authentic.
  • The verifier uses BBS+ selective disclosure to validate a diploma. (ZKP is not possible at present)
  • The university application requires an admin zone and a student zone.

Setup

  • The university application plays the role of credential issuer and has it’s own users.
  • Trinsic.id wallet is used to store the verifiable credentials for students.
  • Company X HR plays the role of verifier and has no connection to the university. The company has a list of trusted universities. (DIDs)

Issuing a credential

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.

Creating a proof in the wallet

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.

Verifying the proof

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:

Notes

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:

  • JSON JWT
  • SD-JWT (JSON-LD with LD Signatures)
  • AnonCreds with CL Signatures
  • JSON-LD ZKP with BBS+ Signatures
  • mDL ISO ISO/IEC 18013-5

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.

Links

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/

2 comments

  1. […] Issue and verify BBS+ verifiable credentials using ASP.NET Core and trinsic.id (Damien Bowden) […]

  2. […] Issue and verify BBS+ verifiable credentials using ASP.NET Core and trinsic.id – Damien Bowden […]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.