fix(did-provider-oyd): follow the CMSM session protocol, delegate resolution - #403
Open
fabianekc wants to merge 1 commit into
Open
Conversation
…olution The did:oyd registrar moved client-managed-secret-mode to session handles. Creating a DID is now a challenge/response flow: every intermediate answer is HTTP 201 and names the value to sign, the key to sign it with, and a session handle that has to be echoed back. This provider did two calls with one signature and no session, so the registrar started a new flow on the second call and never completed one - which is what the FIXME on the CMSM test was about. Three calls and two signatures are needed with a server-managed revocation key, and both sign with the document key. - drive the flow as a loop: post, and while the answer is 201, sign the returned value with the key named in `with` and post back session and signature. Bounded by MAX_CMSM_PHASES against a misbehaving registrar - keep the log_revoke from the closing answer in the key metadata. It is signed with a key the registrar never sees and a secure element need not sign deterministically, so it cannot be reproduced; without it the DID can neither be updated nor revoked. It is a log entry, not a secret - make the registrar endpoint configurable via OydConstructorOptions - surface registrar errors with their status and message instead of only the status text, and drop the console.log calls that printed the request, the response and the signature Resolution now goes through oydid-did-resolver, which is maintained with the method. The copy in this package had the resolver host hard-coded inside the function with no way to override it, and that host is being replaced by resolver.ownyourdata.eu. It also reported every failure as invalidDid, including a 404, and treated a deactivated DID (HTTP 410) as invalid. The library reports DID Resolution error codes, reports deactivation as didDocumentMetadata.deactivated per DID Core 7.1.3, and has no runtime dependencies - did-resolver is a types-only peer dependency. The CMSM test no longer carries a hard-coded private key, and the stray top-level console.log that ran on import is gone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(did-provider-oyd): follow the CMSM session protocol, delegate resolution
Repo:
Sphereon-Opensource/SSI-SDK, base branchdevelop(checked against4f8b21c).Why
Two independent things were broken in
packages/did-provider-oyd.1. Client-Managed-Secret-Mode could not complete. The did:oyd registrar moved
CMSM to session handles. Creating a DID is a challenge/response flow: every
intermediate answer is HTTP 201 and names the value to sign (
sign), the key tosign it with (
with), and asessionhandle that must be echoed back. Thisprovider did two calls with one signature and no session, so the registrar
started a fresh flow on the second call and never finished one. That is what the
// FIXME: Enabled when CMSM is workingon the second test was about.To be fair: the server side was broken too. The registrar driver dropped both the
session and the signature, so even a correct client would have failed. That is
fixed and deployed —
oydid-registrar.data-container.netnow reports version0.5.1 and the multi-phase flow is verified against it.
2. The resolver was a copy of an old library with
https://oydid-resolver.data-container.nethard-coded inside the function, notoverridable. That host is being replaced by
resolver.ownyourdata.eu. The copyalso reported every failure as
invalidDid— including a 404 — and treated adeactivated DID (HTTP 410) as an invalid one.
What changed
CMSM flow —
createIdentifierWithCMSMbecomes a loop: post, and while theanswer is 201, sign the returned value with the key named in
withand post back{session, sig}. With a server-managed revocation key that is three calls andtwo signatures, both signing with the document key.
The callback interface is unchanged.
publicKeyCallbackandsignCallbackkeeptheir signatures, and the signing itself is untouched: for P-256 it is SHA-256
over the returned ASCII string, ECDSA, base64url
r‖swithout padding — exactlywhat
SphereonKeyManageralready produces.log_revokefrom the closing answer is kept inidentifier.keys[0].meta.oyd.logRevoke.It is signed with a key the registrar never sees, and a secure element does not
have to sign deterministically, so it cannot be reproduced later; without it the
DID can neither be updated nor revoked. It is a log entry, not a secret. If you
would rather surface it somewhere else, say where and I will move it.
Registrar endpoint is now configurable through
OydConstructorOptions.registrarUrl.Resolution is delegated to
oydid-did-resolver@0.3.0,maintained alongside the method. Configurable endpoint, DID Resolution error
codes, deactivation per DID Core 7.1.3, and no runtime dependencies at all —
did-resolveris a types-only peer dependency there.Housekeeping — the
console.logcalls that printed the request, the responseand the signature are gone; the CMSM test no longer carries a hard-coded private
key; the stray top-level
console.log(generatePrivateKeyHex())that ran onimport is removed.
Verification
The signature format was confirmed against the live registrar from plain
JavaScript, with a non-extractable WebCrypto P-256 key:
The probe stops after phase 2 on purpose, so it creates no DID; the session
expires after 15 minutes.
Both tests in
__tests__now pass against the live registrar, including the onethat carried the FIXME:
That also settles the one thing I could not check from the outside:
SphereonKeyManagerproduces exactly the signature encoding the registrarexpects for Secp256r1, so
signCallbackneeded no change.pnpm --filter @sphereon/did-provider-oyd... buildis clean.Unrelated, but you may want to know: on Node 24,
pnpm installfails whilebuilding
canvas@2.11.2— no prebuilt binary for the ABI and the source buildneeds
pixman-1.pnpm install --ignore-scriptsworks around it. Nothing to dowith this change; it just bit me on the way in.
Open
updateIdentifierstill throwsnot supported yet. The registrar now implementsCMSM for update as well (three signatures, the last with the previous document
key, plus the
log_revoke_oldof the current document). Happy to follow up withthat in a separate PR if it is useful to you.