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.4 (ghcr.io/fedify-dev/hollo:0.9.4)
  • 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

Labels

bugSomething isn't workingdependenciesPull requests that update a dependency file

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