release publish

TRQP for Dart

A Dart package for implementing the Trust Registry Query Protocol (TRQP), a protocol that enables applications to query trust registries for authorisation and recognition decisions. TRQP provides a standardised way to verify whether entities are authorised to perform specific actions on resources or whether authorities recognize other authorities.

The TRQP package consists of two main client implementations:

  • TrqpHttpClient that implements TRQP over HTTP/HTTPS for secure REST API communication with trust registries.

  • TrqpDidcommClient that implements TRQP over DIDComm v2.1 protocol for secure, decentralised communication with trust registries.

These clients simplify trust registry queries, including authorisation and recognition checks, supporting both traditional HTTP-based architectures and decentralized DIDComm-based systems.

TRQP enables developers to build robust, trust-based applications where verifying authority relationships is critical. TRQP unlocks use cases such as:

  • Credential issuance authorisation (verifying an entity can issue specific credential types)
  • Authority recognition (verifying one authority recognises another's governance)
  • Access control decisions based on registry policies
  • Federated trust networks across organisations

These are a few scenarios from a wide range of use cases requiring authoritative verification of entity permissions and authority relationships.

Table of Contents

Core Concepts

The Trust Registry Query Protocol (TRQP) provides a standardized approach to querying trust registries for authorisation and recognition decisions.

  • Trust Registry - A service that maintains authoritative records of which entities are authorized to perform specific actions on resources, and which authorities recognize other authorities. Trust registries are the source of truth for authorisation policies in decentralized trust networks.

  • Authorization Query - A query to determine whether a specific entity is authorized by an authority to perform an action on a resource. For example, "Is entity X authorized by authority Y to issue engineering licenses?"

  • Recognition Query - A query to determine whether one authority recognises another authority as being authoritative for specific actions on resources. For example, "Does authority A recognize authority B as governing professional engineering credentials?"

  • Entity - An identifier (typically a DID) representing a party that may be authorized to perform actions. Entities can be individuals, organisations, or services.

  • Authority - An identifier (typically a DID) representing a party that can authorise entities or recognize other authorities. Authorities define and enforce authorisation policies.

  • Action - The operation being authorized or governed (e.g., "issue", "verify", "govern", "revoke").

  • Resource - The type of credential, service, or entity that the action applies to (e.g., "engineer-license", "medical-credential").

  • Context - Additional contextual information that may affect authorisation decisions (e.g., timestamp, location, metadata).

Key Features

  • Implements the Trust Registry Query Protocol (TRQP) for authorisation and recognition queries.
  • Supports both HTTP/HTTPS and DIDComm v2.1 transport protocols.
  • Type-safe request and response models with JSON serialization.
  • Flexible authentication with per-request bearer token support (HTTP client).
  • Decentralized communication using DIDComm protocol (DIDComm client).
  • Automatic DIDComm mediator integration for secure message routing.
  • Problem reporting mechanism for error handling in DIDComm.
  • Built-in support for contextual authorisation decisions.

Protocol Support

The package supports two transport protocols:

Protocol Implementation Use Case
HTTP/HTTPS TrqpHttpClient REST API integration with trust registries
DIDComm v2.1 TrqpDidcommClient Decentralized trust registry communication

Query Types

Query Type Purpose Request Returns
Authorization Check if entity is authorized for an action AuthorizationRequest AuthorizationResponse
Recognition Check if authority recognises another authority RecognitionRequest RecognitionResponse

Requirements

  • Dart SDK version ^3.6.0
  • A running and accessible Trust Registry. For a quick start, you can deploy Affinidi Trust Registry in your environment.

Installation

Run:

dart pub add affinidi_trqp_dart

or manually, add the package into your pubspec.yaml file:

dependencies:
  affinidi_trqp_dart: ^1.0.0-dev

and then run the command below to install the package:

dart pub get

TRQP Clients

TrqpHttpClient

The TrqpHttpClient implements TRQP over HTTP/HTTPS, enabling your application to:

  • Query trust registries via REST APIs.
  • Send authorisation and recognition requests.
  • Handle authentication with bearer tokens (per-request or client-level).
  • Process typed responses with automatic JSON deserialization.

Key Methods:

  • queryAuthorization() - Queries whether an entity is authorized for an action.
  • queryRecognition() - Queries whether an authority recognises another authority.
  • close() - Closes the HTTP client and releases resources.

Constructor Parameters:

  • baseUrl - The base URL of the trust registry HTTP endpoint.
  • bearerToken (optional) - Default bearer token for authentication.
  • httpClient (optional) - Custom HTTP client instance.

TrqpDidcommClient

The TrqpDidcommClient implements TRQP over DIDComm v2.1, enabling your application to:

  • Query trust registries using decentralized DIDComm messaging.
  • Establish secure, end-to-end encrypted communication channels.
  • Automatically route messages through DIDComm mediators.
  • Handle authorisation and recognition queries with cryptographic security.

Key Methods:

  • queryAuthorization() - Sends an authorisation query via DIDComm.
  • queryRecognition() - Sends a recognition query via DIDComm.
  • init() - Static factory method to initialize the client with DID manager and mediator.

Initialization Requirements:

  • didManager - DID manager for key management and DID operations.
  • authorizationProvider (optional) - Provider for Access Control List (ACL) configuration in the DIDComm mediator. This is essential for DIDComm communication as it grants the trust registry service permission to send messages through your mediator, enabling bi-directional communication. If not provided, a default AffinidiAuthorizationProvider will be created automatically.
  • clientOptions (optional) - Client configuration options.

Usage

HTTP Client Usage

The HTTP client is ideal for applications integrating with trust registries over REST APIs:

import 'package:trqp/trqp.dart';

Future<void> main() async {
  // Create HTTP client with base URL
  final client = TrqpHttpClient(
    baseUrl: 'https://registry.example.com',
    bearerToken: 'your-auth-token', // Optional default token
  );

  // Authorization Query
  final authRequest = AuthorizationRequest(
    entityId: 'did:example:user-1234',
    authorityId: 'did:example:authority-A',
    action: 'issue',
    resource: 'engineer-license',
    context: {
      'time': DateTime.now().toUtc().toIso8601String(),
    },
  );

  final authResponse = await client.queryAuthorization(
    authRequest,
    bearerToken: 'request-specific-token', // Optional per-request token
  );

  if (authResponse.authorized) {
    print('Entity is authorized to issue engineer licenses');
  } else {
    print('Authorization denied: ${authResponse.message}');
  }

  // Recognition Query
  final recRequest = RecognitionRequest(
    entityId: 'did:example:authority-B',
    authorityId: 'did:example:authority-A',
    action: 'govern',
    resource: 'professional-engineers',
    context: {
      'time': DateTime.now().toUtc().toIso8601String(),
    },
  );

  final recResponse = await client.queryRecognition(recRequest);

  if (recResponse.recognized) {
    print('Authority B is recognized by Authority A');
  } else {
    print('Recognition denied: ${recResponse.message}');
  }

  // Always close the client
  client.close();
}

DIDComm Client Usage

The DIDComm client is ideal for decentralized applications requiring secure, peer-to-peer communication:

import 'package:trqp/trqp.dart';
import 'package:ssi/ssi.dart';

Future<void> main() async {
  // Set up DID manager
  final keyStore = InMemoryKeyStore();
  final wallet = PersistentWallet(keyStore);
  final didManager = DidKeyManager(
    wallet: wallet,
    store: InMemoryDidStore(),
  );

  final keyId = 'client-key-1';
  await wallet.generateKey(
    keyId: keyId,
    keyType: KeyType.p256,
  );
  await didManager.addVerificationMethod(keyId);

  // Initialize DIDComm client
  final client = await TrqpDidcommClient.init(
    didManager: didManager,
    trqpDid: '<did:example:trqp-service>',
  );

  // Authorization Query via DIDComm
  final authRequest = AuthorizationRequest(
    entityId: 'did:example:user-5678',
    authorityId: 'did:example:authority-C',
    action: 'verify',
    resource: 'medical-credential',
    context: {
      'time': DateTime.now().toUtc().toIso8601String(),
      'location': 'hospital-A',
    },
  );

  final authResponse = await client.queryAuthorization(authRequest);

  print('Authorized: ${authResponse.authorized}');
  print('Time Evaluated: ${authResponse.timeEvaluated}');

  // Recognition Query via DIDComm
  final recRequest = RecognitionRequest(
    entityId: 'did:example:authority-D',
    authorityId: 'did:example:authority-C',
    action: 'govern',
    resource: 'healthcare-providers',
  );

  final recResponse = await client.queryRecognition(recRequest);

  print('Recognized: ${recResponse.recognized}');
  print('Message: ${recResponse.message ?? "No message"}');
}

Error Handling

Both HTTP and DIDComm clients have distinct error handling mechanisms that help you troubleshoot issues effectively.

HTTP Client Errors

The HTTP client throws TrqpHttpException when requests fail. This exception includes:

  • statusCode - The HTTP status code (e.g., 401, 403, 500)
  • message - A descriptive error message
  • details - Additional error details following RFC 7807 Problem Details format

Example HTTP Error:

import 'package:trqp/trqp.dart';

Future<void> handleHttpErrors() async {
  final client = TrqpHttpClient(
    baseUrl: 'https://registry.example.com',
  );

  try {
    final authRequest = AuthorizationRequest(
      entityId: 'did:example:user-1234',
      authorityId: 'did:example:authority-A',
      action: 'issue',
      resource: 'engineer-license',
    );

    final response = await client.queryAuthorization(authRequest);
    print('Authorization result: ${response.authorized}');
  } on TrqpHttpException catch (e) {
    print('HTTP Error occurred:');
    print('Status Code: ${e.statusCode}');
    print('Message: ${e.message}');
    if (e.details != null) {
      print('Details: ${e.details}');
    }

    // Handle specific error codes
    if (e.statusCode == 401) {
      print('Authentication failed - check your bearer token');
    } else if (e.statusCode == 403) {
      print('Access forbidden - insufficient permissions');
    } else if (e.statusCode == 404) {
      print('Trust registry endpoint not found');
    }
  } catch (e) {
    print('Unexpected error: $e');
  } finally {
    client.close();
  }
}

Sample HTTP Error Output:

TrqpHttpException: Unauthorized access (status: 401)
Details: {type: about:blank, title: Unauthorized, status: 401, detail: Invalid or missing bearer token}

DIDComm Client Errors

The DIDComm client handles errors through the DIDComm Problem Report mechanism. When a trust registry service encounters an error, it sends a ProblemReportMessage containing:

  • code - A standardized problem code (e.g., trust-registry-forbidden)
  • comment - Human-readable description of the error
  • args - Additional contextual information about the error

Example DIDComm Error:

import 'package:trqp/trqp.dart';
import 'package:ssi/ssi.dart';

Future<void> handleDidcommErrors() async {
  // Setup DID manager
  final keyStore = InMemoryKeyStore();
  final wallet = PersistentWallet(keyStore);
  final didManager = DidKeyManager(
    wallet: wallet,
    store: InMemoryDidStore(),
  );

  final keyId = 'client-key-1';
  await wallet.generateKey(keyId: keyId, keyType: KeyType.p256);
  await didManager.addVerificationMethod(keyId);

  try {
    final client = await TrqpDidcommClient.init(
      didManager: didManager,
      trqpDid: '<did:example:trqp-service>',
    );

    final authRequest = AuthorizationRequest(
      entityId: 'did:example:user-5678',
      authorityId: 'did:example:authority-C',
      action: 'verify',
      resource: 'medical-credential',
    );

    final response = await client.queryAuthorization(authRequest);
    print('Authorization result: ${response.authorized}');
  } catch (e) {
    if (e.toString().contains('Access forbidden')) {
      print('DIDComm Error: Access forbidden');
      print('Your DID is not authorized to communicate with this trust registry');
      print('Error details: $e');
    } else {
      print('DIDComm communication error: $e');
    }
  }
}

Sample DIDComm Error Output:

Access forbidden: You do not have permission to access this TRQP service

Common Error Scenarios:

Error Type HTTP Client DIDComm Client
Authentication failure 401 status code Access forbidden message
Insufficient permissions 403 status code Access forbidden message
Service unavailable 503 status code Timeout or connection error
Invalid request format 400 status code Problem report with validation details
Network issues Connection exceptions Message delivery timeout

Complete Examples

See example/affinidi_trqp_dart_example.dart for complete runnable examples demonstrating:

  • HTTP client configuration and usage
  • Authorization and recognition queries
  • Bearer token authentication
  • Error handling
  • Resource cleanup

Security Features

  • Transport Security: HTTP client supports HTTPS for encrypted communication; DIDComm client provides end-to-end encryption.

  • Authentication: Flexible bearer token authentication in HTTP client, supporting both client-level and per-request tokens.

  • Cryptographic Security: DIDComm client uses cryptographic signatures and encryption for secure, tamper-evident communication.

  • Contextual Authorization: Support for context parameters enables time-based, location-based, and metadata-driven authorisation decisions.

  • Problem Reporting: DIDComm client includes standardized error reporting for debugging and issue resolution.

Support & Feedback

If you face any issues or have suggestions, please don't hesitate to contact us using this link.

Reporting Technical Issues

If you have a technical issue with the TRQP codebase, you can also create an issue directly in GitHub.

  1. Ensure the bug was not already reported by searching on GitHub under Issues.
  2. If you're unable to find an open issue addressing the problem, open a new one. Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behaviour that is not occurring.

Contributing

Want to contribute?

Head over to our CONTRIBUTING guidelines.

Libraries

trqp
Support for Trust Registry Query Protocol (TRQP) in Dart.