Credentials
The Credentials SDK is built on top of meeting_place_core and provides the credential layer for Meeting Place applications.
It supports:
- Relationship Cards and Verifiable Relationship Credentials (R-Cards and VRCs): Verifiable Credentials for exchanging contact details (name, email, phone, company, and custom fields) as jCard (RFC 7095), exportable to vCard 3.0 (RFC 6350), and for recording mutual relationships between DIDs through a two-step VDIP handshake. See R-Card and VRC Feature. No AWS account needed.
- Human ZKP: Prove you are a real human to a contact without sharing biometric data. See Human Zero Knowledge Proof Feature. Requires an external liveness provider such as AWS Rekognition Face Liveness or Azure Face Liveness Detection for production; the Flutter Reference App includes a synthetic demo that works without one.
Install Dependency
Package: meeting_place_credentials
dart pub add meeting_place_credentialsCheck the latest version on the pub.dev registry or view the source on GitHub.
R-Card and VRC Feature
Relationship Card (R-Card)
An R-Card is a signed W3C Verifiable Credential containing a jCard (RFC 7095) payload. Think of it as a digital business card with verifiable contact and relationship details: name, email, phone, company, and optional custom fields that can be cryptographically verified. R-Cards are exchanged automatically when a DIDComm channel is established and can be resent or shared manually at any time.
Verifiable Relationship Credential (VRC)
A VRC demonstrates that two DIDs have established a verified relationship. It uses a two-step VDIP handshake: the initiating side requests, and the responding side sends a relationship credential back. Both participants receive a signed copy of the final credential.
Human Zero Knowledge Proof Feature
The Human Zero Knowledge Proof (Human ZKP) feature lets one participant prove to another that they are a real human, without sharing personal or biometric data. The proof travels over the secure DIDComm channel and the recipient can validate it without seeing any personal data.
This SDK handles Stage 2. Stage 1 (the liveness check) is handled by an external provider such as AWS Rekognition. Stage 3 (proof generation) is performed by the consuming application using utilities provided by this SDK.
How Human ZKP works
Liveness check
The user performs real-time actions (such as blinking or turning their head) in front of their device camera. The liveness provider analyses the session and scores whether a live person is present.
Owner: External provider (for example AWS Rekognition) · Output: LivenessEvidence
Issue liveness credential
LivenessVcIssuanceService.issue(evidence) signs a W3C Verifiable Credential from the normalised evidence.
Owner: This SDK · Output: Signed LivenessCredential (W3C VC)
Generate proof
The consuming application uses the SDK’s LivenessCredentialSubject and ZKP attachment builders to generate a Groth16 proof from the liveness credential.
Owner: Consuming application (SDK utilities provided) · Output: Groth16 ZKP
Transmit proof
The ZKP is sent over the DIDComm channel with no personal data.
Owner: meeting_place_core transport · Output: ZKP message on channel
Validate proof
The contact’s device validates the proof cryptographically.
Owner: Verifier (contact’s device) · Output: Verified badge shown in UI
Why this is privacy-preserving: The Groth16 proof in Stage 3 is mathematically derived from the liveness credential, but it does not expose the credential contents. The validator learns only that the holder passed a liveness check.
Try it in action: The Flutter Reference App includes a working end-to-end Human ZKP demo. Enable it with ZKP_ENABLED=true and follow the end-to-end demo flow to see all five stages execute on a real device.
For implementation details and setup guides, refer to:
Classes and Methods
MeetingPlaceCredentialsSDK
The MeetingPlaceCredentialsSDK is the main facade for R-Card and VRC exchange flows. It wires protocol handlers, stream managers, and repositories on top of MeetingPlaceCoreSDK and exposes a single injectable service for credential operations.
MeetingPlaceCredentialsSDK (constructor)
Creates an instance of MeetingPlaceCredentialsSDK.
Parameters
coreSDK
MeetingPlaceCoreSDK
RequiredMeetingPlaceCoreSDK instance providing DIDComm transport and VDIP primitives.rCardRepository
RCardRepository
RequiredRCardRepositoryDrift from meeting_place_drift_repository.vrcRepository
VrcRepository
RequiredVrcRepositoryDrift from meeting_place_drift_repository.logger
MeetingPlaceCoreSDKLogger?
OptionalDefaultMeetingPlaceCoreSDKLogger.Example
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
final credentialsSDK = MeetingPlaceCredentialsSDK(
coreSDK: coreSDK,
rCardRepository: RCardRepositoryDrift(database: rCardDb),
vrcRepository: VrcRepositoryDrift(database: vrcDb),
);Streams
receivedRCards
A broadcast Stream<RCard> that emits a verified R-Card whenever one arrives over any channel: either via the DIDComm attachment path (OOB / channel inauguration) or the VDIP issued-credential path (chat-time update). Every emitted card is also automatically persisted via RCardRepository.
Example
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
credentialsSDK.receivedRCards.listen((RCard rCard) {
print('R-Card received from ${rCard.subjectDid}');
});receivedRCardsOnChannel
A broadcast Stream<ChannelRCardEvent> that emits an R-Card and its originating channel for every card received via the connection establishment path (OOB / channel inauguration). VDIP-path R-Cards are not emitted here; use receivedRCards for those.
ChannelRCardEvent.channel exposes permanentChannelDid and otherPartyPermanentChannelDid to correlate the card to its conversation.
Example
credentialsSDK.receivedRCardsOnChannel.listen((ChannelRCardEvent event) {
print('R-Card from channel: ${event.channel.permanentChannelDid}');
print('Contact DID: ${event.rCard.subjectDid}');
});receivedVrcRequests
A broadcast Stream<VrcRequest> that emits an incoming VDIP issuance request for every VRC exchange initiated by a contact.
Example
credentialsSDK.receivedVrcRequests.listen((VrcRequest request) {
print('VRC request from ${request.senderDid}');
});receivedVrcs
A broadcast Stream<VrcIssuance> that emits a signature-verified VRC for each finished credential received over VDIP.
Example
credentialsSDK.receivedVrcs.listen((VrcIssuance issuance) {
print('VRC received from ${issuance.senderDid}');
});R-Card Operations
watchReceivedRCards
Returns a live Stream<List<RCard>> of all persisted R-Cards, ordered by receivedAt descending. Backed by RCardRepository.watchAll, it emits a new list whenever any record is added, updated, or removed from local storage.
Parameters
No parameters required.
Example
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
credentialsSDK.watchReceivedRCards().listen((List<RCard> cards) {
print('Contact count: ${cards.length}');
});listReceivedRCards
Returns a snapshot Future<List<RCard>> of all persisted R-Cards, ordered by receivedAt descending.
Parameters
No parameters required.
Example
final cards = await credentialsSDK.listReceivedRCards();getReceivedRCardBySubjectDid
Returns the persisted R-Card whose sender DID matches subjectDid, or null if no such record exists.
Parameters
subjectDid
String
RequiredExample
final rCard = await credentialsSDK.getReceivedRCardBySubjectDid('did:example:alice');updateReceivedRCardNotes
Updates the notes field on the R-Card identified by subjectDid. Pass null to clear the notes. Does nothing if no record with that DID exists.
Parameters
subjectDid
String
Requirednotes
String?
Requirednull to clear existing notes.Example
await credentialsSDK.updateReceivedRCardNotes('did:example:alice', 'Met at conference 2025');
// Clear the notes
await credentialsSDK.updateReceivedRCardNotes('did:example:alice', null);deleteReceivedRCard
Removes the persisted R-Card identified by subjectDid.
Parameters
subjectDid
String
RequiredExample
await credentialsSDK.deleteReceivedRCard('did:example:alice');sendRCard
Builds, signs, and delivers an R-Card to the other party in channel via VDIP. Returns the sent RCard so callers can display or persist the issued card.
Parameters
channel
Channel
RequiredsubjectDid
String
Requiredcard
RCardSubject
RequiredissuerDidManager
DidManager
RequiredDidManager used to sign the credential with ecdsa-jcs-2019 Data Integrity proof.Example
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
final sent = await credentialsSDK.sendRCard(
channel: myChannel,
subjectDid: 'did:example:bob',
card: RCardSubject(
firstName: 'Alice',
lastName: 'Smith',
email: 'alice@example.com',
company: 'Acme Corp',
),
issuerDidManager: myDidManager,
);parseRCard
Parses and signature-verifies a raw R-Card VC blob. Returns null if the blob is not a valid, verified R-Card.
Parameters
vcBlob
String
RequiredExample
final rCard = await credentialsSDK.parseRCard(vcBlob: rawVcJson);
if (rCard != null) {
print('Valid R-Card from ${rCard.subjectDid}');
}consumePendingRCard
Returns and removes the last RCard from senderDid that arrived while no stream listener was attached. Returns null if no pending card exists.
Parameters
senderDid
String
RequiredExample
final pending = credentialsSDK.consumePendingRCard('did:example:alice');VRC Operations
requestVrcExchange
Initiates a VRC exchange by sending a VDIP issuance request to the contact identified by channelDid.
Parameters
channelDid
String
RequiredidentityDid
String
RequiredidentityName
String
RequiredExample
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
await credentialsSDK.requestVrcExchange(
channelDid: myChannelDid,
identityDid: myDid,
identityName: 'Alice',
);handleReceivedVrcRequest
Handles the credential-protocol outcome of receiving a VRC request. Returns a VrcRequestProcessingResult indicating whether a reciprocal credential was sent or the exchange was deferred.
Parameters
permanentChannelDid
String
Requiredrequest
VrcRequest
RequiredVrcRequest object from the receivedVrcRequests stream.hasVrcExchangeInitiated
bool
RequiredisConnectionInitiator
bool
RequiredissuerDid
String?
OptionalissuerName
String?
OptionalExample
credentialsSDK.receivedVrcRequests.listen((VrcRequest request) async {
final result = await credentialsSDK.handleReceivedVrcRequest(
permanentChannelDid: myChannelDid,
request: request,
hasVrcExchangeInitiated: false,
isConnectionInitiator: true,
issuerDid: myDid,
issuerName: 'Alice',
);
print('VRC request handled: $result');
});handleReceivedVrc
Handles the credential-protocol outcome of receiving a finished VRC. Returns VrcProcessingResultIgnored when the exchange is already completed, so no pre-guard is needed.
Parameters
permanentChannelDid
String
RequiredvcBlob
String
RequiredVrcIssuance.vcBlob field.exchangeState
VrcExchangeState
RequiredissuerDid
String?
OptionalissuerName
String?
OptionalExample
credentialsSDK.receivedVrcs.listen((VrcIssuance issuance) async {
final result = await credentialsSDK.handleReceivedVrc(
permanentChannelDid: myChannelDid,
vcBlob: issuance.vcBlob,
exchangeState: currentExchangeState,
issuerDid: myDid,
issuerName: 'Alice',
);
print('VRC processed: $result');
});watchVrcs
Returns a live Stream<List<Vrc>> of all persisted VRCs. Emits a new list whenever any record is added or removed from local storage.
Parameters
No parameters required.
Example
credentialsSDK.watchVrcs().listen((List<Vrc> vrcs) {
print('VRC count: ${vrcs.length}');
});listVrcs
Returns a snapshot Future<List<Vrc>> of all persisted VRCs.
Parameters
No parameters required.
Example
final vrcs = await credentialsSDK.listVrcs();getVrcById
Returns the persisted VRC identified by id, or null if no such record exists.
Parameters
id
String
Requiredid field).Example
final vrc = await credentialsSDK.getVrcById('<VRC-ID>');listVrcsByHolderDid
Returns all persisted VRCs where the holder DID matches holderDid.
Parameters
holderDid
String
RequiredExample
final vrcs = await credentialsSDK.listVrcsByHolderDid('did:example:alice');countVrcsByHolderDid
Returns the number of persisted VRCs where the holder DID matches holderDid.
Parameters
holderDid
String
RequiredExample
final count = await credentialsSDK.countVrcsByHolderDid('did:example:alice');
print('Verified relationships: $count');deleteVrc
Removes the persisted VRC identified by id.
Parameters
id
String
RequiredExample
await credentialsSDK.deleteVrc('<VRC-ID>');storeVrc
Parses, verifies, and persists a VRC from a raw VC blob. Throws MeetingPlaceCredentialsSDKException with error code vrcInvalidCredential if the blob cannot be parsed.
Parameters
vcBlob
String
RequiredreferenceId
String
RequiredverifiedAt
DateTime?
OptionalreceivedAt
DateTime?
OptionalcredentialFormat
String?
OptionalExample
final vrc = await credentialsSDK.storeVrc(
vcBlob: issuance.vcBlob,
referenceId: myChannelDid,
receivedAt: DateTime.now().toUtc(),
);
print('Stored VRC: ${vrc.id}');consumePendingVrcRequest
Returns and removes the last VrcRequest from senderDid that arrived while no stream listener was attached. Returns null if no pending request exists.
Parameters
senderDid
String
RequiredExample
final pending = credentialsSDK.consumePendingVrcRequest('did:example:alice');consumePendingVrc
Returns and removes the last VrcIssuance from senderDid that arrived while no stream listener was attached. Returns null if no pending issuance exists.
Parameters
senderDid
String
RequiredExample
final pending = credentialsSDK.consumePendingVrc('did:example:alice');Lifecycle
closeCredentialStreams
Cancels all internal stream subscriptions, closes the broadcast stream controller, and releases VDIP resources. Call this when the SDK instance is no longer needed to prevent memory leaks.
Parameters
No parameters required.
Example
await credentialsSDK.closeCredentialStreams();RCardSubject
Parsed contact fields from an R-Card Verifiable Credential. All fields are optional because a contact card may be partially filled.
RCardSubject (constructor)
Creates an RCardSubject with the given optional contact fields.
Parameters
id
String?
OptionalfirstName
String?
OptionallastName
String?
Optionalemail
String?
Optionalphone
String?
Optionalcompany
String?
Optionalposition
String?
Optionalwebsite
String?
Optionalsocial
String?
OptionalprofilePic
String?
OptionalExample
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
final subject = RCardSubject(
firstName: 'Alice',
lastName: 'Smith',
email: 'alice@example.com',
phone: '+1-555-0100',
company: 'Acme Corp',
position: 'Engineer',
);toVCard
An extension method on RCardSubject that serialises the contact fields to a vCard 3.0 string following RFC 6350.
Parameters
notes
String?
OptionalNOTE field.Example
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
final subject = RCardSubject(firstName: 'Alice', email: 'alice@example.com');
final vCard = subject.toVCard(notes: 'Met at conference 2025');
print(vCard);
// BEGIN:VCARD
// VERSION:3.0
// ...
// END:VCARDLivenessEvidenceSource
An abstract interface that collects LivenessEvidence from a configured liveness provider. Implement this in your application or in a provider-specific package.
getEvidence
Collects raw liveness evidence from the configured provider for the given holder.
Parameters
holderDid
String
RequiredExample
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
class AwsRekognitionEvidenceSource implements LivenessEvidenceSource {
@override
Future<LivenessEvidence> getEvidence({required String holderDid}) async {
// Call AWS Rekognition and map the response to LivenessEvidence
return LivenessEvidence(
providerId: 'aws_rekognition',
providerTransactionId: '<SESSION-ID>',
livenessScore: 0.98,
livenessThreshold: 0.90,
checkedAt: DateTime.now().toUtc(),
);
}
}LivenessVcIssuanceService
Issues signed W3C liveness credentials from normalised provider evidence. This is Stage 2 of the Human ZKP pipeline.
LivenessVcIssuanceService (constructor)
Creates a stateless liveness credential issuance service. Takes no arguments.
Parameters
No parameters required.
issue
Issues a signed VcDataModelV2 liveness credential for the given holder.
Parameters
issuerDid
String
RequiredholderDid
String
RequiredissuerDidManager
DidManager
RequiredDidManager used to sign the credential.evidence
LivenessEvidence
RequiredvalidFor
Duration
OptionalExample
import 'package:meeting_place_credentials/meeting_place_credentials.dart';
const issuanceService = LivenessVcIssuanceService();
final evidence = await evidenceSource.getEvidence(holderDid: myDid);
final livenessVc = await issuanceService.issue(
issuerDid: myIssuerDid,
holderDid: myDid,
issuerDidManager: myDidManager,
evidence: evidence,
validFor: const Duration(days: 1),
);Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.