digitalcredentials / vc

JavaScript implementation of W3C Verifiable Credentials standard
BSD 3-Clause "New" or "Revised" License
4 stars 4 forks source link

The property in the input was not defined in the context #11

Closed fabrii closed 1 year ago

fabrii commented 1 year ago

Hi!

We are having an error when signing a VP.

Error: { [Error: The property "category" in the input was not defined in the context.] line: 1281, column: 101, sourceURL: 'index.android.bundle' }

Presentation:

{
   "@context":[
      "https://www.w3.org/2018/credentials/v1"
   ],
   "type":[
      "VerifiablePresentation"
   ],
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://xxx.com/api/contexts/transport/driverlicense-v1",
            "https://w3id.org/security/suites/jws-2020/v1"
         ],
         "type":[
            "VerifiableCredential",
            "DriverLicenseCredential"
         ],
         "id":"urn:uuid:a3af8a05-814e-4255-b427-641bdf541f12",
         "issuer":"urn:oid:2.16.858.0.0.0.3.0",
         "issuanceDate":"2023-05-03T20:08:01Z",
         "expirationDate":"2024-05-03T20:08:01Z",
         "credentialSubject":{
            "id":"did:key:z6Mkh4Nn9RWwp1gcNGJSkAaW58F9mAsc979pj5ET4QXu85TH",
            "name":"X",
            "category":"B",
            "observations":"X",
            "image":"urn:uuid:883d1c4a-7cd5-4cd4-80dd-10bf5126b2ff"
         },
         "credentialStatus":{
            "type":"CustomStatusEntry",
            "id":"urn:uuid:a3af8a05-814e-4255-b427-641bdf541f12"
         },
         "proof":{
            "type":"JsonWebSignature2020",
            "created":"2023-05-03T20:08:01Z",
            "proofPurpose":"assertionMethod",
            "verificationMethod":"urn:oid:2.16.858.0.0.0.3.0#1",
            "jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJSUzI1NiJ9..KEAl0GIz-8nmMeytHqPK9HvoHDqrv2zGgidLBYLzNzXr82_4JNo5cNE0n-Lq9Na12wkqNkDEi3t-nixt_2G8Lj5tqMQS5ycr3_AFoOXZsiYOY6gMHUJckL0MJdUb_5rUhj-LEtESUs8r-tibdhZdbDO4Cu5nZXmNqSPqTY0Ilg6SwK08XymUlCRbj22ATNPcDHBagq_XmS_q_IZMVRIuDxHlmGpCnF78mEAof5QU3DKyPSpxryoox-SH0p9csDDz8cWcWWElO1Fvy3HXjAF3T3s38D12T3mpU0ZIzEeo-h2zL83xD7SxBCokrEomDNIC2C2uqoo8w1ExjHHJ8GxFfQ"
         }
      }
   ],
   "holder":"did:key:z6Mkh4Nn9RWwp1gcNGJSkAaW58F9mAsc979pj5ET4QXu85TH"
}

Method used for signing:

const verifiablePresentation = await vc.signPresentation({
      presentation,
      suite,
      challenge,
      documentLoader
    })

Context loaded as follow:

const loader = securityLoader()
loader.addStatic('https://xxx.com/api/contexts/transport/driverlicense-v1', obj);
loader.addStatic('https://w3id.org/security/suites/jws-2020/v1', jws2020)
const documentLoader = loader.build()

Where obj is:

{
    "@context": {
        "@version": 1.1,
        "@protected": true,
        "id": "@id",
        "type": "@type",
        "DriverLicenseCredential": {
            "@id": "https://cv-registry.gub.uy/docs/credentials/driverlicensecredential",
            "@context": {
                "@version": 1.1,
                "@protected": true,
                "id": "@id",
                "type": "@type",
                "dlcred": "https://cv-registry.gub.uy/docs/credentials/driverlicensecredential#",
                "name": "dlcred:name",
                "image": "dlcred:image",
                "category": "dlcred:category",
                "observations": "dlcred:observations"
            }
        } 
    }
}

Do you have any insight on what might be going on?

Thanks you !

dmitrizagidulin commented 1 year ago

Hmmm that's interesting! On a first quick glance, I don't see anything wrong with the context. We'll need to do some digging to see why.

Meanwhile, just to get you unstuck, feel free to add a @vocab term to your context.

