Loading CHANGES.md +8 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,15 @@ To be released. - Added `NodeInfoDispatcher` type. - Added `nodeInfoToJson()` function. - Implemented [WebFinger] client. - Added `lookupWebFinger()` function. - `Federation.handle()` now responds with `Access-Control-Allow-Origin: *` header for WebFinger requests. [NodeInfo]: https://nodeinfo.diaspora.software/ [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033 [#1]: https://github.com/dahlia/fedify/issues/1 Loading docs/manual/actor.md +5 −0 Original line number Diff line number Diff line Loading @@ -46,9 +46,14 @@ In the above example, the `~Federation.setActorDispatcher()` method registers an actor dispatcher for the `/users/{handle}` path. This pattern syntax follows the [URI Template] specification. > [!TIP] > By regisrtering the actor dispatcher, `Federation.handle()` automatically > deals with [WebFinger] requests for the actor. [actors]: https://www.w3.org/TR/activitystreams-core/#actors [activities]: https://www.w3.org/TR/activitystreams-core/#activities [URI Template]: https://datatracker.ietf.org/doc/html/rfc6570 [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033 Key properties of an `Actor` Loading webfinger/handler.test.ts +2 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,8 @@ Deno.test("handleWebFinger()", async () => { onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); const expected = { subject: "acct:someone@example.com", aliases: [ Loading webfinger/handler.ts +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ export async function handleWebFinger<TContextData>( return new Response(JSON.stringify(jrd), { headers: { "Content-Type": "application/jrd+json", "Access-Control-Allow-Origin": "*", }, }); } webfinger/lookup.test.ts 0 → 100644 +67 −0 Original line number Diff line number Diff line import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts"; import { assertEquals } from "jsr:@std/assert@^0.218.2"; import { ResourceDescriptor } from "./jrd.ts"; import { lookupWebFinger } from "./lookup.ts"; Deno.test("lookupWebFinger()", async (t) => { mf.install(); await t.step("invalid resource", async () => { assertEquals(await lookupWebFinger("acct:johndoe"), null); assertEquals(await lookupWebFinger(new URL("acct:johndoe")), null); assertEquals(await lookupWebFinger("acct:johndoe@"), null); assertEquals(await lookupWebFinger(new URL("acct:johndoe@")), null); }); mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response("", { status: 404 }); }); await t.step("not found", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), null); assertEquals(await lookupWebFinger("https://example.com/foo"), null); }); const expected: ResourceDescriptor = { subject: "acct:johndoe@example.com", links: [], }; mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals( req.url, "https://example.com/.well-known/webfinger?resource=acct%3Ajohndoe%40example.com", ); return new Response(JSON.stringify(expected)); }); await t.step("acct", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), expected); }); const expected2: ResourceDescriptor = { subject: "https://example.com/foo", links: [], }; mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals( req.url, "https://example.com/.well-known/webfinger?resource=https%3A%2F%2Fexample.com%2Ffoo", ); return new Response(JSON.stringify(expected2)); }); await t.step("https", async () => { assertEquals(await lookupWebFinger("https://example.com/foo"), expected2); }); mf.mock("GET@/.well-known/webfinger", (_req) => { return new Response("not json"); }); await t.step("invalid response", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), null); }); mf.uninstall(); }); Loading
CHANGES.md +8 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,15 @@ To be released. - Added `NodeInfoDispatcher` type. - Added `nodeInfoToJson()` function. - Implemented [WebFinger] client. - Added `lookupWebFinger()` function. - `Federation.handle()` now responds with `Access-Control-Allow-Origin: *` header for WebFinger requests. [NodeInfo]: https://nodeinfo.diaspora.software/ [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033 [#1]: https://github.com/dahlia/fedify/issues/1 Loading
docs/manual/actor.md +5 −0 Original line number Diff line number Diff line Loading @@ -46,9 +46,14 @@ In the above example, the `~Federation.setActorDispatcher()` method registers an actor dispatcher for the `/users/{handle}` path. This pattern syntax follows the [URI Template] specification. > [!TIP] > By regisrtering the actor dispatcher, `Federation.handle()` automatically > deals with [WebFinger] requests for the actor. [actors]: https://www.w3.org/TR/activitystreams-core/#actors [activities]: https://www.w3.org/TR/activitystreams-core/#activities [URI Template]: https://datatracker.ietf.org/doc/html/rfc6570 [WebFinger]: https://datatracker.ietf.org/doc/html/rfc7033 Key properties of an `Actor` Loading
webfinger/handler.test.ts +2 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,8 @@ Deno.test("handleWebFinger()", async () => { onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); const expected = { subject: "acct:someone@example.com", aliases: [ Loading
webfinger/handler.ts +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ export async function handleWebFinger<TContextData>( return new Response(JSON.stringify(jrd), { headers: { "Content-Type": "application/jrd+json", "Access-Control-Allow-Origin": "*", }, }); }
webfinger/lookup.test.ts 0 → 100644 +67 −0 Original line number Diff line number Diff line import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts"; import { assertEquals } from "jsr:@std/assert@^0.218.2"; import { ResourceDescriptor } from "./jrd.ts"; import { lookupWebFinger } from "./lookup.ts"; Deno.test("lookupWebFinger()", async (t) => { mf.install(); await t.step("invalid resource", async () => { assertEquals(await lookupWebFinger("acct:johndoe"), null); assertEquals(await lookupWebFinger(new URL("acct:johndoe")), null); assertEquals(await lookupWebFinger("acct:johndoe@"), null); assertEquals(await lookupWebFinger(new URL("acct:johndoe@")), null); }); mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals(new URL(req.url).host, "example.com"); return new Response("", { status: 404 }); }); await t.step("not found", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), null); assertEquals(await lookupWebFinger("https://example.com/foo"), null); }); const expected: ResourceDescriptor = { subject: "acct:johndoe@example.com", links: [], }; mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals( req.url, "https://example.com/.well-known/webfinger?resource=acct%3Ajohndoe%40example.com", ); return new Response(JSON.stringify(expected)); }); await t.step("acct", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), expected); }); const expected2: ResourceDescriptor = { subject: "https://example.com/foo", links: [], }; mf.mock("GET@/.well-known/webfinger", (req) => { assertEquals( req.url, "https://example.com/.well-known/webfinger?resource=https%3A%2F%2Fexample.com%2Ffoo", ); return new Response(JSON.stringify(expected2)); }); await t.step("https", async () => { assertEquals(await lookupWebFinger("https://example.com/foo"), expected2); }); mf.mock("GET@/.well-known/webfinger", (_req) => { return new Response("not json"); }); await t.step("invalid response", async () => { assertEquals(await lookupWebFinger("acct:johndoe@example.com"), null); }); mf.uninstall(); });