--- tags: aca-py, dts --- # Aries VCR Issuer-As-A-Service Design Design document for the implementation of a Issuer-As-A-Service workflow/application. See [this GitHub issue](https://github.com/bcgov/von/issues/371) for initial details. ## Architecture ### Agent Aca-Py instance, managed by the BCGov DTS Team. The "main" agent will be initially deployed, with tenants (sub-wallets) created on-demand by new issuers. The main hosted agent *COULD* be an endorser (T.B.D.) ### Controller A single controller (scalable) exposing functionality to new issuers. It will expose both endpoints that act as proxy for Aca-Py functionality, and management-specific APIs: #### Agent API - Create Schema (this will take care of Schema and Cred Def, users do not need to worry about dealing with both) - ~~Receive Invitation: used to receive an invitation to connect with the Aries VCR agent~~ - Issue Credential: issues a credential with the specified schema/payload to Aries VCR - ~~Revoke Credential~~ - ~~Set/Update webhook endpoint(s)~~ - Retrieve DID/verkey - Fetch/Accept TAA **Note 1:** Actions that involve a ledger write should provide the option for a batched/deferred write. **Note 2:** The endorsement protocol is still being implemented, so - for the time being - the assumption that ALL write transactions will need to be endorsed will be made, deferring the actual implementation logic to later. **Note 3:** Endorsing ledger writes is assumed to be a "transparent" workflow/process to the issuers, who will NOT need to know this is happening and how. The assumption is also that - at this time - only one endorser will be required, and it can/will always be the same (e.g.: BCGov endorsing agent, such as the primary Aca-Py instance) #### Management API - Register new issuer: this will create a new tenant/sub-wallet, and store its token in a profile. The same token could be given to the issuer, OR a new API Key mapping (recommended) will be generated for the user(s) to use - Update API Key: updates the internal mapping between the user-visible API Key and the wallet token. ### Management UI (phase 2) A web application providing a Web UI for the controller APIs. It should provide an interface to "poke" at things and prepare for B2B automated business flows. Some functionality will be basic (e.g.: display the result of calls to the agent's endpoints) while other could be more advanced (e.g.: view/edit/save schema definitions) ## Technology To ensure consistency with existing projects, the following technology stacks are suggested ### Backend ~~#### Django + PostgreSQL~~ ~~The current BC Registries Issuer is based on this technology stack, however this is the most "outdated" issuer.~~ #### Flask + PostgreSQL/MongoDB The Aries VCR Issuer Controller is written in Flask, and uses PostgreSQL as RDBMS. Using MongoDB would allow more flexibility for configuration objects as it does not require tight coupling with relational models. #### NodeJS + MongoDB This is what Issuer Kit uses. ### Frontend (Phase 2) Any frontend web framework will work, but the recommendation is for VueJS based on current experience/skill and existing working examples (Issuer Kit). ## Testing Unit Tests from inception, integration and API tests once the full flow is complete. Both test workflows should be included in the CI/CD pipelines responsible for deployments. ## Open Points #### Ledger Write Cost Tracking - What are the requirements for tracking the cost for each ledger write for each issuer? - Complexity of tracking batched writes vs. instant writes. #### Endorsement - Protocol is still T.B.D., controller logic will be defined when protocol is ready. - Are issuers required to select how many and which endorsers to use? #### Tenants - Do DIDs need to be registered on the ledger? How does that happen (technically and workflow-wise)? - Can tenants/sub-wallets be started in read-only mode independently from the main Aca-Py wallet? - TAA acceptance: what workflow will be enforced? Do we need to simplify/guide the current process? - Expected onboarding flow: what steps are required for a new issuer to be able to authenticate and start? This will dictate a number of steps in the business logic. #### General - Agent caching: need to verify caching is refreshed when accepting TAA, etc. - Webhooks: is an admin API available? It is required for multi-tenancy, it might require aca-py updates. - Use controller as webhook delivery proxy: what are the implications of this? What happens during load? And on long-lived requests that may timeout? - Database: defining requirements (e.g.: need for reporting) should show what the better architecture is for the purpose. --- ## Implementation Notes/Details - Webhook delivery will specify which wallet webhooks are coming from by using the `x-wallet-id` header - Each tenant can specify which URLs to send webhooks to, so we may be able to skip proxying these requests - submitting equests to a tenant's API is done by adding the `Authorization: Bearer <token>` header, where `<token>` is the `jwt` assigned to the wallet during creation - Rather than exposing the jwt to the users, an internal mapping in the controller/proxy should be used instead. - creating schemas is more than a request to the agent: the `issuer_registration` protocol needs to be used/triggered --- # API Specification ## Admin API **API Namespace:** /admin/ Global API namespace for provisioning and deleting issuers. :::info The endpoints in the `admin` namespace may be configured to only accept requests from a superuser api-key, rather than from each tenant. ::: ### Create Issuer Creates a new issuer profile. Internally, the API will perform the following tasks: 1. Create a new issuer profile and store it in the Database. - This includes a new api-key that will be provided to the consumers. 2. Submit a POST request to the `/multitenancy/wallet` endpoint on the agent Admin API to create a new sub-wallet, with default values to match the API signature. 3. Update the value in the issuer profile with the new `jwt` created for the sub-wallet, as well as the `wallet_id`. - :::warning T.B.D.: value could be salted before being stored. ::: 4. Establish a connection with the Aries VCR agent. 5. Return a success response containing the name of the issuer, and the generated api-key. **URL:** `<namespace>/issuer` **Method:** `POST` **Parameters:** ```json { "name": "issuer_name" } ``` **Success Response** Wallet created successfully **Code:** `201 CREATED` **Response:** ```json { "name": "issuer_name", "api-key": "super-secret-1" } ``` ### Delete Issuer Proxy for `{AGENT_ADMIN_URL}/multitenancy/wallet/{id}/remove`. **URL:** `<namespace>/issuer/{id}` **Method:** `DELETE` **Parameters:** ```json { "api-key": "super-secret-1" } ``` **Success Response** Wallet created successfully **Code:** `204 NO CONTENT` **Response:** ```json {} ``` ## Issuer API **API Namespace:** /issuer/ API namespace for issuers self-management functionality. Requests to this namespace will expect an `x-api-key` header to be set, with the value corresponding to the API Key for issuer being managed. ### Get Issuer Info **URL:** `<namespace>/profile/{name}` **Method:** `GET` **Success Response** Issuer info list. **Code:** `200 OK` **Response:** ```json { "did": "WgWxqztrNooG92RXvxSTWv", "verkey": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", "name": "my-org-full-name", "abbreviation": "MOFN", "url": "https://www.my-organization.ca/my-organization-info-page", "email": "info@my-organization.ca" "logo": "base64-encoded-image-data" } ``` ### Update Issuer Info Updates the issuer metadata used when registering the issuer with the Credential Registry. **URL:** `<namespace>/profile/{name}` **Method:** `POST` **Parameters:** ```json { "name": "my-org-full-name", "abbreviation": "MOFN", "url": "https://www.my-organization.ca/my-organization-info-page", "email": "info@my-organization.ca" "logo": "base64-encoded-image-data" } ``` **Success Response** Issuer profile updated successfully. **Code:** `200 OK` **Response:** ```json { "did": "WgWxqztrNooG92RXvxSTWv", "verkey": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", "name": "my-new-full-name", "abbreviation": "MNFN", "url": "https://www.my-new-organization.ca/my-organization-info-page", "email": "info@my-new-organization.ca" "logo": "new-base64-encoded-image-data" } ``` ### Update API Key Updates the api-key used by the issuer, useful in case the consumers want to update their authentication method. **URL:** `<namespace>/authentication` **Method:** `POST` **Parameters:** ```json {} ``` **Success Response** Wallet created successfully **Code:** `200 OK` **Response:** ```json { "api-key": "super-secret-2" } ``` ### TAA Issuers will be required to accept Sovrin's Transaction Author Agreement before they can start writing to the Ledger. #### Get TAA Proxy for `{AGENT_ADMIN_URL}/ledger/taa`. **URL:** `<namespace>/taa` **Method:** `GET` **Success Response** Current TAA. **Code:** `200 OK` **Response:** ```json { // TAA content } ``` #### Accept TAA Proxy for `{AGENT_ADMIN_URL}/ledger/taa/accept`. **URL:** `<namespace>/taa/accept` **Method:** `POST` **Parameters:** ```json { // TAA payload - could use defaults based on requirements } ``` **Success Response** TAA accepted. **Code:** `200 OK` **Response:** ```json {} ``` ### Schemas #### Get Schemas The API will consolidate the result of requests to `{AGENT_ADMIN_URL}/schemas/created` and `{AGENT_ADMIN_URL}/schemas/{schema_id}` and return the resulting object. **URL:** `<namespace>/schemas` **Method:** `GET` **Success Response** List of schemas that have been created by the agent. **Code:** `200 OK` **Response:** ```json [ { "id": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", "attrNames": [ "game_date" "score", "value", "name" ], "name": "schema_name", "version": "1.0", "metadata": { ....... } }, ......... ] ``` #### Create/Update Schema Proxy for `{AGENT_ADMIN_URL}/schemas`. It will also take care of calling `{AGENT_ADMIN_URL}/credential-definitions`. The API will expect to receive either `schema_id` (to start from a schema previously published by another agent/issuer), or `schema_name`, `schema_version`. `attributes` will need to be specified in both cases. The `metadata` object contains information required by Aries VCR to correctly map/process the information in the credential. **URL:** `<namespace>/schemas` **Method:** `POST` **Parameters:** ```json { "schema_id": "AgFxqzurNooG66RXloSTWv:2:schema_name:1.0", "credential_definition_id": "WgWxqztrNooG92RXvxSTWv:3:CL:20:issuer_name" "attributes": [ "score_date", "game_date" "score", "value", "name" ], "schema_name": "prefs", "schema_version": "1.0", "metadata": { "topic": [ { "name": "name", "topic_type": "registration.registries.ca" } ], "cardinality": ["name", "score"], "effective_date": "game_date", "other_date_fields": ["score_date"], "search_fields": ["name"] ......... } } ``` **Success Response** Schema created. **Code:** `200 OK` **Response:** ```json { "schema_id": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", "credential_definition_id": "WgWxqztrNooG92RXvxSTWv:3:CL:20:issuer_name" } ``` ### Credentials #### Issue Credential **URL:** `<namespace>/credentials` **Method:** `POST` **Parameters:** ```json { "schema_name": "quick_game_result", "schema_version": "1.0", "attributes": [ { "name": "score_date", "value": "2021-01-22T16:45:55+0000" }, { "name": "game_date", "value": "2021-01-25T22:57:25+0000" }, { "name": "score", "value": "home" }, { "name": "value", "value": "85" }, { "name": "name", "value": "Wildcats" } ] } ``` **Success Response** Credential issued. **Code:** `200 OK` **Response:** ```json { "credential_exchange_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "issued" } ``` :::warning Issuing a credential will be performed synchronously: the controller will wait for a response from the agent via webhooks before returning the rsult to the issuer. This could potentially be problematic when under load from one or more issuers, and will need to be tested and potentially tweaked. ::: :::warning To consider: submitting an array of credentials of 1+ elements to support batching. This could be a good approach, however it would make the blocking logic slightly more complex as ordering will need to be accounted for when returning a response. Additionally, the request time could become very long and exceed reasonable timeout intervals. ::: #### Revoke Credential Revocation in Aries VCR is achieved by issuing a new credential to the same `topic` and `cardinality` fields: the existing credential will automatically be considered expired and displayed as such. ### Additional Endpoints The following endpoints expose additional utility APIs, may be implemented in a second phase. #### Apply Changes Ensures the current settings are applied and synchronized with Aries VCR. **URL:** `<namespace>/synchronize` **Method:** `POST` **Parameters:** ```json {} ``` **Success Response** Changes applied successfully. **Code:** `200 OK` **Response:** ```json {} ``` #### Export Data Exports the issuer profile and all of its settings. **URL:** `<namespace>/export` **Method:** `POST` **Parameters:** ```json {} ``` **Success Response** Data exported succesfully.. **Code:** `200 OK` **Response:** ```json { // TBD: data structure containing all the profile/settings } ``` #### Import Data Imports the issuer profile and all of its settings. **URL:** `<namespace>/import` **Method:** `POST` **Parameters:** ```json {} ``` **Success Response** Data imported successfully. **Code:** `200 OK` **Response:** ```json { // TBD: data structure containing all the profile/settings, // will match the output of the export API } ```