Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verifiable Credentials JSON Schema Validation - Custom Errors #9

Open
tsnyder-gs1us opened this issue Jun 4, 2024 · 5 comments
Open

Comments

@tsnyder-gs1us
Copy link

Is it possible to use custom JSON Schema error messages? Here is an example JSON Schema with error messages.

const schema = {
  type: "object",
  required: ["foo", "bar"],
  properties: {
    foo: {type: "integer"},
    bar: {type: "string"},
  },
  errorMessage: {
    type: "should be an object", // will not replace internal "type" error for the property "foo"
    required: {
      foo: 'should have an integer property "foo"',
      bar: 'should have a string property "bar"',
    },
  },
}

Looks, like custom error messages only work when the ajv-errors: https://ajv.js.org/packages/ajv-errors.html is used.

Have you considered adding custom error message support to the VC library?

@OR13
Copy link
Sponsor Member

OR13 commented Jun 12, 2024

You can see the default JSON Schema validation error messages here:
#10

The default JSON Schema version is 2020 and the JSON Schema formats backage (that understands dates and uri's) is included by default.

@OR13
Copy link
Sponsor Member

OR13 commented Jun 12, 2024

Example of a test asserting a schema validation error for an unexpected property:

const validation1 = await validator.validate({
    type: "application/vc+ld+json+jwt",
    content: issued,
  });
  expect(validation1.valid).toBe(false);
  expect(validation1.schema).toEqual({
    "https://vendor.example/api/schemas/product-passport": {
      "valid": false,
      "errors": [
        {
          "instancePath": "/credentialSubject",
          "schemaPath": "#/properties/credentialSubject/additionalProperties",
          "keyword": "additionalProperties",
          "params": {
            "additionalProperty": "unexpectedProperty"
          },
          "message": "must NOT have additional properties"
        }
      ]
    }
  })

@OR13
Copy link
Sponsor Member

OR13 commented Jun 12, 2024

Keep in mind that the media types this library supports will likely change, based on this:

https://mailarchive.ietf.org/arch/msg/media-types/KbAgK9iEwLTILvubXD4fQRHMFho/

@OR13
Copy link
Sponsor Member

OR13 commented Jun 12, 2024

PR #10 adds support for custom errors in AJV, for example:

 const validator = await transmute.validator({
    resolver: {
      resolve: async ({ id, type, content }) => {
        // Resolve external resources according to verifier policy
        // In this case, we return inline exampes...
        if (id === `${baseURL}/schemas/product-passport`) {
          return {
            type: `application/schema+json`,
            content: transmute.text.encoder.encode(`
{
  "$id": "${baseURL}/schemas/product-passport",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Example JSON Schema",
  "description": "This is a test schema",
  "type": "object",
  "properties": {
    "credentialSubject": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "degree": {
          "type": "object"
        }
      },
      "additionalProperties": false,
      "errorMessage": {
        "additionalProperties": "🔥 This is a custom error message for extra properties 🔥"
      }
    }
  }
}
            `),
          };
        }

        if (content != undefined && type === `application/vc+ld+json+jwt`) {
          const { kid } = jose.decodeProtectedHeader(
            transmute.text.decoder.decode(content)
          );
          // lookup public key on a resolver
          if (kid === `did:example:123#key-42`) {
            return {
              type: "application/jwk+json",
              content: publicKey,
            };
          }
        }
        throw new Error("Resolver option not supported.");
      },
    },
  });
  const validation1 = await validator.validate({
    type: "application/vc+ld+json+jwt",
    content: issued,
  });
  expect(validation1.valid).toBe(false);
  expect(validation1.schema).toEqual({
    "https://vendor.example/api/schemas/product-passport": {
      "valid": false,
      "errors": [
        {
          "instancePath": "/credentialSubject",
          "schemaPath": "#/properties/credentialSubject/errorMessage",
          "keyword": "errorMessage",
          "params": {
            "errors": [
              {
                "instancePath": "/credentialSubject",
                "schemaPath": "#/properties/credentialSubject/additionalProperties",
                "keyword": "additionalProperties",
                "params": {
                  "additionalProperty": "unexpectedProperty"
                },
                "message": "must NOT have additional properties",
                "emUsed": true
              },
            ]
          },
          "message": "🔥 This is a custom error message for extra properties 🔥"
        }
      ]
    }
  }
  )

@OR13
Copy link
Sponsor Member

OR13 commented Jul 3, 2024

Support for this has been merged to main, but the interface has been adjusted in #13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants