Credential Service¶
The Credential Service exposes functionality for issuance, proof generation, verification, and revocation of Verifiable Credentials.
Signature Format
The Credential service currently supports BBS+ Signatures , which enable selective disclosure of credential fields during proof generation.
Credentials are signed, and proofs are created, using a key pair unique to the signing / holding wallet. This key pair is created and managed by Trinsic upon account creation.
Issue Credential from Credential Template¶
Issues a credential from a previously-defined credential template.
import { TrinsicService } from "@trinsic/trinsic";
const trinsic = new TrinsicService({ authToken: "<auth token>" });
const request = {
templateId: "https://schema.trinsic.cloud/default/example-credential",
include_governance: true,
valuesJson: JSON.stringify({
"name": "John Doe",
"email": "john.doe@example.com"
})
};
const response = await trinsic.credential().issueFromTemplate(request);
var credentialJson = await trinsic.Credential.IssueFromTemplateAsync(new() {
TemplateId = templateId,
ValuesJson = values
});
values = json.dumps({"firstName": "Jane", "lastName": "Doe", "age": "42"})
issue_response = await trinsic_service.credential.issue_from_template(
request=IssueFromTemplateRequest(
template_id=template.data.id, values_json=values
)
)
issueTemplateResponse, err := trinsic.Credential().IssueFromTemplate(context.Background(),
&credential.IssueFromTemplateRequest{
ValuesJson: valuesJson,
TemplateId: templateId,
})
var valuesMap = new HashMap<String, Object>();
valuesMap.put("firstName", "Jane");
valuesMap.put("lastName", "Doe");
valuesMap.put("age", 42);
var valuesJson = new Gson().toJson(valuesMap);
var issueResponse =
trinsic
.credential()
.issueFromTemplate(
IssueFromTemplateRequest.newBuilder()
.setTemplateId(templateId)
.setValuesJson(valuesJson)
.build())
.get();
template_id
EXPERIMENTAL
.IssueFromTemplateRequest
IssueFromTemplateRequest
Add Trust Registry information to issued credential¶
In order to attach governance information to the credential, issuers must request this explicitly by specifying the parameter
include_governance
to true
in the above request. This will reference the ecosystem's Trust Registry that the issuer is authorized
to issue credentials of the designated type (schema).
When this parameter is set to true
, the issued credential will contain extended information in the issuer
field to assert authorization in
the given Trust Registry. The registry identifier will be in the issuer.trustRegistry
field.
Here's an example of a VC with extended issuer information:
"issuer": {
"id": "did:web:ecosystemid.connect.trinsic.cloud:z5TcEFAQPu7RkrBCMCJDGgVziV",
"type": "AuthoritativeMember"
"governanceFramework": "https://acme.org/authorized-issuers",
"trustRegistry": "urn:egf:acme:92f21b4cb3bc48dd8bb19a872f03afca",
}
See Trust Registry Service for more information on managing your ecosystem's governance.
Create Credential Offer¶
The purpose of the "Create Offer" endpoint is to initiate interactive issuance and facilitate the issuance of a verifiable credential. This endpoint serves as the starting point for the interactive issuance process, allowing the issuing party to generate an offer to issue a credential to a specific user or entity.
By utilizing the "Create Offer" endpoint, the issuing party can initiate a request for the user's consent to issue a verifiable credential on their behalf. This interactive process ensures that the user actively participates in the credential issuance and has the opportunity to review and provide their consent before the credential is issued.
Furthermore, the "Create Offer" endpoint enables the issuing party to define and configure the holder binding properties of the credential. This means that the issued VC will be bound to the intended holder, making it non-transferable and restricted for use solely by the authorized holder. When holder_binding
is set to true, upon acceptance of the credential offer, the resulting VC will have credentialSubject.id
set to the holder's wallet DID. Otherwise, it defaults to a static value urn:vc:subject:0
. During proof creation, if the VC includes a holder DID within the credentialSubject
field, the resulting proof will also include a proof of ownership associated with the holder DID. This serves to authenticate to the verifier that the presenter of the proof indeed possesses the DID designated by the issuer within the credentialSubject
field of the credential.
let createCredentialOfferResponse = await trinsic.credential().createCredentialOffer(
CreateCredentialOfferRequest.fromPartial({
templateId: templateId,
valuesJson: issueResponse.documentJson,
holderBinding: true,
includeGovernance: true,
generateShareUrl: true,
signatureType: SignatureType.EXPERIMENTAL,
})
);
template_id
EXPERIMENTAL
.Accept Credential¶
The purpose of the "Accept Offer" method is for the user to formally accept the offered verifiable credential. It enables the user to provide explicit consent and indicate their willingness to receive and possess the credential. By using this method, the user confirms their agreement to become the rightful holder of the credential.
The "Accept Offer" method is a mandatory step in the issuance process, as it solidifies the user's commitment to acquiring the verifiable credential. During this step, if the issuer requested binding information, the user's wallet will submit a proof of DID ownership bind the credential to the user and make it non-transferable. The verifiable credential is then securely generated and delivered to the user, officially establishing their possession and ownership of the credential.
let acceptCredentialResponse = await trinsic.credential().acceptCredential(
AcceptCredentialRequest.fromPartial({
documentJson: createCredentialOfferResponse.documentJson,
})
);
Reject Credential¶
This endpoint allows users to decline or reject an offered verifiable credential. By utilizing this endpoint, users can explicitly communicate their decision not to accept the credential being offered to them. By offering this option in your application, your platform promotes user empowerment and supports a transparent workflow for the acceptance or rejection of verifiable credentials. Â Â
let rejectCredentialResponse = await trinsic.credential().rejectCredential(
RejectCredentialRequest.fromPartial({
documentJson: createCredentialOfferResponse.documentJson,
})
);
Check Revocation Status¶
Checks a credential's revocation status by its credential_status_id
.
A credential_status_id
can be found in a credential's credentialStatus.id
field, if present.
trinsic vc get-status --credential-status-id <ID>
let checkStatusResponse = await trinsic
.credential()
.checkStatus(CheckStatusRequest.fromPartial({}));
var checkResponse = await trinsic.Credential.CheckStatusAsync(new() { CredentialStatusId = "" });
# check_response = await trinsic_service.credential.check_status(
# request=CheckStatusRequest(credential_status_id="")
# )
status, err := trinsic.Credential().CheckStatus(context.Background(), &credential.CheckStatusRequest{CredentialStatusId: ""})
var checkStatusResponse =
trinsic.credential().checkStatus(CheckStatusRequest.newBuilder().build()).get();
Update Revocation Status¶
Updates the revocation status of a credential (revoke or unrevoke).
A credential_status_id
can be found in a credential's credentialStatus.id
field, if present.
# Revoke a credential
trinsic vc update-status --revoked --credential-status-id <ID>
# Unrevoke a credential
trinsic vc update-status --unrevoked --credential-status-id <ID>
let updateStatusResponse = await trinsic
.credential()
.updateStatus(UpdateStatusRequest.fromPartial({}));
await trinsic.Credential.UpdateStatusAsync(new() { CredentialStatusId = "", Revoked = true });
# update_response = await trinsic_service.credential.update_status(
# request=UpdateStatusRequest(credential_status_id="", revoked=True)
# )
updateStatusResponse, err := trinsic.Credential().UpdateStatus(context.Background(), &credential.UpdateStatusRequest{CredentialStatusId: "", Revoked: true})
trinsic.credential().updateStatus(UpdateStatusRequest.newBuilder().build());
UpdateStatusRequest
Create Proof¶
Creates and signs a proof for a valid JSON-LD credential, using the BBS+ Signature Suite.
If the credential is stored in a Trinsic cloud wallet, pass its item_id
; otherwise, pass the raw JSON-LD credential via document_json
.
Selective Disclosure
BBS+ Signatures support the ability to generate a proof for a subset of a credential's fields, instead of every field.
This enables increased user privacy: fields which aren't included in reveal_document_json
will not be present in the generated proof.
- If
reveal_document_json
is passed, a proof will be generated for only the fields specified. This is a JSON-LD frame. - Rather than formulating a complete JSON-LD frame, you can instead provide a list of proof attributes to reveal via
reveal_template.template_attributes
, and the service will construct the JSON-LD proof frame internally - If neither is provided, the entire proof will be returned.
trinsic vc create-proof --document-id <STRING> --out <OUTPUT_FILE> --reveal-document <JSONLD_FRAME_FILE>
let proof = await trinsic.credential().createProof(
CreateProofRequest.fromPartial({
documentJson: issueResponse.documentJson,
itemId: insertItemResponse.itemId,
}),
);
let selectiveProof = await trinsic.credential().createProof(
CreateProofRequest.fromPartial({
documentJson: issueResponse.documentJson,
itemId: insertItemResponse.itemId,
revealTemplate: {
templateAttributes: ["firstName", "lastName"],
},
}),
);
var proof = await trinsic.Credential.CreateProofAsync(new() {
DocumentJson = credentialJson.DocumentJson,
RevealDocumentJson = frame.ToString(Formatting.None)
});
var selectiveProof = await trinsic.Credential.CreateProofAsync(new() {
DocumentJson = credentialJson.DocumentJson,
RevealTemplate = new() {
// The other field, not disclosed, is "age"
TemplateAttributes = { "firstName", "lastName" }
}
});
proof_response = await trinsic_service.credential.create_proof(
request=CreateProofRequest(document_json=credential_json)
)
selective_proof_response = await trinsic_service.credential.create_proof(
request=CreateProofRequest(
document_json=credential_json,
reveal_template=RevealTemplateAttributes(template_attributes=["firstName"]),
)
)
request := &credential.CreateProofRequest{
Proof: &credential.CreateProofRequest_DocumentJson{
DocumentJson: credentialJson,
},
}
proofResponse, err := trinsic.Credential().CreateProof(context.Background(), request)
selectiveRequest := &credential.CreateProofRequest{
Proof: &credential.CreateProofRequest_DocumentJson{
DocumentJson: credentialJson,
},
Disclosure: &credential.CreateProofRequest_RevealTemplate{RevealTemplate: &credential.RevealTemplateAttributes{TemplateAttributes: []string{"name"}}},
}
selectiveResponse, err2 := trinsic.Credential().CreateProof(context.Background(), selectiveRequest)
var createProofResponse =
trinsic
.credential()
.createProof(
CreateProofRequest.newBuilder().setDocumentJson(signedCredentialJson).build())
.get();
var credentialProof = createProofResponse.getProofDocumentJson();
var selectiveProofResponse =
trinsic
.credential()
.createProof(
CreateProofRequest.newBuilder()
.setDocumentJson(signedCredentialJson)
.setRevealTemplate(
RevealTemplateAttributes.newBuilder()
.addTemplateAttributes("batchNumber")
.build())
.build())
.get();
var selectiveProof = selectiveProofResponse.getProofDocumentJson();
item_id
, or document_json
may be provided, not both.CreateProofRequest
Verify Proof¶
Verifies a proof for validity and authenticity. Only supports BBS+ Signatures at present.
# The JSONLD_FILE refers to the proof document obtained from a CreateProofResponse
trinsic vc issuer verify-proof --proof-document <JSONLD_FILE>
let verifyResponse = await trinsic.credential().verifyProof({
proofDocumentJson: proof.proofDocumentJson,
});
let selectiveVerifyResponse = await trinsic.credential().verifyProof({
proofDocumentJson: selectiveProof.proofDocumentJson,
});
var valid = await trinsic.Credential.VerifyProofAsync(new() { ProofDocumentJson = proof.ProofDocumentJson });
verify_result = await trinsic_service.credential.verify_proof(
request=VerifyProofRequest(proof_document_json=credential_proof)
)
selective_verify_result = await trinsic_service.credential.verify_proof(
request=VerifyProofRequest(
proof_document_json=selective_proof_response.proof_document_json
)
)
verifyResponse, err := trinsic.Credential().VerifyProof(context.Background(), &credential.VerifyProofRequest{
ProofDocumentJson: proofJson,
})
var verifyProofResponse =
trinsic
.credential()
.verifyProof(
VerifyProofRequest.newBuilder().setProofDocumentJson(credentialProof).build())
.get();
boolean isValid = verifyProofResponse.getIsValid();
VerifyProofRequest
validation_results
passedValidation Results
The verification process performs a number of validations, each of which may fail independently of the others.
For example, a credential may be expired or revoked, but otherwise perfectly valid.
validation_results
contains an entry for each of the following verification steps:
Name | Description |
---|---|
SignatureVerification |
The cryptographic proof over the entire Verifiable Credential, specifically using BBS+ Proof of Signature |
CredentialStatus |
(if supported by credential) Checks if credential has been revoked |
SchemaConformance |
Ensures credential conforms with its schema. It is possible to issue a credential omitting a required field (as captured in the credential template). If your use case allows this kind of omission, you can ignore this validation entry. |
TrustRegistryMembership |
(if relevant) Verifies that credential issuer is an authorized member of the credential's governing Trust Registry |
IssuerIsSigner |
Document issuer is same DID as document signer. If false, it is not safe to assume that the claimed issuer actually issued the credential. |
Exchange Credentials¶
Send via Email¶
Sends a credential to a user via email.
The specified email address must be tied to an existing account in the same ecosystem.
trinsic vc send --email <EMAIL_ADDRESS> --item <FILE>
await trinsic.credential().send(SendRequest.fromPartial({
documentJson: issueResponse.documentJson,
email: "<EMAIL>",
sendNotification: true,
}));
var sendResponse = await trinsic.Credential.SendAsync(new() {
Email = "<EMAIL>",
DocumentJson = issueResponse.DocumentJson,
SendNotification = true,
});
send_response = await trinsic_service.credential.send(
request=SendRequest(
document_json=credential_json, email="example@trinsic.id"
)
)
sendResponse, err := trinsic.Credential().Send(context.Background(), &credential.SendRequest{
DeliveryMethod: &credential.SendRequest_Email{
Email: "example@trinsic.id",
},
DocumentJson: credentialJson,
})
trinsic
.credential()
.send(
SendRequest.newBuilder()
.setDocumentJson(signedCredentialJson)
.setEmail(recipientEmail)
.build());
SendRequest
Alternative Exchange Protocols
Credentials may only be sent to a wallet via email address or with the InsertItem call.
There are a number of ongoing industry efforts to standardize exchange protocols, such as:
We aim to provide support for these methods as they mature.