~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs

0
0
main
77 lines · 3.9 KB Raw History
// node --test javascript/src/oauth/renew-once.test.mjs   (skips without Chrome, or when esm.sh is unreachable)
// The real BrowserOAuthClient (esm.sh, the pin examples/web/hello uses) in headless Chrome, signed in at a fake
// authorization server + PDS (javascript/src/oauth-server/fake.mjs), and two more pages of the same origin (frames:
// their own client and memory, the same IndexedDB session and Web Locks). Forced order: page 2's token is refused,
// page 1 renews it, and only then does page 2 go on with its refused token: sessionXrpc (renewOnce) must take the
// saved one, not refresh again (refresh tokens rotate). The library's own fetchHandler refreshes twice here.
import assert from "node:assert/strict";
import { test } from "node:test";

import { browse, CHROME } from "../../../docs/browser.mjs";
import { fakeAtproto } from "../oauth-server/fake.mjs";

const ROOT = new URL("../../../", import.meta.url).pathname;
const CLIENT = "https://esm.sh/@atproto/oauth-client-browser@0.5.14";
const online = await fetch(CLIENT, { signal: AbortSignal.timeout(5000) }).then((r) => r.ok, () => false);

// "/" (and "/?frame=<n>"): a page with its own BrowserOAuthClient; page.holdNextRefusal() holds its next refused
// response until released
const page = (fake) => `<!DOCTYPE html><script type="importmap">{"imports":{"atlibs/":"/javascript/src/","@atproto/oauth-client-browser":"${CLIENT}"}}</script>
<script type="module">
import { BrowserOAuthClient } from "@atproto/oauth-client-browser";
import { sessionXrpc } from "atlibs/run-module/host-call.js";
let hold;
const fetch = async (input, init) => {
	const response = await globalThis.fetch(input, init);
	if (hold && response.headers.get("www-authenticate")?.includes("invalid_token")) {
		const held = hold;
		hold = undefined;
		held.reached();
		await held.go;
	}
	return response;
};
const client = new BrowserOAuthClient({ handleResolver: "${fake}", plcDirectoryUrl: "${fake}", allowHttp: true, fetch });
window.page = {
	ready: client.init(),
	signIn: (handle) => void client.signIn(handle).catch(() => {}),
	holdNextRefusal() {
		const held = {};
		const reached = new Promise((ok) => (held.reached = ok));
		const go = new Promise((ok) => (held.go = ok));
		hold = { reached: held.reached, go };
		return { reached, release: held.go };
	},
	getSession: async (did) => sessionXrpc(await client.restore(did))("com.atproto.server.getSession"),
};
</script>`;

test("pages sharing a BrowserOAuthClient session renew a refused token once (sessionXrpc)", { skip: (!CHROME && "no Chrome") || (!online && `${CLIENT} unreachable`) }, async () => {
	const fake = await fakeAtproto();
	try {
		await browse(
			async ({ on, open, evaluate }) => {
				await open("/");
				await evaluate("page.ready");
				const back = new Promise((ok) => on("Page.loadEventFired", ok));
				await evaluate("page.signIn('alice.test')"); // to the fake's authorize, which approves and redirects back
				await back;
				const did = await evaluate("page.ready.then((signedIn) => signedIn?.session.did)");
				assert.equal(did, fake.did("alice"));
				await evaluate(`Promise.all([1, 2].map((n) => new Promise((ok) => { const f = document.createElement("iframe"); f.onload = ok; f.src = "/?frame=" + n; document.body.append(f) })))`);
				await evaluate("Promise.all([frames[0], frames[1]].map((f) => f.page.ready))");

				fake.expireAll();
				fake.refreshes = 0;
				await evaluate(`(window.held = frames[1].page.holdNextRefusal(), window.second = frames[1].page.getSession(${JSON.stringify(did)}), held.reached.then(() => true))`);
				assert.equal(await evaluate(`frames[0].page.getSession(${JSON.stringify(did)}).then((s) => s.did)`), did);
				assert.equal(fake.refreshes, 1, "page 1 renewed the refused token");
				assert.equal(await evaluate("(held.release(), second.then((s) => s.did))"), did);
				assert.equal(fake.refreshes, 1, "page 2 used page 1's renewal: one refresh across pages");
			},
			{ root: ROOT, index: page(fake.base) },
		);
	} finally {
		await fake.close();
	}
});