Verify profile links through reciprocal rel=me or rel=fediprofile #31

Closed
opened 2026-09-28 17:05:59 +00:00 by Michael Westergaard · 1 comment
Owner

Current behavior

FediProfile emits rel="me" for every visible profile link, but does not check for a backlink. The displayed verified flag is instead derived from VerifiedUris, populated by login/account initialization and backfill. That table also affects badge recipient matching, so it cannot simply be reused as a link-check cache.

Required behavior

  • On adding a visible profile link, and on URL changes or making a link visible, fetch its destination and check for an HTML <a> or <link> whose rel token list includes me or fediprofile and whose href resolves to the canonical FediProfile profile URL. Document the custom rel="fediprofile" form. Parse HTML/relative URLs properly; compare URI identity using the existing canonicalization rules, without treating a link to another local account or path as proof. Keep FediProfile's outgoing rel="me" links.
  • Store verification per link (and link/account generation), with last attempt, last success, result, and a reason visible to the owner. A failed backlink check removes the public verified indication. Distinguish an absent/mismatched backlink from temporary fetch failure and define a bounded expiry for stale success; transient failures must not confer indefinite verification.
  • Recheck links in a background service on a configurable, validated five-field UTC cron schedule, following existing scheduled-service conventions. Bound work per run; safely handle concurrent instances, retries, disabled/hidden/deleted links, URL changes during a check, and account deletion. Verification state changes must update the public profile and notify ActivityPub followers through the existing actor-update path.
  • Protect destination fetches against SSRF across redirects and DNS changes; limit schemes, response size, time, and redirects. Do not execute JavaScript or trust unvalidated HTML.

Migration and boundaries

  • Replace VerifiedUris as the source of the link badge. Recheck existing links rather than grandfathering them as verified. Keep authenticated account-identity evidence separate where needed: audit registration/backfill, badge import, automatic badge dispatch, and actor serialization so reciprocal link proof does not silently grant unrelated identity/authorization privileges.
  • This is independent of #25-#30. RSS/Atom feed URLs need not contain HTML backlinks; source import must not depend on this check. A verified profile link does not automatically enable an import source or replace the Bluesky ownership proof in #29.

Tests

Use a red/green functional test for the behavior change, then unit contracts for rel token parsing, URL identity, add/edit/recheck/revocation, stale results, concurrent edits, and fetch safety. Keep mocked-HTTP tests in commit CI; run real HTTP/database integration manually where required. Document the new verification meaning and cron setting.

## Current behavior FediProfile emits `rel="me"` for every visible profile link, but does not check for a backlink. The displayed `verified` flag is instead derived from `VerifiedUris`, populated by login/account initialization and backfill. That table also affects badge recipient matching, so it cannot simply be reused as a link-check cache. ## Required behavior - On adding a visible profile link, and on URL changes or making a link visible, fetch its destination and check for an HTML `<a>` or `<link>` whose `rel` token list includes `me` or `fediprofile` and whose `href` resolves to the canonical FediProfile profile URL. Document the custom `rel="fediprofile"` form. Parse HTML/relative URLs properly; compare URI identity using the existing canonicalization rules, without treating a link to another local account or path as proof. Keep FediProfile's outgoing `rel="me"` links. - Store verification per link (and link/account generation), with last attempt, last success, result, and a reason visible to the owner. A failed backlink check removes the public verified indication. Distinguish an absent/mismatched backlink from temporary fetch failure and define a bounded expiry for stale success; transient failures must not confer indefinite verification. - Recheck links in a background service on a configurable, validated five-field UTC cron schedule, following existing scheduled-service conventions. Bound work per run; safely handle concurrent instances, retries, disabled/hidden/deleted links, URL changes during a check, and account deletion. Verification state changes must update the public profile and notify ActivityPub followers through the existing actor-update path. - Protect destination fetches against SSRF across redirects and DNS changes; limit schemes, response size, time, and redirects. Do not execute JavaScript or trust unvalidated HTML. ## Migration and boundaries - Replace `VerifiedUris` as the source of the *link badge*. Recheck existing links rather than grandfathering them as verified. Keep authenticated account-identity evidence separate where needed: audit registration/backfill, badge import, automatic badge dispatch, and actor serialization so reciprocal link proof does not silently grant unrelated identity/authorization privileges. - This is independent of #25-#30. RSS/Atom feed URLs need not contain HTML backlinks; source import must not depend on this check. A verified profile link does not automatically enable an import source or replace the Bluesky ownership proof in #29. ## Tests Use a red/green functional test for the behavior change, then unit contracts for `rel` token parsing, URL identity, add/edit/recheck/revocation, stale results, concurrent edits, and fetch safety. Keep mocked-HTTP tests in commit CI; run real HTTP/database integration manually where required. Document the new verification meaning and cron setting.
Author
Owner

Implemented on michael through 5bfe3f0.

  • Profile-link verification now requires a reciprocal HTML rel="me" or rel="fediprofile" link to the canonical profile URL. Link verification is stored separately from login/identity evidence.
  • Adding or changing a visible link checks it immediately. Failed checks show the backlink HTML and let the owner explicitly add/save the link unverified or cancel.
  • Destination fetches use the federated network safety handler with HTTPS, DNS/redirect safeguards, size/time bounds, and rate/concurrency limits. A configurable UTC cron job rechecks links, with durable work claims and stale-success expiry.
  • Verification changes update the public actor and notify followers. Documentation and unit, integration, and browser tests cover the new contract.

Red test: e684e17, Forgejo run 1182. Final green commit: 5bfe3f0. Commit CI and integration workflow both passed. The integration workflow passed 466 repository, 149 service, 16 web, 220 full-application, and 14 browser tests.

Implemented on `michael` through `5bfe3f0`. - Profile-link verification now requires a reciprocal HTML `rel="me"` or `rel="fediprofile"` link to the canonical profile URL. Link verification is stored separately from login/identity evidence. - Adding or changing a visible link checks it immediately. Failed checks show the backlink HTML and let the owner explicitly add/save the link unverified or cancel. - Destination fetches use the federated network safety handler with HTTPS, DNS/redirect safeguards, size/time bounds, and rate/concurrency limits. A configurable UTC cron job rechecks links, with durable work claims and stale-success expiry. - Verification changes update the public actor and notify followers. Documentation and unit, integration, and browser tests cover the new contract. Red test: `e684e17`, [Forgejo run 1182](https://git.westergaard.cloud/klafbang/fediprofile/actions/runs/1065). Final green commit: `5bfe3f0`. [Commit CI](https://git.westergaard.cloud/klafbang/fediprofile/actions/runs/1075) and [integration workflow](https://git.westergaard.cloud/klafbang/fediprofile/actions/runs/1076) both passed. The integration workflow passed 466 repository, 149 service, 16 web, 220 full-application, and 14 browser tests.
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
klafbang/fediprofile#31
No description provided.