fix: centralize unsigned fallback in lookupWithSecurity
Some servers (e.g., tags.pub) return 400 for signed GET requests. Previously only followActor had an unsigned fallback — all other callers (resolve, unfollowActor, profile viewer, messages, post detail, OG unfurl) would silently fail. Fix: moved the fallback logic into lookupWithSecurity itself. When an authenticated documentLoader is provided and the lookup fails, it automatically retries without the loader (unsigned GET). This fixes ALL AP resolution paths in one place — resolve, follow, unfollow, profile viewing, message sending, quote fetching. Removed individual fallbacks in followActor and resolve controller since the central helper now handles it.
This commit is contained in:
@@ -721,19 +721,13 @@ export default class ActivityPubEndpoint {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Resolve the remote actor to get their inbox
|
// Resolve the remote actor to get their inbox
|
||||||
// Try authenticated document loader first (for Authorized Fetch servers),
|
// lookupWithSecurity handles signed→unsigned fallback automatically
|
||||||
// fall back to unsigned if that fails (some servers reject signed GETs)
|
|
||||||
const documentLoader = await ctx.getDocumentLoader({
|
const documentLoader = await ctx.getDocumentLoader({
|
||||||
identifier: handle,
|
identifier: handle,
|
||||||
});
|
});
|
||||||
let remoteActor = await lookupWithSecurity(ctx, actorUrl, {
|
const remoteActor = await lookupWithSecurity(ctx, actorUrl, {
|
||||||
documentLoader,
|
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) {
|
if (!remoteActor) {
|
||||||
return { ok: false, error: "Could not resolve remote actor" };
|
return { ok: false, error: "Could not resolve remote actor" };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ export function resolveController(mountPath, plugin) {
|
|||||||
let object;
|
let object;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
object = await lookupWithSecurity(ctx,lookupInput, { documentLoader });
|
// lookupWithSecurity handles signed→unsigned fallback automatically
|
||||||
|
object = await lookupWithSecurity(ctx, lookupInput, { documentLoader });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[resolve] lookupObject failed for "${query}":`,
|
`[resolve] lookupObject failed for "${query}":`,
|
||||||
|
|||||||
+27
-5
@@ -14,14 +14,36 @@
|
|||||||
* Using `crossOrigin: "ignore"` tells Fedify to silently discard objects
|
* Using `crossOrigin: "ignore"` tells Fedify to silently discard objects
|
||||||
* whose id doesn't match the fetch origin, rather than throwing.
|
* 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 {object} ctx - Fedify Context
|
||||||
* @param {string|URL} input - URL or handle to look up
|
* @param {string|URL} input - URL or handle to look up
|
||||||
* @param {object} [options] - Additional options passed to lookupObject
|
* @param {object} [options] - Additional options passed to lookupObject
|
||||||
* @returns {Promise<object|null>} Resolved object or null
|
* @returns {Promise<object|null>} Resolved object or null
|
||||||
*/
|
*/
|
||||||
export function lookupWithSecurity(ctx, input, options = {}) {
|
export async function lookupWithSecurity(ctx, input, options = {}) {
|
||||||
return ctx.lookupObject(input, {
|
const baseOptions = { crossOrigin: "ignore", ...options };
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"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.",
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
|
|||||||
Reference in New Issue
Block a user