Verifiable credentials API in Motoko

Hello,

A community member recently shared the verifiable credentials docs and I saw that there was no implementation in motoko. I thought the easiest way to work around this way to use an inter canister call and this is the result of some experimentation in a short amount of time. I observed the VC playground and i do not believe you would be able to replicate the rust implementation because some of the crates used do not exist in motoko. As such a native solution would have to be created. Is this something the community would be interested in? As a motoko maxi this is something I would like to see realised(I am willing to contribute with enough feedback). I also thought the verifiable credentials docs was quite technical and should be modified to be more accessible for developers.

Here is the (very rough) implementation using inter canister calls

I tested it on the playground to make sure the types were compatible but I am getting an error for step 3 and 4 as i do not have a way to directly access the signed id alias that should be provided by internet identity after step 2 in addition to the prepared context bytes from step 3. The latency is high as a result of the inter canister calls with a 6-8s runtime for each function.

actor {
  // Specification of a requested credential.
  type CredentialSpec = {
  credential_type : Text;
  arguments : ?[(Text, ArgumentValue)];
  };
  type ArgumentValue = {
  #Int : Int32;
  #String : Text;
  };
  // Types for ICRC-21 consent message
  type Icrc21ConsentInfo = { 
    consent_message : Text; 
    language : Text 
    };
  type Icrc21ConsentPreferences = { 
    language : Text 
    };
  type Icrc21Error = {
      #GenericError :  { description : Text; 
      error_code : Nat 
      };
      #UnsupportedCanisterCall : Icrc21ErrorInfo;
      #ConsentMessageUnavailable : Icrc21ErrorInfo;
  };
  type Icrc21ErrorInfo = { description : Text };
  type Icrc21VcConsentMessageRequest =  {
      preferences : Icrc21ConsentPreferences;
      credential_spec : CredentialSpec;
  };
  //prepare credential types 
  type PrepareCredentialRequest = {
    signed_id_alias : SignedIdAlias;
    credential_spec : CredentialSpec;
  };
  type SignedIdAlias =  {
      credential_jws : Text;
  };
  type PreparedCredentialData =  { prepared_context : ?[Nat8] };

  // Types for get_credential
  type GetCredentialRequest = {
    signed_id_alias : SignedIdAlias;
    credential_spec : CredentialSpec;
    prepared_context : ?Blob;
  };
  type IssuedCredentialData = { vc_jws : Text };

  type IssueCredentialError = {
    #UnknownSubject : Text;
    #UnauthorizedSubject : Text;
    #InvalidIdAlias : Text;
    #UnsupportedCredentialSpec : Text;
    #SignatureNotFound : Text;
    #Internal : Text;
  };
  type DerivationOriginRequest = {
    frontend_hostname : Text;
  };
  type DerivationOriginData = { origin : Text };
  type DerivationOriginError = {
    #Internal : Text;
    #UnsupportedOrigin : Text;
  };

  let VC: actor {
    derivation_origin: (DerivationOriginRequest) -> async {
    #Ok : DerivationOriginData;
    #Err : DerivationOriginError;
  };
    vc_consent_message : (Icrc21VcConsentMessageRequest) -> async {
    #Ok : Icrc21ConsentInfo;
    #Err : Icrc21Error;
  };
  prepare_credential : (PrepareCredentialRequest) -> async {
    #Ok : PreparedCredentialData;
    #Err : IssueCredentialError;
  };  
  get_credential : query (GetCredentialRequest) -> async {
    #Ok : IssuedCredentialData;
    #Err : IssueCredentialError;
  };
  } = actor "qdiif-2iaaa-aaaap-ahjaq-cai";
  
  //local return types
  type DerivationRT= { #Ok : DerivationOriginData; #Err : DerivationOriginError };
  type ConsentMessageRT = { #Ok : Icrc21ConsentInfo; #Err : Icrc21Error};
  type PrepCredentailRT = { #Ok : PreparedCredentialData; #Err : IssueCredentialError};  
  type GetCredentialRT = { #Ok : IssuedCredentialData; #Err : IssueCredentialError};
  
  public func derivation_origin(req:DerivationOriginRequest ): async DerivationRT{
    
    let result = await VC.derivation_origin(req);
    result;
  };
  public func vc_consent_message(req:Icrc21VcConsentMessageRequest): async ConsentMessageRT{
     let result = await VC.vc_consent_message(req);
    result;
  };
  public func prepare_credential(req:PrepareCredentialRequest): async PrepCredentailRT{
    let result = await VC.prepare_credential(req);
    result;
  };
  public func get_credential(req:GetCredentialRequest): async GetCredentialRT{
    let result = await VC.get_credential(req);
    result;
  };
};


2 Likes