import { readdirSync, readFileSync } from "node:fs"; import path from "node:path"; import type { Nsid } from "@atcute/lexicons/syntax"; import { describe, expect, it } from "vitest"; import { missingPermissions, type Permission } from "$lib/auth/scopes"; import metadata, { loopbackClientId, loopbackScope } from "$lib/oauth-client-metadata"; const KNOT_WRITE_LXMS = [ "com.atproto.repo.createRecord", "com.atproto.repo.putRecord", "com.atproto.repo.deleteRecord", "com.atproto.repo.uploadBlob", "sh.tangled.repo.pull.merge" ] as const; describe("oauth client scope", () => { it("grants any-audience RPC scopes for every lxm used in knot writes", () => { const granted = new Set(metadata.scope.split(" ")); for (const lxm of KNOT_WRITE_LXMS) { expect( granted.has(`rpc:${lxm}?aud=*`), `The grant must include rpc:${lxm}?aud=* or the PDS rejects tokens minted for it.` ).toBe(true); } }); it("grants repo scope over org.tangled.graph.attestation", () => { expect(metadata.scope.split(" ")).toContain("repo:org.tangled.graph.attestation"); }); }); const lexicons = path.resolve(import.meta.dirname, "../../../lexicons/org/tangled"); const recordCollections = (dir: string): string[] => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { const file = path.join(dir, entry.name); if (entry.isDirectory()) return recordCollections(file); const lexicon = JSON.parse(readFileSync(file, "utf8")) as { id: string; defs: { main?: { type?: string } }; }; return lexicon.defs.main?.type === "record" ? [lexicon.id] : []; }); describe("liar migration scope", () => { it("grants repo scope over every org.tangled record, since the import writes whatever the repo holds", () => { const wanted = recordCollections(lexicons).map((collection): Permission => ({ resource: "repo", collection: collection as Nsid, actions: ["create"] })); expect(wanted.length).toBeGreaterThan(20); expect(missingPermissions(metadata.scope, wanted)).toEqual([]); }); }); describe("loopbackClientId", () => { it("keeps authorize URLs within chromium's referrer length limit", () => { const url = new URL("https://tngl.sh/oauth/authorize"); url.searchParams.set( "client_id", loopbackClientId("http://127.0.0.1:5173/oauth/callback", loopbackScope) ); url.searchParams.set( "request_uri", `urn:ietf:params:oauth:request_uri:req-${"0".repeat(32)}` ); expect(url.href.length).toBeLessThanOrEqual(4096); }); });