How to fail to read a Fediverse post
So I'm trying to add support for automatically embedding
Fediverse posts on this blog, right? And yeah I could use the
Mastodon API for it, but I'd really rather be able to make it
work with anything that speaks ActivityPub. So okay,
I look up
the ActivityPub spec. It's a bit confusing, but I figure out how to at make a GET
request for an ActivityPub resource: just add
Content-Type: application/ld+json;
profile="https://www.w3.org/ns/activitystreams"
. I can even make a request for one of my statuses and it
works! Fantastic!
Just to verify, I make a request to another server, and that's where things start getting hairy. Instead of a nice JSON representation of the post, I get back a 401 Unauthorized with a body that says "Request not signed". This is a public post, but after some digging it turns out Mastodon (and by extension ActivityPub as a whole) has a "secure mode" where all requests have to be signed.
Signed by what though? This is a distributed system—there's no central authority to distribute authorization in the first place.
To answer that I looked in the spec. I searched for "signature" and found nothing particularly relevant. Eventually I found the Authentication and Authorization section, which says "Unfortunately at the time of standardization, there are no strongly agreed upon mechanisms for authentication." Well, shit. Clearly some people agree enough for it to be implemented, but I guess not enough for it to be actually specified!
This does, mercifully, link to
a wiki page
that purports to lay out "some possible directions" and, under
the
Server to Server
section, seems to describe the scheme that
this StackOverflow post
describes as "an odd, somewhat well-known ActivityPub
quirk"[1]. This wiki page may not be an official specification, but at
the very least it describes the publicKey
field
that I can see in the actor JSON on Mastodon.social.
This is all fundamentally busywork. Because the whole thing is decentralized, the receiving instance has no choice but to trust whatever public key a new requesting instance provides. All this scheme really does is prove that someone making requests runs a server somewhere that speaks basic ActivityPub, and even then this constraint only exists for ActivityPub requests—HTTP requires no authentication at all.
{
"id": "https://mastodon.social/users/nex3",
"type": "Person",
/* ... */
"publicKey": {
"id": "https://mastodon.social/users/nex3#main-key",
"owner": "https://mastodon.social/users/nex3",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
}
}
Or does it? The publicKey
link there points to
https://web-payments.org/vocabs/security#publicKey, a URL that at time of writing is refusing HTTPS connections
entirely. Naturally I looked it up on
archive.org, only to find
that
the specification for the publicKey
field
is not only totally different from what I'm observing in the
wild, its one-sentence specification is essentially useless:
"A public key property is used to specify a URL that contains
information about a public key." The example included
doesn't even have a publicKey
field.
{
"@context": "https://w3id.org/security/v1",…