Skip to content

Split-domain WebFinger: reciprocal query returns subject matching the queried resource instead of the HANDLE_HOST identity #539

Description

@campegg

Summary

  • Hollo 0.9.0 (ghcr.io/fedify-dev/hollo:0.9.0)
  • Docker Compose, PostgreSQL 17, Caddy
  • HANDLE_HOST=campegg.com, WEB_ORIGIN=https://ap.campegg.com

Querying Hollo's WebFinger endpoint at WEB_ORIGIN for the WEB_ORIGIN-based acct (i.e. the reciprocal lookup Mastodon performs to verify a split-domain identity after fetching an actor object) returns a subject that echoes back exactly what was asked, instead of the canonical HANDLE_HOST identity; i.e. in my case, it returns cam@ap.campegg.com rather than cam@campegg.com (the canonical identity is present, but in a secondary aliases entry).

It looks like Mastodon's account resolution stops as soon as the subject matches the requested resource, so it never gets to aliases. So any server that discovers this account by fetching the actor object first (which appears to be the common path) settles on WEB_ORIGIN as canonical rather than HANDLE_HOST. Confirmed with a live search from a Mastodon account that had never previously resolved this account or domain, so it's not cached or stale results.

Steps to reproduce

Query WebFinger at the account domain directly (this works correctly):

curl -s "https://campegg.com/.well-known/webfinger?resource=acct:cam@campegg.com"
{"subject":"acct:cam@campegg.com","aliases":["https://ap.campegg.com/@cam"],"links":[...]}

Query WebFinger at WEB_ORIGIN for the WEB_ORIGIN-based acct... this is the reciprocal check that a remote server runs after fetching the actor (note: you won't be able to reproduce this now because of the workaround I'm using—see below):

curl -s "https://ap.campegg.com/.well-known/webfinger?resource=acct:cam@ap.campegg.com"
{"subject":"acct:cam@ap.campegg.com","aliases":["https://ap.campegg.com/@cam","acct:cam@campegg.com"],"links":[...]}

Expected behavior

The second response should have subject set to acct:cam@campegg.com regardless of which valid acct variant was queried, which matches how Mastodon's own LOCAL_DOMAIN/WEB_DOMAIN split handles the identical case.

Actual behavior

The subject mirrors the query verbatim; the canonical identity only shows up as an alias.

Impact

Remote search and mention resolution display and store the account under the WEB_ORIGIN domain instead of HANDLE_HOST for any discovery path that starts from the actor object. This includes direct account search, on at least one tested Mastodon instance.

Workaround

I'm currently using a rule in my Caddyfile to serve a corrected response, but it's not ideal (I'd have to remember to update it if I change my avatar image, etc).

@reciprocalcheck {
	path /.well-known/webfinger
	query resource=acct:cam@ap.campegg.com
}
handle @reciprocalcheck {
	header Content-Type "application/jrd+json"
	respond `{"subject":"acct:cam@campegg.com","aliases":["https://ap.campegg.com/@cam"],"links":[{"rel":"self","href":"https://ap.campegg.com/@cam","type":"application/activity+json"},{"rel":"http://webfinger.net/rel/profile-page","href":"https://ap.campegg.com/@cam"},{"rel":"http://webfinger.net/rel/avatar","href":"https://ap.campegg.com/assets/avatars/d358e7d2-bc2e-453a-8dca-4c9041b9b00b/d3b1dbca-6f33-4b17-90d3-27e9424bbc56.png"}]}` 200
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions