resolveHandle

tcmts.atproto.resolveHandle · #5boonho2 · term · runs on the server

uses node:dns/promises

A handle's DID: from its DNS (_atproto.<handle>), else its HTTPS well-known (or the directory, if one is set).

import { directory, getJson, guardedFetch, HOSTNAME, isDid, Refused } from "#codebase/tcmts/atproto";
import dns from "node:dns/promises";

export async function resolveHandle(handle: string): Promise<string> {
  const h = handle.trim().replace(/^@/, "").toLowerCase();
  if (!HOSTNAME.test(h))
    throw new Refused(`${h} isn't a handle: a domain name, like alice.example.com`);
  const dir = directory();
  if (dir)
    return (
      await getJson<{ did: string }>(
        `${dir}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(h)}`,
      )
    ).did;
  const txt = await dns.resolveTxt(`_atproto.${h}`).catch(() => [] as string[][]);
  const fromDns = txt.map((t) => t.join("")).find((t) => t.startsWith("did="));
  if (fromDns && isDid(fromDns.slice(4))) return fromDns.slice(4);
  // (a server the handle names: asked as any is that someone else names)
  const res = await guardedFetch(`https://${h}/.well-known/atproto-did`, `${h}'s server`).catch(
    () => undefined,
  );
  const did = res?.ok ? (await res.text()).trim() : "";
  if (isDid(did)) return did;
  throw new Refused(`no AT Protocol account has the handle ${h}`);
}

uses

used by