dpopFetch

tcmts.atproto.dpopFetch · #2wqmto6c · term · runs on the server

uses tcmts.atproto.dpopProof, which uses node:crypto

A request with a DPoP proof (and the access token, when given), made again once with a fresh nonce when the server asks for one, as servers do.

import { dpopNonces, dpopProof } from "#codebase/tcmts/atproto";
import type crypto from "node:crypto";

export async function dpopFetch(
  key: crypto.JsonWebKey,
  url: string,
  init: RequestInit = {},
  accessToken?: string,
): Promise<Response> {
  const origin = new URL(url).origin;
  const method = init.method ?? "GET";
  const send = () =>
    fetch(url, {
      ...init,
      headers: {
        ...Object.fromEntries(new Headers(init.headers)),
        dpop: dpopProof(key, method, url, dpopNonces.get(origin), accessToken),
        ...(accessToken ? { authorization: `DPoP ${accessToken}` } : {}),
      },
    });
  const res = await send();
  const nonce = res.headers.get("dpop-nonce");
  if (nonce) dpopNonces.set(origin, nonce);
  if (!nonce || (res.status !== 400 && res.status !== 401)) return res;
  const asked =
    (res.headers.get("www-authenticate") ?? "").includes("use_dpop_nonce") ||
    (
      (await res
        .clone()
        .json()
        .catch(() => ({}))) as { error?: string }
    ).error === "use_dpop_nonce";
  return asked ? send() : res;
}

uses

used by