Skip to content
Snippets Groups Projects
anoncredsCredentials.service.ts 5.72 KiB
Newer Older
import type { CredentialExchangeRecord} from '@aries-framework/core';
import type {
  EventAnonCredsCredentialOfferGetAllInput,
  EventAnonCredsCredentialOfferGetById,
  EventAnonCredsCredentialOfferGetByIdInput,
  EventAnonCredsCredentialRequestGetAllInput,
  EventAnonCredsCredentialRequestGetById,
  EventAnonCredsCredentialRequestGetByIdInput,
  EventAnonCredsCredentialsDeleteById,
  EventAnonCredsCredentialsDeleteByIdInput,
  EventAnonCredsCredentialsGetAllInput,
  EventAnonCredsCredentialsGetByIdInput,
  EventDidcommAnonCredsCredentialsOffer,
  EventDidcommAnonCredsCredentialsOfferInput,
  EventDidcommAnonCredsCredentialsOfferToSelf,
  EventDidcommAnonCredsCredentialsOfferToSelfInput
} from '@ocm/shared';

import { AutoAcceptCredential, CredentialState } from '@aries-framework/core';
import { Injectable } from '@nestjs/common';
import { logger } from '@ocm/shared';

import { MetadataTokens } from '../../common/constants.js';
import { WithTenantService } from '../withTenantService.js';

@Injectable()
export class AnonCredsCredentialsService {
  public constructor(private withTenantService: WithTenantService) {}

  public async getAll({
    tenantId,
  }: EventAnonCredsCredentialsGetAllInput): Promise<
    Array<CredentialExchangeRecord>
  > {
    return this.withTenantService.invoke(tenantId, (t) =>
      t.credentials.getAll(),
    );
  }

  public async getAllOffers({
    tenantId,
  }: EventAnonCredsCredentialOfferGetAllInput): Promise<
    EventAnonCredsCredentialOfferGetAll['data']
  > {
    return this.withTenantService.invoke(tenantId, (t) =>
      t.credentials.findAllByQuery({
        $or: [
          { state: CredentialState.OfferSent },
          { state: CredentialState.OfferReceived },
        ],
      }),
    );
  }

  public async getAllRequests({
    tenantId,
  }: EventAnonCredsCredentialRequestGetAllInput): Promise<
    EventAnonCredsCredentialRequestGetAll['data']
  > {
    return this.withTenantService.invoke(tenantId, (t) =>
      t.credentials.findAllByQuery({
        $or: [
          { state: CredentialState.RequestSent },
          { state: CredentialState.RequestReceived },
        ],
      }),
    );
  }

  public async deleteById({
    tenantId,
    credentialRecordId,
  }: EventAnonCredsCredentialsDeleteByIdInput): Promise<
    EventAnonCredsCredentialsDeleteById['data']
  > {
    return this.withTenantService.invoke(tenantId, async (t) => {
      await t.credentials.deleteById(credentialRecordId);
      return {};
    });
  }

  public async getById({
    tenantId,
    credentialRecordId,
  }: EventAnonCredsCredentialsGetByIdInput): Promise<CredentialExchangeRecord | null> {
    return this.withTenantService.invoke(tenantId, (t) =>
      t.credentials.findById(credentialRecordId),
    );
  }

  public async getOfferById({
    tenantId,
    credentialOfferId,
  }: EventAnonCredsCredentialOfferGetByIdInput): Promise<
    EventAnonCredsCredentialOfferGetById['data']
  > {
    return this.withTenantService.invoke(tenantId, async (t) => {
      const credential = await t.credentials.findById(credentialOfferId);

      if (
        credential &&
        credential.state !== CredentialState.OfferSent &&
        credential.state !== CredentialState.OfferReceived
      ) {
        logger.warn(
          `Credential '${credentialOfferId}' does exist, but is not in offer state. Actual state: ${credential.state}`,
        );
      }

      return credential;
    });
  }

  public async getRequestById({
    tenantId,
    credentialRequestId,
  }: EventAnonCredsCredentialRequestGetByIdInput): Promise<
    EventAnonCredsCredentialRequestGetById['data']
  > {
    return this.withTenantService.invoke(tenantId, async (t) => {
      const credential = await t.credentials.findById(credentialRequestId);

      if (
        credential &&
        credential.state !== CredentialState.RequestSent &&
        credential.state !== CredentialState.RequestReceived
      ) {
        logger.warn(
          `Credential '${credentialRequestId}' does exist, but is not in a request state. Actual state: ${credential.state}`,
        );
      }

      return credential;
    });
  }

  public async offer({
    tenantId,
    connectionId,
    credentialDefinitionId,
    attributes,
  }: EventDidcommAnonCredsCredentialsOfferInput): Promise<
    EventDidcommAnonCredsCredentialsOffer['data']
  > {
    return this.withTenantService.invoke(tenantId, (t) =>
      t.credentials.offerCredential({
        protocolVersion: 'v2',
        connectionId,
        credentialFormats: {
          anoncreds: { credentialDefinitionId, attributes },
        },
      }),
    );
  }

  public async offerToSelf({
    tenantId,
    credentialDefinitionId,
    attributes,
  }: EventDidcommAnonCredsCredentialsOfferToSelfInput): Promise<
    EventDidcommAnonCredsCredentialsOfferToSelf['data']
  > {
    return this.withTenantService.invoke(tenantId, async (t) => {
      const connections = await t.connections.getAll();
      const connection = connections.find((c) => {
        const metadata = c.metadata.get<{ withSelf: boolean }>(
          MetadataTokens.GAIA_X_CONNECTION_METADATA_KEY,
        );
        return metadata && metadata.withSelf === true;
      });

      if (!connection) {
        throw new Error(
          'Cannot offer a credential to yourself as there is no connection',
        );
      }

      if (!connection.isReady) {
        throw new Error('Connection with yourself is not ready, yet');
      }

      return t.credentials.offerCredential({
        protocolVersion: 'v2',
        autoAcceptCredential: AutoAcceptCredential.Always,
        connectionId: connection.id,
        credentialFormats: {
          anoncreds: { credentialDefinitionId, attributes },
        },
      });
    });
  }
}