openwallet-foundation / credo-ts

Typescript framework for building decentralized identity and verifiable credential solutions
https://credo.js.org
Apache License 2.0
260 stars 197 forks source link

Protocol version type incompatible in CredentialModule #902

Closed janrtvld closed 1 year ago

janrtvld commented 2 years ago

Im trying to convert the rest api to use AFJ interfaces. In this case im using the OfferCredentialOptions interface to offer a credential.

  @Post('/offer-credential')
  public async offerCredential(@Body() options: OfferCredentialOptions) {
    try {
      const credential = await this.agent.credentials.offerCredential(options)
      return credential.toJSON()
    } catch (error) {
      ...
  }

I'm getting the following type error:

Argument of type 'OfferCredentialOptions<CredentialFormat[], CredentialService<CredentialFormat[]>[]>' is not assignable to parameter of type 'OfferCredentialOptions<[IndyCredentialFormat], [V1CredentialService, V2CredentialService<[IndyCredentialFormat]>]>'.
  Types of property 'protocolVersion' are incompatible.
    Type 'string' is not assignable to type '"v1" | "v2"'.ts(2345)

If i serialise it it works fine.

const serializedOptions = {
...options,
protocolVersion: (options.protocolVersion === 'v1' ? 'v1' : 'v2') as 'v1' | 'v2',
}
const credential = await this.agent.credentials.offerCredential(serializedOptions)
TimoGlastra commented 2 years ago

You need to provide the credentials module with the correct typing to work. By default the OfferCredentialOptions interface doesn't have any formats / protocol versions. Two options.

  1. Use the agent method input (will help if the types change)

    @Post('/offer-credential')
    public async offerCredential(@Body() options: Parameters<Agent['credentials']['offerCredential']>[0]) {
    try {
      const credential = await this.agent.credentials.offerCredential(options)
      return credential.toJSON()
    } catch (error) {
      ...
    }
  2. Add the needed types to the interface

    @Post('/offer-credential')
    public async offerCredential(@Body() options: OfferCredentialOptions<[IndyCredentialFormat], [V1CredentialService, V2CredentialService]>) {
    try {
      const credential = await this.agent.credentials.offerCredential(options)
      return credential.toJSON()
    } catch (error) {
      ...
    }
TimoGlastra commented 2 years ago

However this was one of the issues I opened in TSOA as it doesn't infer the type of the interface correctly. It will infer it as:

{
  protocolVersion: string
  credentialFormats: {}
}

While it should actually be a deeply nested type. To get around this, I had to declare it like this manually:

type CredentialFormats = [IndyCredentialFormat]
type CredentialServices = [V1CredentialService, V2CredentialService]

interface OfferCredentialOptions {
  // update to ProtocolVersionType<CredentialServices> once https://github.com/hyperledger/aries-framework-javascript/pull/903 has been merged
  protocolVersion: ProtocolVersionType<CredentialFormats, CredentialServices> 
  credentialFormats: CredentialFormatPayload<CredentialFormats, 'createOffer'>
  autoAcceptCredential?: AutoAcceptCredential
  comment?: string
}
janrtvld commented 2 years ago

I'm getting the following error when using the OfferCredentialOptions you've described:

{
  "message": "something went wrong",
  "error": {
    "message": "No credential service registered for protocol version V1",
    "name": "AriesFrameworkError",
    "stack": "AriesFrameworkError: No credential service registered for protocol version V1\n    at CredentialsModule.getService (/Users/jan/Developer/aries-framework-javascript-ext/node_modules/@aries-framework/core/src/modules/credentials/CredentialsModule.ts:135:13)\n    at CredentialsModule.offerCredential (/Users/jan/Developer/aries-framework-javascript-ext/node_modules/@aries-framework/core/src/modules/credentials/CredentialsModule.ts:253:26)\n    at CredentialController.offerCredential (/Users/jan/Developer/aries-framework-javascript-ext/packages/rest/src/controllers/credentials/file:/Users/jan/Developer/aries-framework-javascript-ext/packages/rest/src/controllers/credentials/CredentialController.ts:152:26)"
  }
}

Payload:

{
   "connectionId":"2aecf74c-3073-4f98-9acb-92415d096834",
   "protocolVersion":"V1",
   "credentialFormats":{
      "indy":{
         "credentialDefinitionId":"q7ATwTYbQDgiigVijUAej:3:CL:351756:string",
         "attributes":[
            {
               "name":"string",
               "value":"test"
            }
         ]
      }
   }
}

Same error if i put in V2. Any idea where this is coming from?

TimoGlastra commented 2 years ago

it should be v1 ;)