piprate / json-gold

A JSON-LD processor for Go
Apache License 2.0
259 stars 30 forks source link

Getting Cryptic "Invalid Local Context" Error While Normalizing JSON-LD Document #56

Closed LuisOsta closed 2 years ago

LuisOsta commented 2 years ago

Summary We are working in the digital identity space. And right now trying to use the json-gold library to create some VerifiablePresentation json-ld documents. But the normalization fails due to a cryptic error.

This is the exact error that we're getting:

 Expected no error, got invalid local context: [https://www.w3.org/2018/credentials/v1]

It seems that the error comes from here - https://github.com/piprate/json-gold/blob/33b90c4ca86c41ea5c28cfd1ea0a5fd314c91848/ld/context.go#L216.

Though I haven't been able to parse what exactly is going in the context.

Do y'all have a sense of what we are providing that is incorrect relative to what json-gold expects? The strange thing is that the JSON-LD Playground seems to have no issues parsing the document

Extra Context: This is the JSON-LD Document in question:

{
   "@context":[
      "https://www.w3.org/2018/credentials/v1"
   ],
   "type":[
      "VerifiablePresentation"
   ],
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/citizenship/v1"
         ],
         "credentialSubject":{
            "birthCountry":"Bahamas",
            "birthDate":"1958-07-17",
            "commuterClassification":"C1",
            "familyName":"SMITH",
            "gender":"Male",
            "givenName":"JOHN",
            "id":"did:example:b34ca6cd37bbf23",
            "image":"data:image/png;base64,iVBORw0KGgo...kJggg==",
            "lprCategory":"C09",
            "lprNumber":"999-999-999",
            "residentSince":"2015-01-01",
            "type":[
               "PermanentResident",
               "Person"
            ]
         },
         "description":"Government of Example Permanent Resident Card.",
         "expirationDate":"2029-12-03T12:19:52Z",
         "id":"https://issuer.oidp.uscis.gov/credentials/83627465",
         "identifier":"83627465",
         "issuanceDate":"2019-12-03T12:19:52Z",
         "issuer":"did:example:28394728934792387",
         "name":"Permanent Resident Card",
         "proof":{
            "created":"2020-01-30T03:32:15Z",
            "jws":"eyJhbGciOiJFZERTQSIsI...wRG2fNmAx60Vi4Ag",
            "proofPurpose":"assertionMethod",
            "type":"Ed25519Signature2018",
            "verificationMethod":"did:example:28394728934792387#keys-7f83he7s8"
         },
         "type":[
            "VerifiableCredential",
            "PermanentResidentCard"
         ]
      }
   ]
}

And this is how we are trying to normalize it:

    vp := map[string]interface{}{
        "@context":             []string{"https://www.w3.org/2018/credentials/v1"},
        "type":                 []string{"VerifiablePresentation"},
        "verifiableCredential": converted_creds,
    }

    proc := ld.NewJsonLdProcessor()
    options := ld.NewJsonLdOptions("")
    options.Format = NORMALIZATION_FORMAT
    options.Algorithm = NORMALIZATION_ALGO
    normalized, err := proc.Normalize(vp, options)
kazarena commented 2 years ago

@LuisOsta, apologies for the late response.

I believe the problem lies in the types you use in your document. JSON-goLD expects any nested structures to be map[string]interface{} and any included arrays to be []interface{}. The example you gave uses []string. The example below works:

package main

import (
    "github.com/piprate/json-gold/ld"
)

func main() {
    vp := map[string]interface{}{
        "@context": []interface{}{
            "https://www.w3.org/2018/credentials/v1",
        },
        "type": []interface{}{
            "VerifiablePresentation",
        },
        "verifiableCredential": []interface{}{
            map[string]interface{}{
                "@context": []interface{}{
                    "https://www.w3.org/2018/credentials/v1",
                    "https://w3id.org/citizenship/v1",
                },
                "credentialSubject": map[string]interface{}{
                    "birthCountry":           "Bahamas",
                    "birthDate":              "1958-07-17",
                    "commuterClassification": "C1",
                    "familyName":             "SMITH",
                    "gender":                 "Male",
                    "givenName":              "JOHN",
                    "id":                     "did:example:b34ca6cd37bbf23",
                    "image":                  "data:image/png;base64,iVBORw0KGgo...kJggg==",
                    "lprCategory":            "C09",
                    "lprNumber":              "999-999-999",
                    "residentSince":          "2015-01-01",
                    "type": []interface{}{
                        "PermanentResident",
                        "Person",
                    },
                },
                "description":    "Government of Example Permanent Resident Card.",
                "expirationDate": "2029-12-03T12:19:52Z",
                "id":             "https://issuer.oidp.uscis.gov/credentials/83627465",
                "identifier":     "83627465",
                "issuanceDate":   "2019-12-03T12:19:52Z",
                "issuer":         "did:example:28394728934792387",
                "name":           "Permanent Resident Card",
                "proof": map[string]interface{}{
                    "created":            "2020-01-30T03:32:15Z",
                    "jws":                "eyJhbGciOiJFZERTQSIsI...wRG2fNmAx60Vi4Ag",
                    "proofPurpose":       "assertionMethod",
                    "type":               "Ed25519Signature2018",
                    "verificationMethod": "did:example:28394728934792387#keys-7f83he7s8",
                },
                "type": []interface{}{
                    "VerifiableCredential",
                    "PermanentResidentCard",
                },
            },
        },
    }

    proc := ld.NewJsonLdProcessor()
    options := ld.NewJsonLdOptions("")
    options.Format = "application/n-quads"
    options.Algorithm = "URDNA2015"

    normalizedTriples, err := proc.Normalize(vp, options)
    if err != nil {
        panic(err)
    }

    print(normalizedTriples.(string))
}
LuisOsta commented 2 years ago

Thanks! Do you know why JSON-goLD requires map[string]interface{} and []interface{}

kazarena commented 2 years ago

@LuisOsta the library consumes JSON in its most generic form, as if it was unmarshalled into an interface{} variable. Hence it needs to be a combination of []interface{}, map[string]interface{} and singular values. Supporting all kinds of in-between types, like []string or []int or structs would complicate processing substantially.