When should did parameters be dropped?
Consider:
did:example:123?q1=1&q2=2#fragment => did document.
should didDocument.id === did:example:123?q1=1&q2=2 or did:example:123 ?
this is particularly concerning for interactions with the initial-state parameter used by sidetree and didcomm... if its dropped the URI becomes unresolvable, so that client would need to store that somewhere...
To add further detail to this issue, calling to an ion based sidetree node with the following request
curl http://localhost:3000/identifiers/did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A?-ion-initial-state=eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0 -i
Results in
{
"@context":"https://www.w3.org/ns/did-resolution/v1",
"didDocument":{
"id":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A?-ion-initial-state=eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0",
"@context":[
"https://www.w3.org/ns/did/v1",
{
"@base":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A?-ion-initial-state=eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0"
}
]
},
"methodMetadata":{
"recoveryCommitment":"EiDfGi474ij49Vq_yiKG1q8oC1jH_BFLfH27BIQKUnTBTw"
}
}
When I would have assumed the following result being sufficient
{
"@context":"https://www.w3.org/ns/did-resolution/v1",
"didDocument":{
"id":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A",
"@context":[
"https://www.w3.org/ns/did/v1",
{
"@base":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A"
}
]
},
"methodMetadata":{
"recoveryCommitment":"EiDfGi474ij49Vq_yiKG1q8oC1jH_BFLfH27BIQKUnTBTw"
}
}
The group feels that better language around this topic should be added to the spec.
We could say that DID URLs MAY resolve to DID Documents for resolver-parsed parameters and fragments, but DID Documents MUST contain a DID in the id property. Does that sound right @OR13 @tplooker ?
Per the current spec, the above did:ion Sidetree example is not a valid DID document, since the value of id MUST be a DID, not a DID URL (see section DID Subject in the spec). Personally, I don't think anything should be changed about this, except perhaps explain it a bit better.
I agree, we should provide an example (not ION, but similar structure) and a note.
Needs PR to add explicit warning about how DID params are dropped from the did document and may be present in resolver meta data.... needs PR.
Assign me.
Still need to generate PR for this, sorry for the delay :/
If we don't get a PR for this before we enter CR, this issue will be deferred to the next version of the specification. @csuwildcat, you have 30 days to generate a PR for this issue.
I worry that folks don't realize how this impacts Verifiable Credentials....
this means that when you resolve:
"did:example:123?version-id=1#key-0"
and you get back a did document with verificationMethod:
"did:example:123#key-0"
You will need to do the following:
for JWS, you can just "find the public key bytes and check the signature.... "
for linked data proofs, you will need to apply post resolver middleware that "inserts the version" IF the original signature was over the version, OR you will have to be careful about checking the credential...
cc @dlongley
I worry that if we are not explicit here, one of the key features of the did spec will be implemented very differently by DID Methods, leading to broken verifiable credentials.
should didDocument.id === did:example:123?q1=1&q2=2 or did:example:123 ?
The second value... that is did:example:123 -- what am I missing?
The signed VC can have a verificationMethod of did:example:123?q1=1&q2=2 ... and that's what's signed over via the LD Signature... so when you dereference did:example:123?q1=1&q2=2 you get a DID Document that contains something with that ID in there... or a fragment identifier.
Perhaps there is some algorithm somewhere in some spec that is causing an issue here?
@msporny its rather that there is no algorithm in ANY spec, that handles this properly... including the VC Data Model (does not elaborate on query parameters) and JWT/JWS (which does not understand dereferencing).
Best thing for developers would be a set of "Good" and "Bad" example using query params in the verification method and the vc data model.
Best thing for developers would be a set of "Good" and "Bad" example using query params in the verification method and the vc data model.
I'd be supportive of this.
The right place to do that might be the Linked Data Security specs (which is really unfortunate, because they are not normative in any way). We could put an appendix in the DID Core spec on the topic, it would be a bit weird and out of place, but at least it would be there. We could put it in the implementation guide, but I expect that thing won't be done by REC.
I think I'd rather document this in the Linked Data Proofs spec in the processing of the verificationMethod algorithm... but then went to look at the latest spec text: https://w3c-ccg.github.io/ld-proofs/#proof-verification-algorithm ... which is just awful, everything is terrible.
I think this belongs in did core, since did core is defining dereferencing... we either define how parameters are or are not present in the resulting documents... or we stop trying to define dereferencing... the work needs to be done in did core, or its like saying we support HTTP GET, but don't define the response format.
I think this belongs in did core, since did core is defining dereferencing
The idea was that DID Core would define abstract functions with the inputs and outputs of resolving and dereferencing, and that DID Resolution would define algorithms that implement those abstract functions, answering questions such as:
- How are DID URL parameters processed (e.g.
version-id), how do they influence the outputs. - How are input metadata properties processed (e.g.
accept), see https://github.com/w3c/did-core/issues/417.
There is such an early algorithm in DID Resolution (see here), but admittedly this is not in sync with DID Core and needs to be updated.
@peacekeeper regarding query parameters.... they are NOT allowed in the id field of a did document.
Are they allowed in the id field of a verificationMethod?
Are they allowed in the
idfield of a verificationMethod?
@OR13 yes the id of a verification method could be a DID URL such as did:example:123?version-id=1#key-0.
The spec currently says:
The value of the id property for a verification method MUST be a URI.
Perhaps we should add the following?
The value of the id property for a verification method MUST be a URI and MAY be a DID URL.
Or even SHOULD be a DID URL?
@dlongley @peacekeeper @msporny @dhh1128 @kdenhartog This is one of the most important parts of the whole specification.
It's the link between did core and all assertion formats that use identifiers, including JWT, VCs, ZKPs....
Its relevant too HTTP Signatures, DIDComm and many other areas....
Whatever rules we have here will shape how useful DIDs are in those other areas.
If we are saying that a verificationMethod.id MAY be a DID URL that contains query parameters, it raises questions about how they are related the DID URL that is input to dereferencing.
I think there are a number of assumptions developers will have, which I am not sure this WG as fully considered:
Dereferencing from aJWT.kid=did:example:123?version=52#primary-signing-key, I expect a VM with id=did:example:123?version=52#primary-signing-key to be present in the did document. (query params are reflected in verificationMethod id).
However, this is NOT ALLOWED per the spec today when combined with relative refs:
https://w3c.github.io/did-core/#relative-did-urls
didDocument.id cannot include the query params, so relative refs cannot be combined with query params of any format.
This kind of ambiguity is likely to cause serious security / interop issues for anyone using query params.
I would go so far as to say, its very dangerous for us to not have some concrete examples of VCs / JWTs issued from DID URLs in the spec, or language in the spec that clearly explains that they are not allowed.
I'm not feeling confident that I grasp all the implications here. I think the sentence that I most need to understand is this one from Orie:
"didDocument.id cannot include the query params, so relative refs cannot be combined with query params of any format."
I THINK I'm totally comfortable with that, but I may be missing something.
My own plans have always been that the id property of anything in a DID document is either a pure DID (as in the case of didDocument.id), or is a relative reference like #key1. I have never intended for id properties to include parameters anywhere. But perhaps what Orie is pointing out is that the possibility for that has not been carefully eliminated by spec language, and because it exists, there is ambiguity? I admit that I would scratch my head a bit about what it would mean to try to combine parameters from DID URLs with fragments.
Or is there some use case for DIDComm where it is important to have DID URLs as the value of an id property somewhere?
If you are using JSON-LD, the identifiers from the credential are expected to match the document returned by the resolver... JSON-LD has a whole section of the spec dedicated to handling "documentLoaders" of which DID resolvers are related... for JWTs or other ZKP formats, there is not always an assumption of "documentLoaders" and getting to "public key bytes / verification method details" may not be formally defined anywhere.... for example, OIDC defines this process for JWT id_token, but the same logic (using well known URLs) does not work for arbitrary JWTs.... its undefined behavior.
Essentially the DID Spec needs to comment on the following:
{
"id": "did:key:zXwpDqA...",
"verificationMethod": [
{
"id": "#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
}
}
],
...
vs
{
"@context": [
"https://www.w3.org/ns/did/v1",
],
"id": "did:key:zXwpDqA...", // query not allowed here....
"verificationMethod": [
{
"id": "did:example:123?version=52#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
}
}
],
...
vs
{
"@context": [
"https://www.w3.org/ns/did/v1",
{
"@base": "did:key:zXwpDqAV...?version=52"
}
],
"id": "did:key:zXwpDqA...", // query not allowed here....
"verificationMethod": [
{
"id": "did:example:123?version=52#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
}
}
],
...
The reason being that the following URIs are all different, and signing over them / derefrencing them in different ways could lead to interop failures:
- did:example:123
- did:example:123#key-456
- did:example:123?version=0#key-456
- #key-456
If your did document uses relative refs, verificationMethod.id can only be of the form: did:example:123?version=0#key-456 or #key-456
If you don't use relative refs, you can have the following:
{
"id": "did:key:zXwpDqA...",
"verificationMethod": [
{
"id": "did:key:zXwpDqA...#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": { ...}
},
{
"id": "did:key:zXwpDqA...?version=52#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": { ...}
},
{
"id": "did:key:zXwpDqA...?version=51#zXwpDq...",
"type": "JsonWebKey2020",
"controller": "did:key:zXwpDqAV1ME...",
"publicKeyJwk": { ...}
},
],
...
Since did:key:zXwpDqA...?version=51#zXwpDq... != did:key:zXwpDqA...?version=52#zXwpDq... != did:key:zXwpDqA...#zXwpDq...
The are technically unique...
The DID core spec already recommends using verificationMethod.id as the kid field in JOSE... in Linked Data Proof, the value in the proof is expected to be present in the resolved did document... for example:
"proof": {
"type": "EcdsaSecp256k1Signature2019",
"created": "2020-11-16T17:42:52Z",
"verificationMethod": "did:example:123#key-0",
"proofPurpose": "assertionMethod",
"jws": "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdfQ..WBkq33Zs0dwzbqo-F66RUlVfMYWc-g8B3NANI5FULwR10fnYElJNI6K1wbqAuClVMGsEZAbwJX5jmeUPGbdvNA"
}
"verificationMethod": "did:example:123#key-0",
and
"verificationMethod": "did:example:123?version=0#key-0",
Are different URIs....
if did:example:123?version=0#key-0 is not an id of a verificationMethod in the result of resolving did:example:123?version=0#key-0... the proof will not verify with without the verifier mutating the result of the resolution (tampering with the resolution response).
Similarly for JOSE, if the kid is did:example:123?version=0#key-0, but the DID Document only contains:
did:example:123#key-0
Or vice versa, how does the verifier know which public key bytes to use?
cc @selfissued @csuwildcat this is also related to "DID / JOSE interoperability"... and the very poor usability of the "VC-JWT" format... since VC-JWT does not have "documentLoader" but also does not define a dereferencing algorithm for kid -> publicKeyBytes.
Sidetree JWS does not require kid to be present in the JWS header nor would requiring it along be sufficient, and subsequently, we have a whole section of the spec dedicated to explaining how to verify a JWS.... without clarity on this in DID Core, we will be inviting everyone to do the same everywhere else.
Is this maybe the same problem as the long form / short form thing in Sidetree?
Could you do this:
{
"id": "did:example:123",
"sameAs": "did:example:123?version=0",
"verificationMethod": [ {
"id": "#key-0",
"type": "JsonWebKey2020",
"controller": "did:example:123",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
Then dereferencing did:example:123?version=0#key-0 and did:example:123#key-0 would both "work"?
Or could you have a sameAs property on the verificationMethod level instead of the top level?
@peacekeeper yes, its related...
"sameAs": "did:example:123?version=0",
This would imply you can merge the graphs, which if you rotated compromised keys out... that would be bad... also semantically, did subject at timestamp (-5 hours) and did subject at (now) are not really the "same thing" imo... raises questions on if the did subject can change, like did:example:123 => my dog, but then it dies and I get a new dog and use the same did.
In the case of sidetree long and short form, the did documents are actually the same, "except for the id / controller", whereas these would be very different did documents depending on version.
This is why equivalence properties and security need to be handled with care.
We don't want to say that an old (possibly compromised) set of keys are the same kind of authoritative as the current set.
Because version is defined, but can be handled differently by each did method, this becomes a potential area for security fail.
I think the behavior folks are looking for when they craft: did:example:123?version=52#key-73
Is a stable identifier for public key bytes at a moment in time.
Returning a did document at that moment in time with stable identifier for keys at that time satisfies this desire.
But its only possible if the did document DOES NOT use relative refs.... I imagine that will not be obvious to most developers, because its a function of the normative requirements for didDocument.id, verificationMethod.id and relative refs... in other words, you need to read all 3 sections carefully to realize that VCs can only be issued from DID Methods that support version and that DO NOT implement relative refs....
So from my understanding of this issue, we need to do two things.
- update the language to adhere to @peacekeeper suggestion
- update implementations to explicitly use version parameter in the id fields of keys so that they can be properly identified without any semantic ambiguity.
Is that correct and all that's needed to close this issue @OR13 or would there be other orthogonal aspects unrelated to the use in the specific use cases brought up recently?
But its only possible if the did document DOES NOT use relative refs
This is what I'm missing. I don't understand why. It seems to have something to do with embedding a version in a DID URL, and with some requirements of Linked Data Proofs that I don't know because I don't use them? Why would I ever want to reference a version of a DID doc from inside that same DID document? I see that as being useful only outside a DID document, to refer to things inside the doc at a particular version.
I apologize for asking what appears to be a remedial question, @OR13. You've tried really hard to explain this already, I can tell. But I think the amount of detail you've given is overwhelming the insight I'm supposed to be able to derive.
I want to name my keys "#key1", "#key2", and so forth (everything is relative except for the DID document's root id property). Further, the DID methods I use reject a possible usage that the spec allows, where #key1 can mean something different in version 1 than it does in version 2. Relative IDs are never reused. (I argued strenuously in favor of that possibility being eliminated across the board, but was voted down, so I can only speak for my own DID methods semantics there.) In that mental model, if #key1 is present in version 1 and version 2 and version 3 of the doc, then it is the same in all of them, and the relative reference is unambiguous. Perhaps the issue Orie is worried about occurs because some DID methods allow this sort of redefinition of a relative reference, and therefore require a version to disambiguate?
@dhh1128 consider, a JWS or a linked data proof using kid or verificationMethod did:example:12#key-0
Valid
{
"id": "did:example:123",
"verificationMethod": [ {
"id": "#key-0",
"type": "JsonWebKey2020",
"controller": "did:example:123",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
{
"id": "did:example:123",
"verificationMethod": [ {
"id": "did:example:123#key-0",
"type": "JsonWebKey2020",
"controller": "did:example:123",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
Now consider, a JWS or a linked data proof using kid or vm did:example:12?version=53#key-0
Valid
{
"id": "did:example:123", // version is not allowed here
"verificationMethod": [ {
"id": "#key-0", // the strings do not match, (this is a relative ref), but this is the correct key to use.
"type": "JsonWebKey2020",
"controller": "did:example:123", // version is not allowed here
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
{
"id": "did:example:123", // version is not allowed here
"verificationMethod": [ {
"id": "did:example:12?version=53#key-0", // strings match (this is not a relative ref)
"type": "JsonWebKey2020",
"controller": "did:example:123", // version is not allowed here
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
Valid ?
{
"id": "did:example:123", // version is not allowed here
"verificationMethod": [ {
"id": "?version=53#key-0", // strings do not match (this is a relative ref)
"type": "JsonWebKey2020",
"controller": "did:example:123", // version is not allowed here
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
In this example "version" query param is dropped or preserved... this issue asks the question, how and when should query params be dropped / preserved.
The answer appears to be "up to the did method to decide"... the examples I have just shown demonstrate why thats dangerous, because all the examples are "valid" interpretation of did core today, and a verifier / developer would be super sad to have to support all of them.
There is a gap between the VC Data Model / JOSE and DID Core, and through that gap issues like this will rain down on the heads of sad developers :)
I'd love to be able to say something like:
verificationMethod.id MUST NOT contain DID URL path or query values...
or
verificationMethod.id MUST contain DID URL path and query if they are present in the original DID URL...
or
relativeRefs MUST NOT contain path and query
or
relativeRefs MUST contain path and query if they are present in the original DID URL...
Because these statements would reduce the possible ways developers can interpret DID Core.
To my mind...
The underlying assumption is that query arguments impact the data which is returned when dereferencing any URL, including DIDs and DID URLs. Therefore, if a DID method, and hence any DID or DID URL based on that method, uses query arguments -- be those arguments &version or &q1 or &version-id or anything else -- then those query arguments are essential elements of the DIDs and DID URLs, and they MUST be preserved at all times.
Relative URLs found in any document/serialization -- which includes DID Documents and anything else that results from dereferencing a DID or DID URL -- MUST NOT be taken from that document in their relative form, because that is not how they are meant to be consumed, ever. #key-0 is meaningless on its own -- and moving it from one context (i.e., base) to another, while not guaranteed to lead to no value or to an incorrect value, is just as likely to as not. Such relative URLs MUST be expanded to their complete form, which would include any query arguments which were part of the dereferencing request (or in HTTP world, part of the Location header resulting from that dereferencing request), before use anywhere else for anything else.
DID Documents are not encountered as files in a filesystem -- though they may have been composed thus. They are encountered when dereferencing a DID and/or a DID URL. Any relative URLs therein become full URLs when they are encountered by scheme- and/or media-type-compliant tools.
Look at what happens when a relative link in an HTML document is clicked on within an HTML browser rendering of that HTML document. The URL you see in the location box is never the barebones relative URL, nor does it lose any arguments which were present in that location box before you clicked on the originally-relative link. The URL you find when you "View Source" is relative, because it's meant to be used in a context which is not the source; that context is the rendering you see in the browser. Relative URLs are used by authors to save typing, disk consumption, and transmission bandwidth; they are not meant to be taken out of the context of the document in which they're found and/or the protocol through which that document is consumed.
In other words, I believe relativeRefs are only meant to be relative when they're in a place which will turn them into absoluteRefs when they're put to use. All of that results in --
- verificationMethod.id MUST contain DID URL
pathandqueryif they are present in the original DID URL - absoluteRefs MUST contain
pathandqueryif they are present in the original DID URL - relativeRefs SHOULD NOT contain
pathnorquerybecause they are within a context to which the Refs are relative -- and that context will provide thepathorqueryor other URL elements
@TallTed thanks for commenting on this!
I think I agree with your proposal.
consider the verificationMethod in a proof, with value:
did:example:123?version=53#key-0
This must be de-referencable to:
{
"id": "did:example:123", // version is not allowed here
"verificationMethod": [ {
"id": "?version=53#key-0", // strings do not match (this is a relative ref)
"type": "JsonWebKey2020",
"controller": "did:example:123", // version is not allowed here
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "5ZfvRFT4SXjCW2xbsQj6Yt3aYl9P3MnRLUaRSoWV5nA",
"y": "adpC6bexW8SqYTQe_80dKT87mDIC3irPtT7smKR7btA"
} ]
}
note that id and controller DO NOT have path and query, but verificationMethod.id does.
@OR13 -
(I think you meant [emphasis mine], "note that id and verificationMethod.controller DO NOT have path and query, but verificationMethod.id does.")
First, it's important to remember that did:example:123?version=53#key-0 means "the fragment identified by key-0 in the DID Document that results from dereferencing the did:example:123?version=53 DID URL".
Also, that dereferencing the did:example:123?version=53 DID URL is expected to result in a different (perhaps entirely different!) DID Document than dereferencing the did:example:123?version=11 DID URL.
It may also be important to know which of the following DID URLs you get by resolving the did:example:123 DID --
did:example:123?version=53did:example:123did:example:123?version=latest
-- and/or whether the ?version=## parameter is necessary to resolve the DID at all.
All that leads me to think that your --
"id": "did:example:123", // version is not allowed here
-- should be --
"id": "did:example:123?version=53", // all query parameters are likely required here
-- because the content of the DID Document will change somewhat with each version -- and may change entirely with variance in any other named query parameter. Maybe all query parameters must be preserved as top-level attributes, so --
"id": "did:example:123", // version is not allowed here
"version": "53"
And all that makes me think that using some other random query parameter that has no inferred lexical significance would be better for this exploration than ?version=53. (Perhaps ?foo=53?)
It also seems worthwhile to discuss cases with 2 or even 3 parameters, as the complications inherent in such scenarios will help tease out otherwise easy-to-overlook issues.
Similarly, the case where the controller value is the same as the DID Subject is more difficult to understand (and I think will, over time, be substantially less common) than the cases where these values differ. For these reasons, I suggest that discussions proceed with the cases where these values differ until all is fully understood. Discussion/exploration of the less-common case where they are the same should then be a fairly trivial exercise.
Last for this comment -- I submit that using relativeRefs is not always optimal, even if technically possible. Once fully understood, of course, and especially when they are generated and processed mechanically, they can be an important optimization tool. Until then, they can be a substantial source of error and confusion. (Not least, because there are different rules about expansion of relativeRefs in HTTP-land and in JSON-land and in JSON-LD-land, not to mention other contexts. In JSON, relative URLs are expanded by simple concatenation. In JSON-LD, some relative URLs are expanded by simple concatenation, and others are expanded according to HTTP rules.)
Missed one thing...
I said that "verificationMethod.id MUST contain DID URL path and query if they are present in the original DID URL" ... and "relativeRefs SHOULD NOT contain path nor query"
It appears that I should have also said that "verificationMethod.id SHOULD NOT be a relativeRef if path and/or query are present in the original DID URL", which I thought was implied by the previous two statements.
Perhaps, instead of "relativeRefs SHOULD NOT contain path nor query", I should have said "relativeRefs MUST NOT contain path nor query" which would have forced verificationMethod.id to be an absoluteRef based on the other MUSTs... but I'm not sure that the relativeRef restriction is really a MUST in all cases.