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

feat: allow any credentialStatus for oa v4 #315

Conversation

nghaninn
Copy link

@nghaninn nghaninn commented Oct 4, 2024

Summary

What is the background of this pull request?

  • To remove restriction for credetialStatus v4

Changes

What are the changes made in this pull request?

  • Allowing any type for credentialStatus in oa v4.

Issues

What are the related issues or stories?

  • TradeTrust will be utilising oa v4. However we will require customisation to allow other type value for credentialStatus.
  • Note: No @context schema validation in place for external type.

Happy to have a discussion for any suggestion.

Removing restriction for credetialStatus
@nghaninn nghaninn marked this pull request as ready for review October 7, 2024 02:34
@HJunyuan
Copy link
Member

HJunyuan commented Oct 7, 2024

Hi @nghaninn ,

Thanks for the suggestion. May we clarify what are some other type values of credentialStatus you are looking at?

This is because adding z.object({ type: z.string() }).passthrough() might be too permissive, overriding our first 2 definitions (OscpResponderRevocation, RevocationStoreRevocation).

As a result, an object like this would pass incorrectly:

"credentialStatus": {
  // id: "MISSING_ID",
  type: "OpenAttestationOcspResponder"
}

The credentialStatus field is intentionally strict to ensure that all OA documents will be issued in an expected manner. OA schema should be an intentionally narrowed subset of the W3C VC data model so the OA framework knows exactly how to handle these documents end-to-end (from issuance, to verification and to rendering). Adding a generic definition would dilute the OA definition and might lead to ambiguity upon verification.

@nghaninn
Copy link
Author

nghaninn commented Oct 7, 2024

Hi @HJunyuan.

We wanted to add a new type for TransferableRecords, such that the object will be like

    credentialStatus: {
      type: "TransferableRecords",
      id: "0xb1767E3B31A286b051684CdBe0447FBd483D71A7",
      tokenNetwork: {
        chain: "Amoy",
        chainId: 80002
      },
      tokenRegistry: "0xb1767E3B31A286b051684CdBe0447FBd483D71A7"
    },

Understand the strict enforcement. Will the below worth within the constraint? with additional @context

export const TransferableRecords = z.object({
  id: z.string().url().describe("Address of smart contract "),
  type: z.literal("TransferableRecords"),
  tokenNetwork: z.object({
    chainId: z.number(),
    chain: z.string(),
  }).passthrough(),
  tokenRegistry: z.string(),
}).passthrough();

...

    credentialStatus: z.discriminatedUnion("type", [OscpResponderRevocation, RevocationStoreRevocation, TransferableRecords]).optional(),

@HJunyuan
Copy link
Member

After further discussions with the team, we would suggest the following snippet as a first solution to meet your current needs. This would continue to make it strict for known credentialStatus types that are defined by us. Otherwise, we won't enforce the schema:

const Others = z.object({ type: z.string() }).passthrough();

credentialStatus: z.discriminatedUnion("type", [
  OscpResponderRevocation,
  RevocationStoreRevocation,
  Others,
]).optional();

This discussion has also made us rethink how we can overhaul our OA library to support the issuance of more generic VCs and provide the flexibility to mix and match different securing mechanisms/render methods/etc., whether from OA or outside OA.

Here are some possible changes we are exploring. Let's start by defining:

  • OA Framework - OpenAttestation library, oa-verify, decentralised-renderer-components
  • OA Data Model v4 (OA v4) - An intentionally narrower subset of W3C's VC Data Model 2.0 that enables OA features. Documents that conform to this model are supported out of the box by the OA framework from verifying to rendering.
  • OA Feature - OA render methods/securing mechanism/identity assertion/revocation methods

Since most of our users will be using OA framework as is, we will still like to provide a high-level method to produce documents that are strictly OA data model v4:

  • createUnsignedOAv4(payload) -> unsigned-oa-v4

We do however want to support users who wish to pick only a subset of OA features, hence we are looking at something like:

  • createVC(payload) -> unsigned-generic-vc
  • addOARenderMethod(unsigned-generic-vc) -> unsigned-generic-vc-w-oa-render-method
  • addOAIdentityAssertion(unsigned-generic-vc) -> unsigned-generic-vc-w-oa-identity
  • addOACredentialStatus(unsigned-generic-vc) -> unsigned-generic-vc-w-oa-credential-status
  • etc.

And allow any W3C VC to be secured with our securing mechanism:

  • sign(unsigned-generic-vc) -> oa-signed-vc
    • takes any unsigned VC and secures it using OA's securing mechanism, this includes adding our context and types if need be and also adding the salts, etc. (what was previously done in wrapping)

