loopbackServer

tcmts.atproto.loopbackServer · #l33vbmwi · term · runs on the server

uses node:http

A server on 127.0.0.1 (a port of its own) for the browser to come back to from signing in: its port, and the query it comes back with (or a timeout, after waitMs). With state, only an answer with that state is taken: any other request (another program on this machine's) is turned away, and the sign-in goes on waiting.

import { Refused } from "#codebase/tcmts/atproto";
import http from "node:http";

export async function loopbackServer(
  waitMs = 5 * 60_000,
  state?: string,
): Promise<{ port: number; answer: Promise<URLSearchParams>; close(): void }> {
  let done: (q: URLSearchParams) => void = () => undefined;
  let fail: (e: Error) => void = () => undefined;
  const answer = new Promise<URLSearchParams>((res, rej) => ((done = res), (fail = rej)));
  const server = http.createServer((req, res) => {
    const url = new URL(req.url ?? "/", "http://127.0.0.1");
    if (url.pathname !== "/callback") return void res.writeHead(404).end();
    if (state !== undefined && url.searchParams.get("state") !== state)
      return void res
        .writeHead(400, { "content-type": "text/plain" })
        .end("not this sign-in's answer");
    const failed = url.searchParams.get("error");
    res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
    res.end(
      `<!doctype html><meta charset="utf-8"><title>tcm</title><body style="font: 16px system-ui; margin: 4em auto; max-width: 30em">` +
        (failed
          ? `<p>Signing in didn't work: ${failed.replace(/[<&]/g, "")}.</p>`
          : `<p>Signed in. You can close this tab and go back to tcm.</p>`),
    );
    done(url.searchParams);
  });
  await new Promise<void>((r) => server.listen(0, "127.0.0.1", r));
  const timer = setTimeout(
    () => fail(new Refused("signing in took too long (5 minutes): try again")),
    waitMs,
  );
  return {
    port: (server.address() as { port: number }).port,
    answer,
    close: () => {
      clearTimeout(timer);
      server.close();
      server.closeAllConnections();
    },
  };
}

uses

used by