Security Issue when Exercising Choices

Not sure what I am doing wrong but I am getting the same security issue. I tried saving the contractId and exercising a choice. I am trying to exercise the AppInstallRequest_Accept from the Quickstart using Pat 'user_pat_at_app-provider_dot_localhost::12202e0571cc7fc76ccc5cf4dd75f6cbdec6aeef12ee910f91b0c1ee90b692be00dc'. Template id is '#quickstart-licensing:Licensing.AppInstall:AppInstallRequest and contractId is 0065a1ebc5d46cd7a0eef88ea7f45d74b5a9af595c9d19624c0189c8fb9b36abfcca111220bb17772995efc66a839e82f14ff34be1abd1950813436f1e65ca798562b7cf62

Decoded token payload: {
  exp: 1749716820,
  iat: 1749716520,
  jti: 'd754fd49-88b8-488f-9ba7-cfbc07d1db12',
  iss: 'http://keycloak.localhost:8082/realms/AppProvider',
  aud: [ 'https://canton.network.global', 'account' ],
  sub: 'c87743ab-80e0-4b83-935a-4c0582226691',
  typ: 'Bearer',
  azp: 'app-provider-validator',
  acr: '1',
  'allowed-origins': [ '/*' ],
  realm_access: {
    roles: [
      'offline_access',
      'default-roles-quickstart',
      'uma_authorization'
    ]
  },
  resource_access: { account: { roles: [Array] } },
  scope: 'email profile',
  clientHost: '192.168.65.1',
  email_verified: false,
  preferred_username: 'service-account-app-provider-validator',
  clientAddress: '192.168.65.1',
  client_id: 'app-provider-validator'
}
Token verified successfully
Failed to exercise choice: Error: Request failed with HTTP status code 401
Response body: {"code":"NA","cause":"A security-sensitive error has been received","correlationId":"52b1634b6cdfbdeaa997cf7e052e935e","traceId":"52b1634b6cdfbdeaa997cf7e052e935e","context":{},"resources":[],"errorCategory":-1,"grpcCodeValue":16,"retryInfo":null,"definiteAnswer":null}
    at Function.exerciseChoice (/Users/paulwilkinson/projects/deploi/cn-quickstart/quickstart/deploi-server/src/services/providerLedger.service.ts:128:15)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /Users/paulwilkinson/projects/deploi/cn-quickstart/quickstart/deploi-server/src/routes/contracts.routes.ts:698:16
}

  static async exerciseChoice(
    contractId: string,
    templateId: string,
    choice: string,
    choiceArgument: any,
    providerPartyId: string
  ) {
    const token = await TokenGenerator.generateProviderValidatorToken();

    // Add meta and installMeta to choiceArgument if they don't exist
    const enhancedChoiceArgument = {
      ...choiceArgument,
      meta: {
        values: [],
      },
      installMeta: {
        values: [],
      },
    };

    const command = {
      commands: {
        commands: [
          {
            ExerciseCommand: {
              templateId,
              contractId,
              choice,
              choiceArgument: enhancedChoiceArgument,
            },
          },
        ],
        workflowId: `exercise-${choice}-${Date.now()}`,
        applicationId: "92a520cb-2f09-4e55-b465-d178c6cfe5e4",
        commandId: `exercise-${choice}-${Date.now()}`,
        deduplicationPeriod: {
          Empty: {},
        },
        actAs: [providerPartyId],
        readAs: [providerPartyId],
        submissionId: `exercise-${choice}`,
        disclosedContracts: [],
        domainId: "",
        packageIdSelectionPreference: [],
      },
      transactionFormat: {
        eventFormat: {
          filtersByParty: {
            [providerPartyId]: {},
          },
          verbose: true,
        },
        transactionShape: "TRANSACTION_SHAPE_LEDGER_EFFECTS",
      },
    };

    try {
      const response = await fetch(`${this.BASE_URL}/v2/commands/submit-and-wait-for-transaction`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(command),
      });

      if (!response.ok) {
        const responseBody = await response.text();
        const errorDetails = JSON.parse(responseBody);

        // Handle specific error cases
        if (errorDetails.code === "INVALID_ARGUMENT") {
          throw new Error(`Invalid template or choice: ${errorDetails.cause}`);
        }

        throw new Error(`Request failed with HTTP status code ${response.status}\nResponse body: ${responseBody}`);
      }

      return response.json();
    } catch (error: any) {
      // Enhance error message with more context
      if (error.message.includes("Invalid template")) {
        throw new Error(`Failed to exercise choice '${choice}' on contract ${contractId}: ${error.message}`);
      }
      throw error;
    }
  }
}

Might be the same issues as for List Contracts for a Provider, just with “actAs” right, not “readAs” right. Have a look at how your user (c87743ab-80e0-4b83-935a-4c0582226691) is configured - ie what rights it has.

Often the Participant’s logs will have more detail. It’s my habit to always check those because the error message on the client side may be generalized.

Thanks all, I did get through this. Once I stopped looking at tokens and started looking at the daml everything started to fall into place and I can finally create contracts and exercise choices.

2 Likes