Skip to content

connectToDID doesn't resolve DID URL service references, silently skipping peer discovery #4448

Description

@reinkrul

Description

Network.connectToDID (network/network.go) iterates a discovered peer's node.Service entries and calls service.UnmarshalServiceEndpoint(&nutsCommUrl) directly on the raw NutsComm service entry.

This works for a literal serviceEndpoint (e.g. "grpc://example.com:5555"), but not when the serviceEndpoint is itself a DID URL reference, e.g.:

"serviceEndpoint": "did:nuts:C79u41fohcBHt7EwBF7KQdkhqerLCvAceGuLrEjHUq3S/serviceEndpoint?type=NutsComm"

This is a valid, spec-compliant way to publish a service endpoint (an indirection to another DID document's service) — used e.g. by a care organization's (child) DID document referencing its vendor's (parent) DID document. UnmarshalServiceEndpoint tries to parse this string directly as an absolute URL and fails, which is logged (at Debug level) and that service entry is silently skipped:

msg="Failed to extract NutsComm address from service" did="did:nuts:..." error="url must contain scheme and host"

Because the error is Debug-level and per-service, the node never surfaces that it failed to resolve that DID's NutsComm address.

Root cause

connectToDID reads the service entry directly instead of resolving it. Compare with Network.checkNodeDIDHealth, which correctly follows DID URL service references before parsing:

serviceRef := resolver.MakeServiceReference(nodeDID, transport.NutsCommServiceType)
nutsCommService, err := n.serviceResolver.Resolve(serviceRef, resolver.DefaultMaxServiceReferenceDepth)

connectToDID has access to the same n.serviceResolver but never uses it.

Impact

Discovery through a care organization's (child) DID document that references its vendor's (parent) DID document's NutsComm service fails via this path. In practice this is likely low severity: the vendor's (parent) DID document is discovered separately and publishes the endpoint as a literal URL, so the underlying node still gets discovered and connected to through that document. The gap is that discovery specifically through the child DID document silently fails instead of resolving the reference, which is still worth fixing since it's a valid, spec-compliant endpoint format.

Suggested fix

In connectToDID, resolve the service via n.serviceResolver.Resolve(...) (as checkNodeDIDHealth already does) before unmarshalling into transport.NutsCommURL, instead of unmarshalling the raw did.Service entry directly.

Assisted by AI

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions