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;
};
};