It's a temporary measure (it basically says, if you encounter any undefined terms, assume they're in the following url), but should let you proceed while we investigate. So, your VC context would look like:

         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://xxx.com/api/contexts/transport/driverlicense-v1",
            "https://w3id.org/security/suites/jws-2020/v1",
            {
              "@vocab": ""https://xxx.com/api/contexts/transport/driverlicense-v1#"
            }
         ],
dmitrizagidulin commented 1 year ago

@fabrii oh, just as a heads up, you'll probably need to add urn:oid:2.16.858.0.0.0.3.0 to your documentLoader too. I don't think that's what's causing the current error, but I suspect you'll run into that as well.

dmitrizagidulin commented 1 year ago

@fabrii Ah, ok, yeah, so, the issue is that category is defined under the type DriverLicenseCredential. However, it appears in the credentialSubject, not the top level credential itself. (The type structure in JSON-LD is not "recursive").

So basically, move the definitions of the fields appearing in credentialSubject out of the DriverLicenseCredential type in the context (have them be at the top level).

dmitrizagidulin commented 1 year ago

@fabrii Your other option is to define a type for the credentialSubject. So like:

{
   "@context":[
      "https://www.w3.org/2018/credentials/v1"
   ],
   "type":[
      "VerifiablePresentation"
   ],
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://xxx.com/api/contexts/transport/driverlicense-v1",
            "https://w3id.org/security/suites/jws-2020/v1"
         ],
         "type":[
            "VerifiableCredential",
            "DriverLicenseCredential"
         ],
         "id":"urn:uuid:a3af8a05-814e-4255-b427-641bdf541f12",
         "issuer":"urn:oid:2.16.858.0.0.0.3.0",
         "issuanceDate":"2023-05-03T20:08:01Z",
         "expirationDate":"2024-05-03T20:08:01Z",
         "credentialSubject":{
            "type": "Person",
            "id":"did:key:z6Mkh4Nn9RWwp1gcNGJSkAaW58F9mAsc979pj5ET4QXu85TH",
            "name":"X",
            "category":"B",
            "observations":"X",
            "image":"urn:uuid:883d1c4a-7cd5-4cd4-80dd-10bf5126b2ff"
         },
         "credentialStatus":{
            "type":"CustomStatusEntry",
            "id":"urn:uuid:a3af8a05-814e-4255-b427-641bdf541f12"
         },
         "proof":{ ... }
      }
   ],
   "holder":"did:key:z6Mkh4Nn9RWwp1gcNGJSkAaW58F9mAsc979pj5ET4QXu85TH"
}

and the context would be:

{
    "@context": {
        "@version": 1.1,
        "@protected": true,
        "id": "@id",
        "type": "@type",
        "Person": {
            "@id": "https://xxx.com/api/contexts/transport/driverlicense-v1#Person",
            "@context": {
                "@version": 1.1,
                "@protected": true,
                "id": "@id",
                "type": "@type",
                "dlcred": "https://cv-registry.gub.uy/docs/credentials/driverlicensecredential#",
                "name": "dlcred:name",
                "image": "dlcred:image",
                "category": "dlcred:category",
                "observations": "dlcred:observations"
            }
        } 
    }
}

That sort of thing.

fabrii commented 1 year ago

Hi @dmitrizagidulin. We moved the attributes declaration to the top level and worked perfectly!

{
    "@context": {
        "@version": 1.1,
        "@protected": true,
        "id": "@id",
        "type": "@type",
        "dlcred": "https://cv-registry.gub.uy/docs/credentials/driverlicensecredential#",
        "DriverLicenseCredential": "dlcred:DriverLicenseCredential",
        "name": "dlcred:name",
        "idUruguay": "dlcred:iduruguay",
        "image": "dlcred:image",
        "category": "dlcred:category",
        "observations": "dlcred:observations"
    }
}

Regarding adding urn:oid:2.16.858.0.0.0.3.0 to the documentLoader, we got no error if not added. Can you explain me why might be necessary, and what do I have to declare? How is the json format?

Thanks a lot!

dmitrizagidulin commented 1 year ago

@fabrii - you may not need to add the OIDs to the documentLoader for signing, but I think you'll need it for verification. (The verifier will have to resolve/dereference the verificationMethod etc).

fabrii commented 1 year ago

