didresolver

package
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package didresolver provides the two port.DIDResolver adapters:

  • Noop — zero-I/O quickstart resolver; synthesizes the DID document from the submitted proofs' embedded keys.
  • Web — the real thing: hardened HTTPS fetch of did.json with WebPKI validation and SSRF dialer guards.

Selected by `identity.resolver.type` ("noop" | "web") in the RA config — the same pattern as the DNS verifier's `dns.type` ("noop" | "lookup").

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Noop

type Noop struct{}

Noop is the quickstart resolver. It never dials anywhere: the DID document is synthesized from the hints — the kid → public-JWK pairs the service extracted from the submitted proofs' `jwk` protected headers.

What this preserves and what it waives, stated precisely (the noop DNS verifier precedent — real crypto, waived external-world binding):

  • PRESERVED: every JWS still genuinely verifies against the embedded key, the proof input still binds identityId / nonce / purpose / raId, and the sealed event remains self-verifying.
  • WAIVED: the binding "the live did.json at the DID's host really lists this key". Anyone can mint a keypair and claim any did:web value.

Strictly for local development and the demo scripts. NOT for production.

func NewNoopResolver

func NewNoopResolver() *Noop

NewNoopResolver returns the quickstart resolver.

func (*Noop) Resolve

func (n *Noop) Resolve(_ context.Context, did string, hints []port.KeyHint) (*port.DIDDocument, error)

Resolve synthesizes a DID document listing exactly the hinted keys as assertionMethod entries of the requested DID. With no hints (the register-time advisory fetch) the document is valid and empty — the 202 challenge list then carries a single unkeyed entry, and the registrant names keys via the JWS `kid` + `jwk` headers at verify time.

The synthesized Raw entry embeds the registrant's submitted jwk VERBATIM (json.RawMessage passes through marshalling untouched), so the sealed verification method quotes the registrant's exact key bytes — the same no-derived-values rule the web resolver satisfies by quoting the live document.

type Web

type Web struct {
	// contains filtered or unexported fields
}

Web is the production did:web resolver: an HTTPS GET of the DID document at the DID's resolution URL, through one hardened fetcher.

The fetch target is registrant-steered (any host the DID names), so SSRF is a first-class control (design §3.7):

  1. Egress IP denylist enforced at connect time, post-DNS: RFC 1918, loopback, link-local (which covers cloud metadata addresses), ULA, and unspecified addresses are rejected at the dialer, never by hostname string inspection.
  2. The resolved IP is pinned per host for the duration of one Resolve call — a DNS rebind between redirect hops cannot slip an internal target past check 1.
  3. Full WebPKI validation (chain to a trusted root + hostname verification) on every fetch — Go's default TLS behavior, left fully enabled.
  4. Bounded: hard timeout (default 5 s, parity with the DNS verifier), response-size cap (default 1 MiB), ≤5 redirects constrained to the original host's registrable domain.
  5. Error details never echo resolved IPs, ports, or redirect chains (no SSRF oracle).

func NewWebResolver

func NewWebResolver(opts ...WebOption) *Web

NewWebResolver constructs the production resolver.

func (*Web) Resolve

func (w *Web) Resolve(ctx context.Context, did string, _ []port.KeyHint) (*port.DIDDocument, error)

Resolve fetches and parses the DID document. Hints are ignored — the authoritatively resolved document is always the key source.

type WebOption

type WebOption func(*Web)

WebOption customizes the Web resolver.

func WithAllowPrivateNetworks

func WithAllowPrivateNetworks() WebOption

WithAllowPrivateNetworks disables the egress IP denylist. FOR TESTS ONLY — it exists so the full real fetch path (dialer included) is exercisable against a loopback TLS server. Never reachable from configuration.

func WithLogger

func WithLogger(logger zerolog.Logger) WebOption

WithLogger attaches a server-side logger (default: no-op). It records the failure CATEGORY (the underlying error) plus the DID and elapsed time so an on-call engineer can distinguish a denylist rejection from a DNS-rebind block, a TLS failure, a timeout, or a refused connection — none of which ever reach the API caller (the wire error stays deliberately coarse, no SSRF oracle).

func WithMaxBodyBytes

func WithMaxBodyBytes(n int64) WebOption

WithMaxBodyBytes overrides the response-size cap (default 1 MiB).

func WithRootCAs

func WithRootCAs(pool *x509.CertPool) WebOption

WithRootCAs overrides the trusted root pool (default: system roots). Deployments with private PKI inject their pool here; tests inject the httptest server's certificate.

func WithTimeout

func WithTimeout(d time.Duration) WebOption

WithTimeout overrides the per-resolve hard timeout (default 5s).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL