I’ve re-started building with ActivityPub’s Client to Server API. So this post is to document some of the challenges and hiccups.
The very first thing a user will want to do is login, right? So, we ask the user for their handle (webfinger: user@domain.tld). With this, our app can try to find info about how to connect to the site.
Ideally we would start by looking to RFC 8414: OAuth 2.0 Authorization Server Metadata. The main point is that once we have a server URL, we can look up the configuration routes via a predictable URL /.well-known/oauth-authorization-server
Alternately, with a webfinger lookup we might find the OpenID-Connect 1.0 issuer discovery link, leading to /.well-known/openid-configuration
(thanks to @FenTiger for the tip).
These 2 methods really are ideal as they return a set of routes with which a user can authorize our app to obtain tokens for secure requests.
Otherwise, ActivityPub defines some oauth endpoints on the actor object, which is a start, however at the time of publishing the spec it seams app registration was not yet agreed upon.
Another issue is that some activitypub servers will not allow CORS on Actor objects. There are valid privacy reasons for this, nevertheless it is an additional hurdle we must face.
If we run into CORS on the user/actor, so we could look to the instance actor for additional info. Depending on implementation, we might find this via nodeinfo
(FEP-2677), or we might find it via webfinger
(FEP-d556).
The nodeinfo route is a bit more tortuous, because fetching example.social/.well-known/nodeinfo
we potentially receive a payload with links to version 2.0, and or 2.1. example.social/nodeinfo/2.1
. Parsing nodeinfo’s metada
ta field, we’d look for staffAccounts
which would be an array, let’s just take the first one.
The webfinger route would be, adding the domain itself to the resource lookup : example.social/.well-known/webfinger?resource=https://example.social
From there we parse the webfinger links field which is an array, looking for an object whose has rel
=”self
” and whose type="application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
.
Whew, this object’s href
value leads us to the instance actor.
Finally, we arrive at an actor profile, where we will look for the endpoints field, and hope to find:
oauthAuthorizationEndpoint
oauthTokenEndpoint
oauthRegistrationEndpoin
t*
If implementations aren’t serving an OAuth discovery endpoint RFC8414, and are limiting requests to actor pages, then there is really not much we can do!
*As mentioned earlier there may not even be an app registration endpoint. There are downsides to dynamic client registration (RFC7591), namely giving users control over app permission revocation.
ActivityPub co-author Evan has proposed FEP-d8c2: OAuth 2.0 Profile for the ActivityPub API, which suggests bypassing dynamic client registration, and using public clients similar to this IETF draft by Aaron Parecki.
The only downside here might be the minor inconvenience of having to publish a Client ID Metadata Document, regardless of how mature a client is.
Leave a Reply