fix: use URL-hash ID for Mastodon account route guards
Deploy Indiekit Server / deploy (push) Successful in 1m23s

Mastodon API GET /accounts/:id/followers and /following always returned []
because the guards compared profile._id.toString() against the client-provided
id, which comes from verify_credentials via remoteActorId(profile.url) — a
sha256 hash. ObjectId and hash never match.

Patch ap-accounts-id-hash replaces profile._id.toString() with
remoteActorId(profile.url) in all three affected account routes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sven
2026-04-15 20:15:02 +02:00
parent f758fdcdf1
commit 60076225db
+49
View File
@@ -103,4 +103,53 @@ for (const f of OG_CANDIDATES) {
}
if (!ogDone) console.log(`[postinstall] ${SCRIPT}: ap-og-image — no target file found or no changes`);
// ── patch-ap-accounts-id-hash: use URL-hash ID instead of MongoDB _id ─────────
// GET /api/v1/accounts/:id, /followers, /following all checked profile._id.toString()
// against the client-provided id, but verify_credentials returns remoteActorId(profile.url)
// (sha256 hash). The ObjectId and the hash never match → followers/following always [].
const HASH_MARKER = "// [patch] ap-accounts-id-hash";
const ACCOUNTS_CANDIDATES = apPath("lib/mastodon/routes/accounts.js");
const HASH_PATCHES = [
{
old: ` if (profile && profile._id.toString() === id) {`,
new: ` if (profile && remoteActorId(profile.url) === id) { ${HASH_MARKER}`,
},
{
old: ` // Only serve followers for the local account\n if (!profile || profile._id.toString() !== id) {`,
new: ` // Only serve followers for the local account\n if (!profile || remoteActorId(profile.url) !== id) { ${HASH_MARKER}`,
},
{
old: ` // Only serve following for the local account\n if (!profile || profile._id.toString() !== id) {`,
new: ` // Only serve following for the local account\n if (!profile || remoteActorId(profile.url) !== id) { ${HASH_MARKER}`,
},
];
let hashDone = false;
for (const f of ACCOUNTS_CANDIDATES) {
if (!(await fileExists(f))) continue;
const src = await readFile(f, "utf8");
if (src.includes(HASH_MARKER)) {
console.log(`[postinstall] ${SCRIPT}: ap-accounts-id-hash already applied in ${f}`);
hashDone = true; break;
}
let updated = src;
let changed = false;
for (const { old, new: replacement } of HASH_PATCHES) {
if (updated.includes(old)) {
updated = updated.replace(old, replacement);
changed = true;
} else {
console.warn(`[postinstall] ${SCRIPT}: ap-accounts-id-hash — snippet not found in ${f}: ${old.slice(0, 60)}...`);
}
}
if (changed && updated !== src) {
await writeFile(f, updated, "utf8");
console.log(`[postinstall] ${SCRIPT}: applied ap-accounts-id-hash to ${f}`);
total++; hashDone = true; break;
}
}
if (!hashDone) console.log(`[postinstall] ${SCRIPT}: ap-accounts-id-hash — no target file found or no changes`);
console.log(`[postinstall] ${SCRIPT}: done (${total} patch(es) applied)`);