Hi @dmitrizagidulin. Can you please point me in the right direction on how to implement a documentLoader that loads a VerificationMethod like mine? (urn:oid:2.16.858.0.0.0.3.0#1).

In our case, we have a central registry with an API exposing the issuers public keys in JWK format. Those keys can be RSA or ED25519. I guess we have to code a method that invokes that API, obtains de key, and somehow adds it to the document loader. I cannot figure out the method interface. I think we should extend https://github.com/digitalcredentials/security-document-loader, but I cannot find any example for my case.

Meanwhile, I was trying to load the key in the suite.

Thanks in advance!

dmitrizagidulin commented 1 year ago

Hi @fabrii -- sure thing. (And yes, we should add this to the readme).

You'll want to add a custom protocol loader (look at how the optional http loader is added, here https://github.com/digitalcredentials/security-document-loader/blob/main/src/documentLoader.ts#L120 ). So overall, your code will be something like:

import { securityLoader } from '@digitalcredentials/security-document-loader'

const urnResolver = {
  /**
   * @param {object} options - Options hashmap.
   * @param {string} options.url - Document URL (here `urn:...` key id)
   * @returns {Promise<object>} - Resolves with key object document.
   */
  async get(params: Record<string, string>): Promise<unknown> {
    let keyObject
    try {
      keyObject = await fetchMyKeyFromAPI(params.url)
    } catch(e) {
      throw new Error('NotFoundError')
    }

    return keyObject
  }
};

securityLoader.setProtocolHandler({protocol: 'urn', handler: urnResolver})

const documentLoader = securityLoader().build()
fabrii commented 1 year ago

Thanks for the example @dmitrizagidulin .

I have one more doubt. Do the "keyObject" needs to be of type LdKeyPair (https://github.com/digitalcredentials/crypto-ld/blob/main/lib/LDKeyPair.js)?

EDIT:

Reading the code, I can see the following:

declare interface IDocumentLoaderResult {
  contextUrl?: string;
  documentUrl?: string;
  document: any;
}

Digging a big further, I believe I should do it similar as this:

{
  documentUrl: "urn:oid:2.16.858.0.0.0.3.0#1",
  contextUrl: "https://w3id.org/security/suites/ed25519-2020/v1",
  document: {
   '@context': "https://w3id.org/security/suites/ed25519-2020/v1",
    type: "Ed25519VerificationKey2020",
    id: "urn:oid:2.16.858.0.0.0.3.0#1",
    controller: "urn:oid:2.16.858.0.0.0.3.0",
    publicKeyMultibase: "zFj5p9C2Sfqth6g6DEXtw5dWFqrtpFn4TCBBPJHGnwKzY",
  }
}

EDIT2:

The above (EDIT1) object throws and error (type undefined). The keyObject has to be like this:

{
   '@context': "https://w3id.org/security/suites/ed25519-2020/v1",
    type: "Ed25519VerificationKey2020",
    id: "urn:oid:2.16.858.0.0.0.3.0#1",
    controller: "urn:oid:2.16.858.0.0.0.3.0",
    publicKeyMultibase: "zFj5p9C2Sfqth6g6DEXtw5dWFqrtpFn4TCBBPJHGnwKzY",
}

With this, the error changes to: "message": "Verification method \"urn:oid:2.16.858.0.0.0.3.0#1\" not authorized by controller for proof purpose \"assertionMethod\".",

Tracked the error to this file: https://github.com/digitalcredentials/jsonld-signatures/blob/67b540235cf966569ca9180f29da1abb9a8c4f5f/lib/purposes/ControllerProofPurpose.js#L120

Do we also need to declare the controller in the securityLoader? Could not find format examples.

EDIT3:

Adding this to the loader worked!

{
    "@context": [
        "https://www.w3.org/ns/did/v1",
        "https://w3id.org/security/suites/ed25519-2020/v1"
    ],
    "id": "urn:oid:2.16.858.0.0.0.3.0",
    "assertionMethod": [{
        "id": "urn:oid:2.16.858.0.0.0.3.0#1",
        "type": "Ed25519VerificationKey2020",
        "controller": "urn:oid:2.16.858.0.0.0.3.0",
        "publicKeyMultibase": "zFj5p9C2Sfqth6g6DEXtw5dWFqrtpFn4TCBBPJHGnwKzY"
    }]
}

loader.addStatic('urn:oid:2.16.858.0.0.0.3.0', json)