npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dwk/dpop

v0.1.0-beta.2

Published

DPoP (RFC 9449) proof verification. Cross-standard reusable; no Workers runtime dependency.

Downloads

509

Readme

@dwk/dpop

DPoP (RFC 9449) proof verification. Cross-standard reusable.

Part of the @dwk IndieWeb + Solid cohort. See the package specification for the full requirements.

This package is cross-standard reusable: it takes plain-data inputs only, has no Workers-runtime dependency (only Web Crypto), and unit-tests in isolation (Node, no workerd). It is protocol-agnostic — it knows nothing about IndieAuth or Solid. The caller supplies the request facts and any access-token binding it expects, and owns replay detection via the returned jti.

API

import { verifyDpopProof } from "@dwk/dpop";

const result = await verifyDpopProof({
  proof: request.headers.get("DPoP")!, // the DPoP proof JWT
  htm: request.method, // HTTP method, e.g. "POST"
  htu: "https://pod.example/resource", // request URI (query/fragment ignored)
  // now,            // epoch seconds; defaults to Date.now()
  // maxAgeSeconds,  // iat clock-skew window; defaults to 300
});

if (!result.valid) {
  // result.reason is a stable code, e.g. "htu_mismatch", "signature_invalid"
  return new Response("invalid DPoP proof", { status: 401 });
}

// Enforce your own replay policy with the verified jti.
if (await seenBefore(result.jti)) return new Response("DPoP replay", { status: 401 });

Resource Server: token binding

When a request carries a DPoP-bound access token, pass the token and the token's cnf.jkt to verify the binding:

const result = await verifyDpopProof({
  proof,
  htm,
  htu,
  accessToken, // proof MUST carry ath = base64url(SHA-256(accessToken))
  expectedJkt, // proof key thumbprint MUST equal this
});

A Resource Server enforcing ath MUST pass expectedJkt too: ath only proves the proof was made for this token, while cnf.jkt proves the proof key is the one the token was issued to. Supplying accessToken without expectedJkt is rejected with jkt_required rather than validating an unbound proof (RFC 9449 §7.1).

What is verified

  • Headertyp is exactly dpop+jwt; no crit parameter is present (RFC 7515 §4.1.11); alg is an asymmetric algorithm from the allow-list (ES256, ES384, RS256, PS256 — never none or HMAC); jwk is present, carries no private key material, has an EC crv matching the alg, and (for RSA) a modulus of at least 2048 bits.
  • Signature — over header.payload using the embedded jwk.
  • Claimshtm matches the request method (case-insensitive); htu matches the request URI after normalization (scheme/host lowercased, default port and any query/fragment removed); iat is within the clock-skew window; jti is a present, non-empty string.
  • Bindings (optional) — ath matches base64url(SHA-256(accessToken)); the computed jkt (RFC 7638 thumbprint) equals expectedJkt.
  • Server nonce (optional, RFC 9449 §8/§9) — when expectedNonce is supplied, the proof's nonce claim must equal it (else nonce_mismatch). The proof's nonce is surfaced on the result either way so a caller can answer a mismatch with a use_dpop_nonce error and a fresh DPoP-Nonce.

verifyDpopProof never throws — failures return { valid: false, reason } with a stable DpopFailureReason code (the proof's nonce is also surfaced on a nonce_mismatch). On success it returns { valid: true, jti, jkt, nonce? }.

Out of scope

  • Replay detection storage (the caller owns the jti cache).
  • DPoP-Nonce issuance and rotation, and emitting the use_dpop_nonce error (the caller owns the nonce lifecycle; this lib only checks the proof's nonce against the expectedNonce it is given).
  • Access-token validation beyond the DPoP binding checks.

License

ISC