Loading CHANGES.md +8 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,13 @@ Version 0.10.0 To be released. - Besides RSA-PKCS#1-v1.5, Fedify now supports Ed25519 for signing and verifying the activities. [[#55]] - Added an optional parameter to `generateCryptoKeyPair()` function, `algorithm`, which can be either `"RSASSA-PKCS1-v1_5"` or `"Ed25519"`. - The `importJwk()` function now accepts Ed25519 keys. - Deprecated `treatHttps` option in `FederationParameters` interface. Instead, use the [x-forwarded-fetch] library to recognize the `X-Forwarded-Host` and `X-Forwarded-Proto` headers. Loading @@ -19,6 +26,7 @@ To be released. `following`, `followers`, `outbox`, `manuallyApprovesFollowers`, and `url`. [#55]: https://github.com/dahlia/fedify/issues/55 [x-forwarded-fetch]: https://github.com/dahlia/x-forwarded-fetch Loading deno.json +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ "fast-check": "npm:fast-check@^3.18.0", "jsonld": "npm:jsonld@^8.3.2", "mock_fetch": "https://deno.land/x/mock_fetch@0.3.0/mod.ts", "multibase": "npm:multibase@^4.0.6", "uri-template-router": "npm:uri-template-router@^0.0.16", "url-template": "npm:url-template@^3.1.1" }, Loading federation/handler.test.ts +4 −4 Original line number Diff line number Diff line import { assert, assertEquals, assertFalse } from "@std/assert"; import { createRequestContext } from "../testing/context.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { publicKey2 } from "../testing/keys.ts"; import { rsaPublicKey2 } from "../testing/keys.ts"; import { type Activity, Create, Loading Loading @@ -221,7 +221,7 @@ Deno.test("handleActor()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleActor( Loading Loading @@ -466,7 +466,7 @@ Deno.test("handleObject()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleObject( Loading Loading @@ -689,7 +689,7 @@ Deno.test("handleCollection()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleCollection( Loading federation/middleware.test.ts +24 −24 Original line number Diff line number Diff line Loading @@ -14,10 +14,10 @@ import { } from "../runtime/docloader.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { privateKey2, privateKey3, publicKey2, publicKey3, rsaPrivateKey2, rsaPrivateKey3, rsaPublicKey2, rsaPublicKey3, } from "../testing/keys.ts"; import { Create, Note, Person } from "../vocab/vocab.ts"; import type { Context } from "./context.ts"; Loading Loading @@ -105,8 +105,8 @@ Deno.test("Federation.createContext()", async (t) => { federation .setActorDispatcher("/users/{handle}", () => new Person({})) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); assertEquals( ctx.getActorUri("handle"), Loading @@ -127,7 +127,7 @@ Deno.test("Federation.createContext()", async (t) => { ); assertEquals( await ctx.getActorKey("handle"), publicKey2.clone({ rsaPublicKey2.clone({ id: new URL("https://example.com/users/handle#main-key"), owner: new URL("https://example.com/users/handle"), }), Loading @@ -140,7 +140,7 @@ Deno.test("Federation.createContext()", async (t) => { }); const loader2 = ctx.getDocumentLoader({ keyId: new URL("https://example.com/key2"), privateKey: privateKey2, privateKey: rsaPrivateKey2, }); assertEquals(await loader2("https://example.com/object"), { contextUrl: null, Loading Loading @@ -269,36 +269,36 @@ Deno.test("Federation.createContext()", async (t) => { const signedReq = await signRequest( new Request("https://example.com/"), privateKey2, publicKey2.id!, rsaPrivateKey2, rsaPublicKey2.id!, ); const signedCtx = federation.createContext(signedReq, 456); assertEquals(signedCtx.request, signedReq); assertEquals(signedCtx.url, new URL("https://example.com/")); assertEquals(signedCtx.data, 456); assertEquals(await signedCtx.getSignedKey(), publicKey2); assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2); assertEquals(await signedCtx.getSignedKeyOwner(), null); // Multiple calls should return the same result: assertEquals(await signedCtx.getSignedKey(), publicKey2); assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2); assertEquals(await signedCtx.getSignedKeyOwner(), null); const signedReq2 = await signRequest( new Request("https://example.com/"), privateKey3, publicKey3.id!, rsaPrivateKey3, rsaPublicKey3.id!, ); const signedCtx2 = federation.createContext(signedReq2, 456); assertEquals(signedCtx2.request, signedReq2); assertEquals(signedCtx2.url, new URL("https://example.com/")); assertEquals(signedCtx2.data, 456); assertEquals(await signedCtx2.getSignedKey(), publicKey3); assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3); const expectedOwner = await lookupObject( "https://example.com/person2", { documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader }, ); assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner); // Multiple calls should return the same result: assertEquals(await signedCtx2.getSignedKey(), publicKey3); assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3); assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner); federation.setActorDispatcher( Loading Loading @@ -344,7 +344,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { mf.mock("GET@/key2", async () => { return new Response( JSON.stringify( await publicKey2.toJsonLd({ contextLoader: mockDocumentLoader }), await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader }), ), { headers: { "Content-Type": "application/activity+json" } }, ); Loading Loading @@ -399,8 +399,8 @@ Deno.test("Federation.setInboxListeners()", async (t) => { (_, handle) => handle === "john" ? new Person({}) : null, ) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); response = await federation.fetch( new Request("https://example.com/inbox", { method: "POST" }), Loading Loading @@ -435,7 +435,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); response = await federation.fetch(request, { contextData: undefined }); Loading @@ -460,7 +460,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); response = await federation.fetch(request, { contextData: undefined }); Loading Loading @@ -493,8 +493,8 @@ Deno.test("Federation.setInboxListeners()", async (t) => { (_, handle) => handle === "john" ? new Person({}) : null, ) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); const errors: unknown[] = []; federation.setInboxListeners("/users/{handle}/inbox", "/inbox") Loading @@ -517,7 +517,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); const response = await federation.fetch(request, { Loading federation/send.test.ts +7 −7 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import * as mf from "mock_fetch"; import { verifyRequest } from "../sig/http.ts"; import { doesActorOwnKey } from "../sig/owner.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { privateKey2, publicKey2 } from "../testing/keys.ts"; import { rsaPrivateKey2, rsaPublicKey2 } from "../testing/keys.ts"; import type { Actor } from "../vocab/actor.ts"; import { Activity, Loading Loading @@ -162,8 +162,8 @@ Deno.test("sendActivity()", async (t) => { }); await sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox"), contextLoader: mockDocumentLoader, headers: new Headers({ Loading Loading @@ -197,8 +197,8 @@ Deno.test("sendActivity()", async (t) => { () => sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox2"), contextLoader: mockDocumentLoader, }), Loading @@ -213,8 +213,8 @@ Deno.test("sendActivity()", async (t) => { () => sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox2"), contextLoader: mockDocumentLoader, }), Loading Loading
CHANGES.md +8 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,13 @@ Version 0.10.0 To be released. - Besides RSA-PKCS#1-v1.5, Fedify now supports Ed25519 for signing and verifying the activities. [[#55]] - Added an optional parameter to `generateCryptoKeyPair()` function, `algorithm`, which can be either `"RSASSA-PKCS1-v1_5"` or `"Ed25519"`. - The `importJwk()` function now accepts Ed25519 keys. - Deprecated `treatHttps` option in `FederationParameters` interface. Instead, use the [x-forwarded-fetch] library to recognize the `X-Forwarded-Host` and `X-Forwarded-Proto` headers. Loading @@ -19,6 +26,7 @@ To be released. `following`, `followers`, `outbox`, `manuallyApprovesFollowers`, and `url`. [#55]: https://github.com/dahlia/fedify/issues/55 [x-forwarded-fetch]: https://github.com/dahlia/x-forwarded-fetch Loading
deno.json +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ "fast-check": "npm:fast-check@^3.18.0", "jsonld": "npm:jsonld@^8.3.2", "mock_fetch": "https://deno.land/x/mock_fetch@0.3.0/mod.ts", "multibase": "npm:multibase@^4.0.6", "uri-template-router": "npm:uri-template-router@^0.0.16", "url-template": "npm:url-template@^3.1.1" }, Loading
federation/handler.test.ts +4 −4 Original line number Diff line number Diff line import { assert, assertEquals, assertFalse } from "@std/assert"; import { createRequestContext } from "../testing/context.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { publicKey2 } from "../testing/keys.ts"; import { rsaPublicKey2 } from "../testing/keys.ts"; import { type Activity, Create, Loading Loading @@ -221,7 +221,7 @@ Deno.test("handleActor()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleActor( Loading Loading @@ -466,7 +466,7 @@ Deno.test("handleObject()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleObject( Loading Loading @@ -689,7 +689,7 @@ Deno.test("handleCollection()", async () => { onUnauthorizedCalled = null; context = createRequestContext<void>({ ...context, getSignedKey: () => Promise.resolve(publicKey2), getSignedKey: () => Promise.resolve(rsaPublicKey2), getSignedKeyOwner: () => Promise.resolve(new Person({})), }); response = await handleCollection( Loading
federation/middleware.test.ts +24 −24 Original line number Diff line number Diff line Loading @@ -14,10 +14,10 @@ import { } from "../runtime/docloader.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { privateKey2, privateKey3, publicKey2, publicKey3, rsaPrivateKey2, rsaPrivateKey3, rsaPublicKey2, rsaPublicKey3, } from "../testing/keys.ts"; import { Create, Note, Person } from "../vocab/vocab.ts"; import type { Context } from "./context.ts"; Loading Loading @@ -105,8 +105,8 @@ Deno.test("Federation.createContext()", async (t) => { federation .setActorDispatcher("/users/{handle}", () => new Person({})) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); assertEquals( ctx.getActorUri("handle"), Loading @@ -127,7 +127,7 @@ Deno.test("Federation.createContext()", async (t) => { ); assertEquals( await ctx.getActorKey("handle"), publicKey2.clone({ rsaPublicKey2.clone({ id: new URL("https://example.com/users/handle#main-key"), owner: new URL("https://example.com/users/handle"), }), Loading @@ -140,7 +140,7 @@ Deno.test("Federation.createContext()", async (t) => { }); const loader2 = ctx.getDocumentLoader({ keyId: new URL("https://example.com/key2"), privateKey: privateKey2, privateKey: rsaPrivateKey2, }); assertEquals(await loader2("https://example.com/object"), { contextUrl: null, Loading Loading @@ -269,36 +269,36 @@ Deno.test("Federation.createContext()", async (t) => { const signedReq = await signRequest( new Request("https://example.com/"), privateKey2, publicKey2.id!, rsaPrivateKey2, rsaPublicKey2.id!, ); const signedCtx = federation.createContext(signedReq, 456); assertEquals(signedCtx.request, signedReq); assertEquals(signedCtx.url, new URL("https://example.com/")); assertEquals(signedCtx.data, 456); assertEquals(await signedCtx.getSignedKey(), publicKey2); assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2); assertEquals(await signedCtx.getSignedKeyOwner(), null); // Multiple calls should return the same result: assertEquals(await signedCtx.getSignedKey(), publicKey2); assertEquals(await signedCtx.getSignedKey(), rsaPublicKey2); assertEquals(await signedCtx.getSignedKeyOwner(), null); const signedReq2 = await signRequest( new Request("https://example.com/"), privateKey3, publicKey3.id!, rsaPrivateKey3, rsaPublicKey3.id!, ); const signedCtx2 = federation.createContext(signedReq2, 456); assertEquals(signedCtx2.request, signedReq2); assertEquals(signedCtx2.url, new URL("https://example.com/")); assertEquals(signedCtx2.data, 456); assertEquals(await signedCtx2.getSignedKey(), publicKey3); assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3); const expectedOwner = await lookupObject( "https://example.com/person2", { documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader }, ); assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner); // Multiple calls should return the same result: assertEquals(await signedCtx2.getSignedKey(), publicKey3); assertEquals(await signedCtx2.getSignedKey(), rsaPublicKey3); assertEquals(await signedCtx2.getSignedKeyOwner(), expectedOwner); federation.setActorDispatcher( Loading Loading @@ -344,7 +344,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { mf.mock("GET@/key2", async () => { return new Response( JSON.stringify( await publicKey2.toJsonLd({ contextLoader: mockDocumentLoader }), await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader }), ), { headers: { "Content-Type": "application/activity+json" } }, ); Loading Loading @@ -399,8 +399,8 @@ Deno.test("Federation.setInboxListeners()", async (t) => { (_, handle) => handle === "john" ? new Person({}) : null, ) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); response = await federation.fetch( new Request("https://example.com/inbox", { method: "POST" }), Loading Loading @@ -435,7 +435,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); response = await federation.fetch(request, { contextData: undefined }); Loading @@ -460,7 +460,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); response = await federation.fetch(request, { contextData: undefined }); Loading Loading @@ -493,8 +493,8 @@ Deno.test("Federation.setInboxListeners()", async (t) => { (_, handle) => handle === "john" ? new Person({}) : null, ) .setKeyPairDispatcher(() => ({ privateKey: privateKey2, publicKey: publicKey2.publicKey!, privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey!, })); const errors: unknown[] = []; federation.setInboxListeners("/users/{handle}/inbox", "/inbox") Loading @@ -517,7 +517,7 @@ Deno.test("Federation.setInboxListeners()", async (t) => { }); request = await signRequest( request, privateKey2, rsaPrivateKey2, new URL("https://example.com/key2"), ); const response = await federation.fetch(request, { Loading
federation/send.test.ts +7 −7 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import * as mf from "mock_fetch"; import { verifyRequest } from "../sig/http.ts"; import { doesActorOwnKey } from "../sig/owner.ts"; import { mockDocumentLoader } from "../testing/docloader.ts"; import { privateKey2, publicKey2 } from "../testing/keys.ts"; import { rsaPrivateKey2, rsaPublicKey2 } from "../testing/keys.ts"; import type { Actor } from "../vocab/actor.ts"; import { Activity, Loading Loading @@ -162,8 +162,8 @@ Deno.test("sendActivity()", async (t) => { }); await sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox"), contextLoader: mockDocumentLoader, headers: new Headers({ Loading Loading @@ -197,8 +197,8 @@ Deno.test("sendActivity()", async (t) => { () => sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox2"), contextLoader: mockDocumentLoader, }), Loading @@ -213,8 +213,8 @@ Deno.test("sendActivity()", async (t) => { () => sendActivity({ activity, privateKey: privateKey2, keyId: publicKey2.id!, privateKey: rsaPrivateKey2, keyId: rsaPublicKey2.id!, inbox: new URL("https://example.com/inbox2"), contextLoader: mockDocumentLoader, }), Loading