did:web Document Auditor
Pricing
Pay per usage
did:web Document Auditor
Audits a did:web (W3C Decentralized Identifier) document against the did:web method spec. Validates URL construction, id match, verification methods, relationship arrays, and service entries. Returns a score, grade, and recommendations.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Sanskar Jaiswal
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Audits a did:web decentralized identifier against the W3C did:web method specification. did:web anchors a Decentralized Identifier (DID) to a domain name and resolves it to a JSON DID document served over HTTPS. This actor constructs the DID document URL from the identifier, fetches it, and validates its structure against the spec: @context, id, verificationMethod entries, verification relationship arrays, and service entries.
Use cases
- Organizations publishing a
did:webidentity verifying their DID document is spec-conformant before relying parties consume it. - Decentralized identity and verifiable-credentials engineers debugging DID resolution failures.
- Protocol tooling and wallet developers checking real-world
did:webserver behavior against the spec. - Agency consultants auditing a client's decentralized-identity deployment.
- CI/CD pipelines validating a
did.jsonfile after a deploy or key rotation.
Input
| Field | Type | Required | Description |
|---|---|---|---|
identifiers | array of strings | yes | did:web identifiers or plain domains/URLs to audit, e.g. did:web:example.com, did:web:example.com:user:alice, or example.com. Plain domains/URLs are converted into a did:web identifier automatically. Up to 50 per run. |
timeout | integer | no | Request timeout in seconds, 3-60. Default 10. |
maxRedirects | integer | no | Maximum redirect hops to follow when fetching the DID document, 0-5. Default 3. |
maxBodyBytes | integer | no | Maximum response body size read, in bytes. Default 2097152 (2 MB). |
Output
One JSON object per audited identifier, pushed to the default dataset.
| Field | Type | Description |
|---|---|---|
inputIdentifier | string | The identifier or domain provided as input. |
resolvedDid | string|null | The did:web identifier that was resolved (null if construction failed). |
resolvedUrl | string|null | The constructed DID document URL that was fetched. |
finalUrl | string|null | Final URL after following any redirects. |
https | boolean|null | Whether the DID document was served over HTTPS. |
status | integer|null | HTTP status code of the fetch. |
contentType | string|null | Response Content-Type header. |
jsonValid | boolean | Whether the response body parses as a valid JSON object. |
parseError | string|null | JSON parse error message, if any. |
idMatch | boolean | Whether the document's id field matches the resolved did:web identifier. |
context | object|null | @context analysis: present, hasCanonical, note. |
verificationMethod | object|null | verificationMethod analysis: present, count, per-entry issues. |
relationships | object|null | Per-relationship analysis for authentication, assertionMethod, keyAgreement, capabilityInvocation, capabilityDelegation. |
serviceCount | integer | Number of entries in the service array. |
serviceIssues | array | Malformed-service-entry issue descriptions. |
services | array | Service entries reported as data only; serviceEndpoint values are never fetched. |
score | integer | Conformance score, 0-100. |
grade | string | Letter grade, A to F. |
checks | array | Per-check breakdown: name, status, note, weight, recommendation. |
issues | array | Aggregated issue descriptions. |
recommendations | array | Actionable fixes. |
checkedAt | string | ISO timestamp of the audit. |
error | string|null | Top-level construction or fetch error, if any. |
Example input
{"identifiers": ["did:web:well-known-showcase.paulkinlan-ea.deno.net"],"timeout": 10}
Example output
{"inputIdentifier": "did:web:well-known-showcase.paulkinlan-ea.deno.net","resolvedDid": "did:web:well-known-showcase.paulkinlan-ea.deno.net","resolvedUrl": "https://well-known-showcase.paulkinlan-ea.deno.net/.well-known/did.json","https": true,"status": 200,"contentType": "application/json; charset=utf-8","jsonValid": true,"idMatch": true,"context": { "present": true, "hasCanonical": true, "note": "'@context' includes the canonical DID context." },"verificationMethod": { "present": true, "count": 1, "allValid": true },"serviceCount": 1,"serviceIssues": [],"score": 100,"grade": "A","issues": [],"recommendations": ["did:web document looks spec-conformant. Re-run this audit after document updates to catch regressions."]}
How URL construction works
Per the did:web method spec's resolution algorithm:
- Strip the
did:web:prefix (or treat a plain domain/URL input as the method-specific identifier). - Replace
:with/to obtain the domain and optional path, except a percent-encoded port colon (%3A), which is percent-decoded back to a literal:on the host instead of becoming a path separator. - Prepend
https://. - No path segments: append
/.well-known/did.json. - Path segments present: append
/<segment>/<segment>.../did.json.
Before any HTTP request is made, every path segment is rejected if it is empty, ., .., or percent-decodes to contain / or \. This defends against path-traversal and DID-confusion attacks where a crafted identifier could be resolved to an unintended document.
Security
- Only public HTTP/HTTPS targets are fetched. URLs containing userinfo/credentials (
user:pass@host) are rejected. - Localhost and private/link-local IPv4 and IPv6 literal ranges (RFC 1918, RFC 3927, and IPv6 unique-local/link-local equivalents, including bracketed IPv6 literals) are blocked.
- Hostnames are DNS-resolved and the resolved addresses are re-checked against the same private-range rules before any request is made (defense against DNS rebinding).
- Redirects are followed manually (up to
maxRedirectshops) and each hop is revalidated with the same SSRF checks before the next request. - Response bodies are capped at
maxBodyBytes(default 2 MB). - The actor fetches exactly one URL: the constructed DID document URL (and its validated redirect targets).
serviceEndpointvalues and any other URLs or DID references discovered inside the fetched document are reported as data only and are never fetched or resolved by this actor. - No login, no credential collection, no private-data extraction.
Pricing
| Event | Price (USD) |
|---|---|
| Actor start | $0.005 |
| Identifier audited | $0.01 |
Apify takes a 20% platform commission; the operator keeps 80%.
FAQ
Can I pass a plain domain instead of a full did:web identifier? Yes. Supplying example.com is equivalent to did:web:example.com. Supplying example.com/user/alice is equivalent to did:web:example.com:user:alice.
What happens if the identifier includes a non-standard port? Use the did:web percent-encoded form, e.g. did:web:example.com%3A8443:user:alice, which resolves to https://example.com:8443/user/alice/did.json.
Does the actor fetch the serviceEndpoint URLs listed in a DID document? No. Service entries are reported as data only, the same way other .well-known auditors in this portfolio report discovered links without ever fetching them. This keeps the actor from being used to pivot into auditing arbitrary third-party URLs referenced by someone else's DID document.
Why is a mismatched id field treated as a hard failure? The did:web resolution algorithm requires verifying that the resolved document's id matches the DID being resolved (step 7). A mismatch means the document cannot be trusted to represent the queried identifier, so it is weighted heavily in the score.
Are publicKeyBase58 and publicKeyPem still valid? They are legacy/deprecated key-material representations. publicKeyPem is flagged explicitly as deprecated; prefer publicKeyJwk or publicKeyMultibase for new verification methods.