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

refactor: Extract verification method ID generation to a separate class #2235

Merged

Conversation

yvgny
Copy link
Contributor

@yvgny yvgny commented May 17, 2023

Currently, the strategy to generate the verification key IDs only supports an arbitrary set of DID methods, and the IDs are derived from hardcoded strings, which can be too restrictive for some use cases.

For example, if one adds DID methods (such as did:web) using third-party plugins, the current method will always raise an error. Also, one might want to have more advanced ways of deriving the verification key ID, for example when multiple verification methods are used for a given DID and are rotated regularly.

This PR allows for more flexibility by extracting the verification key ID generation to a separate class so that plugins can switch implementations depending on the needs using injection. For example, for the key rotation use-case cited above, the implementation could be switched to one that dynamically returns the correct verkey ID, even if different did methods are used. I believe this solution avoids breaking changes, and keeping the current strategy as the default one allows keeping backward compatibility.

Let me know if you have any comments/suggestions.

Comment on lines 275 to 281
verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy)
verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id)

if verification_method is None:
raise V20CredFormatError(
f"Unable to get retrieve verification method for did {issuer_id}"
)
Copy link
Contributor

@TimoGlastra TimoGlastra May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making this pluggable could work, however maybe it'd be simpler to just provide the verification_method as part of the issue credential options?

So issuer = did:example:123, verification_method is did:example:123#123.

Otherwise, I think the get_verkey_id_for_did should at least take a proof_type as input, so it can determine the correct key based on the proof type (different proof types need different keys)

I would also suggest renaming it to get_verification_method_id_for_did.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it a bit more, If we want to make this a generic / pluggable interface, I think the method should have more input so you can correctly define the verification method.

It should contain:

  • proof_type (or something like allowed_verification_method_types)
  • optional proof_purpose (so we only return a verification method that is allowed to be used for the intended purpose (assertionMethod, authentication, keyAgreement, etc..)
  • did

Also, maybe it should return an array of verification method instances itself, so the caller can determine based on the whole verification method object + can choose which one to take if there's multiple candidates (currnet callees could just take the first one)

Copy link
Contributor Author

@yvgny yvgny May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @TimoGlastra, thanks for your review.

I think making this pluggable could work, however maybe it'd be simpler to just provide the verification_method as part of the issue credential options?

Indeed, I inadvertently removed this part. I added this back again, so now during issuing it'll try to use the given verification_method (if provided) and will otherwise default to generating it dynamically. However, for other protocols such as credential presentation, I think we still need a way to dynamically produce the verification_method ID, as the ACA-Py instance of the presenter could be set to automatically accept presentation requests, in which cases the ID cannot be given in the options.

I would also suggest renaming it to get_verification_method_id_for_did.

Done

Also, maybe it should return an array of verification method instances itself, so the caller can determine based on the whole verification method object + can choose which one to take if there's multiple candidates (currnet callees could just take the first one)

I'm not sure I understand this: from my limited understanding of how ACA-Py works, only a single verification key is used at a time and is stored in the verkey field of a DIDInfo record returned by using get_local_did, or others. The pluggable method this PR introduce is a way to produce the verification method ID associated with this verkey, as ACA-Py doesn't store it in this DIDInfo record. Are you suggesting that we remove this verkey field, and instead use this newly added class when querying for a verification method? And then it would return an array of verification method instances with each verification method instance containing a verkey plus its ID?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On your last point. Yeah what I was saying doesn't fully make sense, as we already have the public key (verkey) and the did, we just want to know the verification method id (so did + # part). I was thinking more of determining the best

However, I think we should provide more than just the issuer_id/did to determine the verification_method_id. In ACA-Py's current model dids only have a single key, but as we already know the public key, the proof type, etc.. I think it would be good to pass it, so once ACA-Py starts supporitng multiple keys for a did, this interface won't have to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I think we should provide more than just the issuer_id/did to determine the verification_method_id. In ACA-Py's current model dids only have a single key, but as we already know the public key, the proof type, etc.. I think it would be good to pass it, so once ACA-Py starts supporitng multiple keys for a did, this interface won't have to change.

Done

dbluhm
dbluhm previously approved these changes May 27, 2023
Copy link
Contributor

@dbluhm dbluhm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interested to see @TimoGlastra's response to @yvgny's questions in the comments but I'm happy with these changes overall

@@ -270,10 +272,17 @@ async def _get_suite_for_detail(
)

did_info = await self._did_info_for_did(issuer_id)
verification_method = verification_method or self._get_verification_method(
issuer_id
verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be Base instead of Default, as you can override the default with a custom one? It would't make sense to make the injection token the default implementation IMO.

Copy link
Contributor Author

@yvgny yvgny Jun 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimoGlastra So the injection token should be renamed to BaseVerificationKeyStrategy? This would mean the base (abstract) class would be named BaseVerificationKeyStrategy and the default implementation DefaultVerificationKeyStrategy, is this correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yvgny that is correct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 275 to 281
verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy)
verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id)

if verification_method is None:
raise V20CredFormatError(
f"Unable to get retrieve verification method for did {issuer_id}"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On your last point. Yeah what I was saying doesn't fully make sense, as we already have the public key (verkey) and the did, we just want to know the verification method id (so did + # part). I was thinking more of determining the best

However, I think we should provide more than just the issuer_id/did to determine the verification_method_id. In ACA-Py's current model dids only have a single key, but as we already know the public key, the proof type, etc.. I think it would be good to pass it, so once ACA-Py starts supporitng multiple keys for a did, this interface won't have to change.

@swcurran swcurran requested a review from andrewwhitehead May 29, 2023 21:26
@swcurran
Copy link
Contributor

Please up date the branch to include changes to main.

@swcurran
Copy link
Contributor

swcurran commented Jun 9, 2023

Ready to merge, but @dkulic or @dbluhm — could you answer @TimoGlastra ’s last question:

However, I think we should provide more than just the issuer_id/did to determine the verification_method_id. In ACA-Py's current model dids only have a single key, but as we already know the public key, the proof type, etc.. I think it would be good to pass it, so once ACA-Py starts supporitng multiple keys for a did, this interface won't have to change.

@dbluhm dbluhm dismissed their stale review June 9, 2023 17:57

I'd like to see Timo's points addressed

@yvgny yvgny requested review from dbluhm and TimoGlastra June 13, 2023 11:25
@swcurran
Copy link
Contributor

Please update to the base branch and we're ready to merge this one. It will be included in 0.8.2.

@dbluhm dbluhm enabled auto-merge June 13, 2023 16:58
@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@dbluhm dbluhm merged commit b59b405 into openwallet-foundation:main Jun 13, 2023
@yvgny yvgny deleted the custom_default_verkey_strategy branch June 14, 2023 08:21
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.

5 participants