We also can provide these utilities/guards:

  • isOADataModelV4(unknown) -> is oa-v4
  • isVC(unknown) -> is-vc
  • isOARenderMethod(VC) -> is-vc-with-oa-render-method
  • isOAIdentityMethod(VC) -> ...
  • isOASecureMechanism -> ...
  • isOACredentialStatus -> ...

We envision that in TrustVC:

vc = createVC(payload)
vc-with-oa-render-method = addOARenderMethod(vc)
bbs-secured-vc-with-oa-render-method = bbsSign(vc-with-oa-render-method)

We are open to suggestions.

@nghaninn
Copy link
Author

Hi @HJunyuan and team,
Thanks for taking the time to consider our requirements and for your thoroughness!
I'll update the PR with the suggested changes to make credentialStatus open to extension.


On the exploration front, we've got plans to simplify schema generation and signing with a StringBuilder-like function. Let's explore that!

@nghaninn
Copy link
Author

Hi @HJunyuan,

Pardon me, it seems like we can't have discriminatedUnion with a open type string.
e.g. type = 'OpenAttestationOcspResponder' | 'OpenAttestationRevocationStore' | string

    credentialStatus: z.discriminatedUnion("type", [OscpResponderRevocation, RevocationStoreRevocation, Others]).optional(),

This results in an error when generating the schema.

    Error: A discriminator value for key `type` could not be extracted from all schema options

An example also illustrated in this discussion. colinhacks/zod#2747


After some research, I think that union type is the best option, which leads to a simpler schema:

export const Others = z.object({ type: z.string() }).passthrough();

    credentialStatus: z.union([OscpResponderRevocation, RevocationStoreRevocation, Others]).optional(),
Schema Output
        "credentialStatus": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uri",
                  "description": "URL of the OCSP responder endpoint"
                },
                "type": {
                  "type": "string",
                  "const": "OpenAttestationOcspResponder"
                }
              },
              "required": [
                "id",
                "type"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^(0x)?[0-9a-fA-F]{40}$",
                  "description": "Ethereum address of the revocation store contract"
                },
                "type": {
                  "type": "string",
                  "const": "OpenAttestationRevocationStore"
                }
              },
              "required": [
                "id",
                "type"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                }
              },
              "required": [
                "type"
              ],
              "additionalProperties": true
            }
          ]
        },

Some Reference:
colinhacks/zod#2106
colinhacks/zod#3407 [New proposal for Union, yet to be in production]


The alternative generate rather complex schema

  1. DiscriminatedUnion or Others
    credentialStatus: z
      .discriminatedUnion("type", [OscpResponderRevocation, RevocationStoreRevocation])
      .or(Others)
      .optional(),

OR

    credentialStatus: OscpResponderRevocation
      .or(RevocationStoreRevocation)
      .or(Others)
      .optional(),
Schema Output
        "credentialStatus": {
          "anyOf": [
            {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uri",
                      "description": "URL of the OCSP responder endpoint"
                    },
                    "type": {
                      "type": "string",
                      "const": "OpenAttestationOcspResponder"
                    }
                  },
                  "required": [
                    "id",
                    "type"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^(0x)?[0-9a-fA-F]{40}$",
                      "description": "Ethereum address of the revocation store contract"
                    },
                    "type": {
                      "type": "string",
                      "const": "OpenAttestationRevocationStore"
                    }
                  },
                  "required": [
                    "id",
                    "type"
                  ],
                  "additionalProperties": false
                }
              ]
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                }
              },
              "required": [
                "type"
              ],
              "additionalProperties": true
            }
          ]
        },

Let me know your thoughts. Thanks.

@nghaninn
Copy link
Author

Hi @HJunyuan and team,

Thank you for meeting with the team. I appreciate your clarification on the constraints surrounding the OA default schema, specifically the limitation to only support two credentialStatus types for now, with a promise to revisit and potentially expand to more generic schema in the future. I also appreciate the heads up on upcoming breaking changes.

I will proceed with closing this PR for now, and I look forward to receiving more information regarding the roadmap for OA v4. This will help us make an informed decision about making TradeTrust compatible.

Please keep me updated, and thank you again for your time and consideration.

@nghaninn nghaninn closed this Oct 23, 2024
@HJunyuan
Copy link
Member

HJunyuan commented Dec 4, 2024

Hi @nghaninn and team,

We have recently raised another PR (#317) to allow both OA and W3C VCs to be digested (previously known as wrapped) or signed.

Please note the changes in the PR description such as:

  • The changes in terminologies
  • Signing will now automatically digest
  • etc.

Do give the branch a try to see if it resolves what you wanted to achieve with a custom credentialStatus object.

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

Successfully merging this pull request may close these issues.

2 participants