Loading CHANGES.md +9 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,15 @@ To be released. - Added `SendActivityOptions.excludeBaseUris` property. - Added `ExtractInboxesParameters.excludeBaseUris` property. - The time window for signature verification is now configurable. - The default time window for signature verification is now a minute (was 30 seconds). - Added `signatureTimeWindow` option to `FederationParameters` interface. - Added `VerifyOptions` interface. - The signature of the `verify()` function is revamped; it now optionally takes a `VerifyOptions` object as the second parameter. - Added more log messages using the [LogTape] library. Currently the below logger categories are used: Loading federation/handler.ts +6 −5 Original line number Diff line number Diff line Loading @@ -314,6 +314,7 @@ export interface InboxHandlerParameters<TContextData> { >; inboxErrorHandler?: InboxErrorHandler<TContextData>; onNotFound(request: Request): Response | Promise<Response>; signatureTimeWindow: Temporal.DurationLike; } export async function handleInbox<TContextData>( Loading @@ -327,6 +328,7 @@ export async function handleInbox<TContextData>( inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, }: InboxHandlerParameters<TContextData>, ): Promise<Response> { const logger = getLogger(["fedify", "federation", "inbox"]); Loading @@ -341,11 +343,10 @@ export async function handleInbox<TContextData>( return await onNotFound(request); } } const key = await verify( request, context.documentLoader, context.contextLoader, ); const key = await verify(request, { ...context, timeWindow: signatureTimeWindow, }); if (key == null) { logger.error("Failed to verify the request signature.", { handle }); const response = new Response("Failed to verify the request signature.", { Loading federation/middleware.test.ts +5 −3 Original line number Diff line number Diff line Loading @@ -36,9 +36,11 @@ Deno.test("Federation.createContext()", async (t) => { mf.mock("GET@/object", async (req) => { const v = await verify( req, mockDocumentLoader, mockDocumentLoader, Temporal.Now.instant(), { contextLoader: mockDocumentLoader, documentLoader: mockDocumentLoader, currentTime: Temporal.Now.instant(), }, ); return new Response(JSON.stringify(v != null), { headers: { "Content-Type": "application/json" }, Loading federation/middleware.ts +15 −5 Original line number Diff line number Diff line Loading @@ -108,6 +108,15 @@ export interface FederationParameters { */ onOutboxError?: OutboxErrorHandler; /** * The time window for verifying the signature of incoming requests. If the * request is older or newer than this window, it is rejected. By default, * the window is a minute. * * @since 0.9.0 */ signatureTimeWindow?: Temporal.DurationLike; // TODO: The following option should be removed, and exponential backoff // should be used instead: backoffSchedule?: Temporal.Duration[]; Loading Loading @@ -159,6 +168,7 @@ export class Federation<TContextData> { #authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; #treatHttps: boolean; #onOutboxError?: OutboxErrorHandler; #signatureTimeWindow: Temporal.DurationLike; #backoffSchedule: Temporal.Duration[]; /** Loading @@ -175,6 +185,7 @@ export class Federation<TContextData> { authenticatedDocumentLoaderFactory, treatHttps, onOutboxError, signatureTimeWindow, backoffSchedule, }: FederationParameters, ) { Loading Loading @@ -204,6 +215,7 @@ export class Federation<TContextData> { getAuthenticatedDocumentLoader; this.#onOutboxError = onOutboxError; this.#treatHttps = treatHttps ?? false; this.#signatureTimeWindow = signatureTimeWindow ?? { minutes: 1 }; this.#backoffSchedule = backoffSchedule ?? [ 3_000, 15_000, Loading Loading @@ -491,6 +503,7 @@ export class Federation<TContextData> { if (request == null) return context; let signedKey: CryptographicKey | null | undefined = undefined; let signedKeyOwner: Actor | null | undefined = undefined; const timeWindow = this.#signatureTimeWindow; const reqCtx: RequestContext<TContextData> = { ...context, request, Loading Loading @@ -554,11 +567,7 @@ export class Federation<TContextData> { }, async getSignedKey() { if (signedKey !== undefined) return signedKey; return signedKey = await verify( request, context.documentLoader, context.contextLoader, ); return signedKey = await verify(request, { ...context, timeWindow }); }, async getSignedKeyOwner() { if (signedKeyOwner !== undefined) return signedKeyOwner; Loading Loading @@ -1424,6 +1433,7 @@ export class Federation<TContextData> { inboxListeners: this.#inboxListeners, inboxErrorHandler: this.#inboxErrorHandler, onNotFound, signatureTimeWindow: this.#signatureTimeWindow, }); case "following": return await handleCollection(request, { Loading federation/send.test.ts +1 −1 Original line number Diff line number Diff line Loading @@ -141,11 +141,11 @@ Deno.test("sendActivity()", async (t) => { let request: Request | null = null; mf.mock("POST@/inbox", async (req) => { request = req; const key = await verify(req, mockDocumentLoader, mockDocumentLoader); const options = { documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }; const key = await verify(req, options); const activity = await Activity.fromJsonLd(await req.json(), options); if (key != null && await doesActorOwnKey(activity, key, options)) { verified = true; Loading Loading
CHANGES.md +9 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,15 @@ To be released. - Added `SendActivityOptions.excludeBaseUris` property. - Added `ExtractInboxesParameters.excludeBaseUris` property. - The time window for signature verification is now configurable. - The default time window for signature verification is now a minute (was 30 seconds). - Added `signatureTimeWindow` option to `FederationParameters` interface. - Added `VerifyOptions` interface. - The signature of the `verify()` function is revamped; it now optionally takes a `VerifyOptions` object as the second parameter. - Added more log messages using the [LogTape] library. Currently the below logger categories are used: Loading
federation/handler.ts +6 −5 Original line number Diff line number Diff line Loading @@ -314,6 +314,7 @@ export interface InboxHandlerParameters<TContextData> { >; inboxErrorHandler?: InboxErrorHandler<TContextData>; onNotFound(request: Request): Response | Promise<Response>; signatureTimeWindow: Temporal.DurationLike; } export async function handleInbox<TContextData>( Loading @@ -327,6 +328,7 @@ export async function handleInbox<TContextData>( inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, }: InboxHandlerParameters<TContextData>, ): Promise<Response> { const logger = getLogger(["fedify", "federation", "inbox"]); Loading @@ -341,11 +343,10 @@ export async function handleInbox<TContextData>( return await onNotFound(request); } } const key = await verify( request, context.documentLoader, context.contextLoader, ); const key = await verify(request, { ...context, timeWindow: signatureTimeWindow, }); if (key == null) { logger.error("Failed to verify the request signature.", { handle }); const response = new Response("Failed to verify the request signature.", { Loading
federation/middleware.test.ts +5 −3 Original line number Diff line number Diff line Loading @@ -36,9 +36,11 @@ Deno.test("Federation.createContext()", async (t) => { mf.mock("GET@/object", async (req) => { const v = await verify( req, mockDocumentLoader, mockDocumentLoader, Temporal.Now.instant(), { contextLoader: mockDocumentLoader, documentLoader: mockDocumentLoader, currentTime: Temporal.Now.instant(), }, ); return new Response(JSON.stringify(v != null), { headers: { "Content-Type": "application/json" }, Loading
federation/middleware.ts +15 −5 Original line number Diff line number Diff line Loading @@ -108,6 +108,15 @@ export interface FederationParameters { */ onOutboxError?: OutboxErrorHandler; /** * The time window for verifying the signature of incoming requests. If the * request is older or newer than this window, it is rejected. By default, * the window is a minute. * * @since 0.9.0 */ signatureTimeWindow?: Temporal.DurationLike; // TODO: The following option should be removed, and exponential backoff // should be used instead: backoffSchedule?: Temporal.Duration[]; Loading Loading @@ -159,6 +168,7 @@ export class Federation<TContextData> { #authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory; #treatHttps: boolean; #onOutboxError?: OutboxErrorHandler; #signatureTimeWindow: Temporal.DurationLike; #backoffSchedule: Temporal.Duration[]; /** Loading @@ -175,6 +185,7 @@ export class Federation<TContextData> { authenticatedDocumentLoaderFactory, treatHttps, onOutboxError, signatureTimeWindow, backoffSchedule, }: FederationParameters, ) { Loading Loading @@ -204,6 +215,7 @@ export class Federation<TContextData> { getAuthenticatedDocumentLoader; this.#onOutboxError = onOutboxError; this.#treatHttps = treatHttps ?? false; this.#signatureTimeWindow = signatureTimeWindow ?? { minutes: 1 }; this.#backoffSchedule = backoffSchedule ?? [ 3_000, 15_000, Loading Loading @@ -491,6 +503,7 @@ export class Federation<TContextData> { if (request == null) return context; let signedKey: CryptographicKey | null | undefined = undefined; let signedKeyOwner: Actor | null | undefined = undefined; const timeWindow = this.#signatureTimeWindow; const reqCtx: RequestContext<TContextData> = { ...context, request, Loading Loading @@ -554,11 +567,7 @@ export class Federation<TContextData> { }, async getSignedKey() { if (signedKey !== undefined) return signedKey; return signedKey = await verify( request, context.documentLoader, context.contextLoader, ); return signedKey = await verify(request, { ...context, timeWindow }); }, async getSignedKeyOwner() { if (signedKeyOwner !== undefined) return signedKeyOwner; Loading Loading @@ -1424,6 +1433,7 @@ export class Federation<TContextData> { inboxListeners: this.#inboxListeners, inboxErrorHandler: this.#inboxErrorHandler, onNotFound, signatureTimeWindow: this.#signatureTimeWindow, }); case "following": return await handleCollection(request, { Loading
federation/send.test.ts +1 −1 Original line number Diff line number Diff line Loading @@ -141,11 +141,11 @@ Deno.test("sendActivity()", async (t) => { let request: Request | null = null; mf.mock("POST@/inbox", async (req) => { request = req; const key = await verify(req, mockDocumentLoader, mockDocumentLoader); const options = { documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }; const key = await verify(req, options); const activity = await Activity.fromJsonLd(await req.json(), options); if (key != null && await doesActorOwnKey(activity, key, options)) { verified = true; Loading