Skip to content

feat: validate directories from verification page#83

Open
MukundaKatta wants to merge 2 commits into
cloudflare:mainfrom
MukundaKatta:codex/validate-directory-ui
Open

feat: validate directories from verification page#83
MukundaKatta wants to merge 2 commits into
cloudflare:mainfrom
MukundaKatta:codex/validate-directory-ui

Conversation

@MukundaKatta
Copy link
Copy Markdown

Fixes #37.

Adds a Worker-backed directory validator to the verification website:

  • exposes /v0/api/validate-directory?url=... so the Worker fetches directory URLs server-side
  • validates that the URL is HTTPS and points at /.well-known/http-message-signatures-directory
  • checks that the response is JSON with a non-empty keys array and purpose string
  • adds a small form to the verification page so users can paste a directory URL and get browser feedback
  • covers validation error paths in the Worker test suite

Validation:

  • npm ci
  • npm run build
  • npm run test -w verification-workers -- --run --reporter verbose

Copy link
Copy Markdown
Contributor

@AkshatM AkshatM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! It's in good shape, but needs broadening to support #37 unfortunately. I've left some comments accordingly.

return response.json();
}

function validateDirectory(directory: unknown): string[] {
Copy link
Copy Markdown
Contributor

@AkshatM AkshatM May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateDirectory and validDirectoryURL are validating something different than what #37 is expecting, alas.

To be a valid directory, it should implement all the checks the http-signature-directory crate is doing today. There are some critical missing ones today:

  1. Content-Type header matches what's expected
  2. It should parse Signature-Input and Signature headers appropriately, then check if performing a WBA verification against Signature passes for every key listed in the key set returned by the directory. This can be accomplished using the existing Typescript packages.
  3. It should ensure Signature-Input contains the right components, specifically @authority and the right tag.
  4. It should verify that the Signature isn't expired.

The reference crate only supports Ed25519, but the Typescript packages in this repository support multiple. You should utilise it to implement what you need.

}
}

if (typeof value.purpose !== "string" || value.purpose.length === 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think purpose is required by the JSON web key set format described in the RFC for signature directories, so you can drop this check.

@AkshatM AkshatM requested a review from thibmeu May 1, 2026 16:27
@MukundaKatta
Copy link
Copy Markdown
Author

Thanks @AkshatM. Two follow-ups:

  • I'll re-read the http-signature-directory crate and rewrite validateDirectory to enforce the same checks (signature verification on the JWKS, controlled-by URI matching, etc.) so the worker example matches what Verification website should be able to validate directory #37 expects.
  • Will drop the purpose check in the next push.

Let me know if you'd prefer this scoped here or split into a follow-up PR — happy to do either.

@AkshatM
Copy link
Copy Markdown
Contributor

AkshatM commented May 4, 2026

Let me know if you'd prefer this scoped here or split into a follow-up PR — happy to do either.

Scoped here is sufficient.

Copy link
Copy Markdown
Collaborator

@thibmeu thibmeu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the PR @MukundaKatta

i worry about the introduced DoS vector this introduces, by allowing a proxy to fetch arbitrary URLs. Specifically, the tricky part is fetching arbitrary URLs, mentioned #37 (comment), which this is essentially proposing

having a simpler design requesting a captcha pass (like turnstile) and light validation on the proxying side, while the browsre performes the actual check

maybe so we can tune the design: is your use case local validation in a browser, an API that you can hit, something else?

);
}

const response = await fetch(parsed);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an arbitrary fetch, which creates a DoS vector

…lare#37

Address @AkshatM's review feedback by aligning the worker's directory
validator with the http-signature-directory crate's checks:

  - Content-Type must start with
    'application/http-message-signatures-directory+json'
    (per crates/http-signature-directory/src/main.rs)
  - drop the 'purpose' check — not required by the JWKS shape in the
    HTTP Message Signatures directory RFC
  - cap the response body at 64 KB to remove the DoS amplification
    vector AkshatM flagged on the arbitrary fetch
  - cryptographically verify the response signature against *every*
    key in the JWKS via the existing 'verify()' helper from
    web-bot-auth. The verify() callback enforces:
      * tag === 'web-bot-auth' (built-in)
      * signature freshness via the created/expires window (built-in)
      * Ed25519 signature against the imported JWK (per-key)
    Per the reference crate, a single key passing isn't sufficient —
    every listed key must verify.
  - request the directory as a ResponseRequestPair so '@authority;req'
    can resolve from the request side when the signature covers it.

Multi-kty support is staged behind 'kty === "OKP"'; non-Ed25519 keys
fail with a clear error rather than silently importing as the wrong
algorithm. The reference crate is also Ed25519-only today, so this
matches behaviour. Adding RSA/EC support is a separate change.
@MukundaKatta
Copy link
Copy Markdown
Author

Pushed f21486b addressing all four points from the review:

  1. Content-Type check — now enforces application/http-message-signatures-directory+json per the reference Rust crate. Mismatch returns 502 with a clear error.
  2. Cryptographic verification — switched from "shape only" validation to running verify() from web-bot-auth against the response, looping over every key in the JWKS. The library handles tag (web-bot-auth) + signature freshness (created/expires) automatically. A failure on any listed key surfaces as keys[N]: <reason> so the report is per-key.
  3. @authority;req support — request side is provided as a ResponseRequestPair so the verifier can resolve @authority with the req parameter from the request that fetched the directory.
  4. DoS vector — capped the response body at 64 KB before parsing JSON. Larger payloads return 502 with a clear error.

Also dropped the purpose check.

Multi-kty: gated behind kty === "OKP" (Ed25519 only today, matching the reference crate); non-Ed25519 fails with unsupported kty "<value>" rather than silently importing under the wrong algorithm. Happy to add RSA/EC import as a follow-up if you'd like — wanted to keep this PR scoped to the alignment with the reference behavior.

The pre-existing tsc error at verifySignature (line 359 in original) for await verify(request, ...) is untouched — it's the workers Request vs ResponseRequestPair mismatch that exists on main too. Happy to fix in this PR or split it out, your call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Verification website should be able to validate directory

3 participants