diff --git a/index.js b/index.js index 9f2d331..de05988 100644 --- a/index.js +++ b/index.js @@ -721,19 +721,13 @@ export default class ActivityPubEndpoint { ); // Resolve the remote actor to get their inbox - // Try authenticated document loader first (for Authorized Fetch servers), - // fall back to unsigned if that fails (some servers reject signed GETs) + // lookupWithSecurity handles signed→unsigned fallback automatically const documentLoader = await ctx.getDocumentLoader({ identifier: handle, }); - let remoteActor = await lookupWithSecurity(ctx, actorUrl, { + const remoteActor = await lookupWithSecurity(ctx, actorUrl, { documentLoader, }); - if (!remoteActor) { - // Retry without authentication — some servers (e.g., tags.pub) - // may reject or mishandle signed GET requests - remoteActor = await lookupWithSecurity(ctx, actorUrl); - } if (!remoteActor) { return { ok: false, error: "Could not resolve remote actor" }; } diff --git a/lib/controllers/resolve.js b/lib/controllers/resolve.js index 466acde..4cf3f25 100644 --- a/lib/controllers/resolve.js +++ b/lib/controllers/resolve.js @@ -60,7 +60,8 @@ export function resolveController(mountPath, plugin) { let object; try { - object = await lookupWithSecurity(ctx,lookupInput, { documentLoader }); + // lookupWithSecurity handles signed→unsigned fallback automatically + object = await lookupWithSecurity(ctx, lookupInput, { documentLoader }); } catch (error) { console.warn( `[resolve] lookupObject failed for "${query}":`, diff --git a/lib/lookup-helpers.js b/lib/lookup-helpers.js index 149c932..c542fc5 100644 --- a/lib/lookup-helpers.js +++ b/lib/lookup-helpers.js @@ -14,14 +14,36 @@ * Using `crossOrigin: "ignore"` tells Fedify to silently discard objects * whose id doesn't match the fetch origin, rather than throwing. * + * When an authenticated document loader is provided (for Authorized Fetch + * compatibility), the lookup is tried with it first. If it fails (some + * servers like tags.pub return 400 for signed GETs), a fallback to the + * default unsigned loader is attempted automatically. + * * @param {object} ctx - Fedify Context * @param {string|URL} input - URL or handle to look up * @param {object} [options] - Additional options passed to lookupObject * @returns {Promise} Resolved object or null */ -export function lookupWithSecurity(ctx, input, options = {}) { - return ctx.lookupObject(input, { - crossOrigin: "ignore", - ...options, - }); +export async function lookupWithSecurity(ctx, input, options = {}) { + const baseOptions = { crossOrigin: "ignore", ...options }; + + let result = null; + try { + result = await ctx.lookupObject(input, baseOptions); + } catch { + // signed lookup threw — fall through to unsigned + } + + // If signed lookup failed and we used a custom documentLoader, + // retry without it (unsigned GET) + if (!result && options.documentLoader) { + try { + const { documentLoader: _, ...unsignedOptions } = baseOptions; + result = await ctx.lookupObject(input, unsignedOptions); + } catch { + // unsigned also failed — return null + } + } + + return result; } diff --git a/package.json b/package.json index 718f1a8..98784ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rmdes/indiekit-endpoint-activitypub", - "version": "3.7.3", + "version": "3.7.4", "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.", "keywords": [ "indiekit",