I’m probably missing something obvious, but how does scanning the outbox verify authentic ownership when actor’s server is not the authority for the (claimed) created object? Can’t I submit an outbox Create
activity for any URL on an external domain even if I didn’t create it? This seems more like an Anounce
of an object created on the drawing.example
server.
Shouldn’t Image
ID in this example belong to social.example
too? In theory ownership can be confirmed by other means, but I think we need to keep authentication / authorization as simple as possible to reduce the risk of software vulnerabilities (such as GHSA-3fjr-858r-92rw) and to reduce the amount of effort required to implement ActivityPub. Same-origin check is easy to implement (can be done offline), and the concept is already familiar to web developers.
This is a “MUST” because clients that fail to authenticate object are vulnerable to impersonation attacks. If some new authentication mechanism will be adopted by Fediverse developers, I will add it to the list.
I’m probably missing something obvious, but how does scanning the outbox verify authentic ownership when actor’s server is not the authority for the (claimed) created object?
The object claims that https://social.example/user/evan
is its creator (see the attributedTo
property in the example). If they had the same origin, you could trust that claim implicitly, since https://social.example
is implicitly permitted to make claims about https://social.example/user/evan
.
Since they’re not from the same origin, you have to verify the claim. Finding the Create
activity verifies the claim – the actor claims to have created the object, and the object claims to have been created by the actor. You’re correct, one or the other on its own isn’t valid.
Shouldn’t
Image
ID in this example belong tosocial.example
too?
No, the object is on the drawing.example
server. The social.example server doesn’t know (in this case) how to make SVG drawings. That’s the whole point of the ActivityPub API – to let the creativity happen at the edge, you can have apps that host a whole object lifecycle outside of the AP server.
This is a “MUST” because clients that fail to authenticate object are vulnerable to impersonation attacks. If some new authentication mechanism will be adopted by Fediverse developers, I will add it to the list.
So, I’ve already named two ways that validation of ownership can happen that you haven’t considered. It’s simply untrue to say that you’ve exhaustively listed all the ways an ownership relationship can be validated.
I strongly object to these MUST requirements, and I don’t think this FEP should go final with them included.
Thanks. I was missing the attributedTo
in the created object. You’re describing this in the context of an “API client”. Is this technique specific to C2S?
(To anyone… is this cross-domain Create
supported by the Mastodon S2S implementation?)
I would flip the question on its head a little, and ask how much of the domain-locked assumptions of the Mastodon API are bleeding into our understanding of the broader ActivityPub protocol, and how the C2S and Mastodon-API implementations can interop stably/robustly. Particularly when we get into moderation tooling that’s agnostic (and needs to work well S2S in both directions), I think it might help to retro-spec and think through Server Actors and domain-locked assumptions other artefacts of the “monolithic server” model in case a few tweaks on the Mastodon side would be a stitch in time…
Conversely, extending features or behaviors originally from the monolithic-server context to others might not make sense without client-side signing bluesky style (or even non-custodial signing nostr-style)… so it’s kind of a long-simmering deep protocol question that might get worse the longer we don’t face it.
No, it’s not specific to the ActivityPub API.
Obviously it’s way clearer if the client uses the ActivityPub API, but it shouldn’t matter what mechanism is used to create activities.
The big thing is that same-origin between owner property and id helps ensure ownership, but it’s not required, and it’s possible to confirm ownership other ways.
That’s an interesting idea, but this FEP is (mostly) about current practices. I’m not aware of any Fediverse server that delegates object processing to another server in the way you described. I’m not even sure this kind of architecture is worth pursuing, because complex tasks can be similarly performed by clients, with an added benefit of supporting offline-first workflows.
One way you named is outbox scanning, which is impractical.
What is the second way? Querying membership endpoint?
I’m not saying that listed authentication methods are the only possible methods, I’m saying they are the only ones that are widely used (LD signatures are also used but shouldn’t be recommended).
The authn/authz framework described in this FEP is fully compatible with FEP-ef61 (and FEP-ae97). The origin of ap://
URL can be computed from scheme and authority components (I’ll specify that in the next revision of FEP-ef61).
You say that unless the specific requirements are met, the implementer MUST discard the activity.
That’s not true, so you probably shouldn’t put it in a document.
In my opinion, the “broader ActivityPub protocol” (or “API”) is a theoretical construct. It’s fun to discuss the possibilities (and there are many given the loose specification), but my question was pragmatic. The Mastodon protocol/API (based on a specific subset of ActivityPub) is what currently drives most of the Fediverse and will probably continue to do so for quite some time.
Sorry for the tangent, but the cross-domain C2S (ActivityPub client API) Create
example @evan gave is very interesting. I had thought of C2S Create
as a server command/instruction (“create this note in the server on my behalf and deliver it to recipients”), which is different than S2S where it is a notification (“an actor created an object”). The example in this thread is using C2S Create
as a notification (“I have created an object somewhere”).
Like I said earlier, this C2S usage seems more like an Announce
than a Create
. The Create
activity happened on the drawing server (by an actor from another server?). The C2S Announce
activity would be drawing attention to that already-created object (the drawing could be announced or the Create/Image
activity could be announced).
Doesn’t an Announce
activity also avoid the ownership issue since one can Announce
any object, not just objects owned by the announcing actor?
I can’t simply change “MUST” to “SHOULD” or “MAY” because that would mean no authentication is fine. But I can add a note saying that same-origin check is not the only way to verify relationship between objects.
Yes, Announce
would not be against FEP-c7d3 authorization rules. This reminds me of boost-bot groups (e.g Guppe) where you send a Create activity to a group and it an automatically announces the created Note to all its followers.
not necessarily! the drawing server could be an AP C2S client, where you authorize from your actor on social.example
and then go to drawing.example
to generate a drawing. once you’re done, drawing.example
does the following things:
- it saves/persists the image on its own domain, as
https://drawing.example/files/5CvE73Yib6...
- it generates an AS2 Image on its own domain, as
https://drawing.example/FJIhGP8Jp7CMWTO5hrjIW
andattributedTo
you. - it lets you POST a Create for that Image to your outbox via the AP C2S API, to notify other actors that you created the drawing.
In this case, the image could have been generated as an AS2 resource on social.example
instead (by omitting the id
, the server running at social.example
would generate one for you), but this shouldn’t be the only supported architecture. enforcing a strict same-origin check would prevent use cases where resources are hosted cross-origin.
tangential issues:
- C2S Create activity has unspecified behavior wrt assigning an ID to the inner object · Issue #438 · w3c/activitypub · GitHub
- CORS · Issue #463 · w3c/activitypub · GitHub
- Disambiguate usage of `attributedTo` · Issue #467 · w3c/activitypub · GitHub partially – in particular, having a follow-your-nose way to get from an object to its Create activity would be useful, to be able to quickly and efficiently verify the bidirectional claim.
I suppose if C2S is ever widely used, we can revisit the topic of whether a posted C2S Create
is a notification (delivery only), a command (to create and deliver the embedded object), or either (depending on some unspecified context).
Does this mean that drawing.server
is some kind of an actor-less AP server (as well as a C2S client)? It must at least support dereferencing the AS2 Image, right?
Seems like a very convoluted edge case to justify the changes being requested. However, it certainly is a thought-provoking example.
yes, but any web server can do this. it doesn’t need to be an AP server. (unrelatedly, the application can be an AP client.)
I wouldn’t say it’s convoluted at all. It would be a pretty common case if C2S was implemented more widely. There’s no reason the storage/persistence layer has to be coupled tightly to the network layer.
My initial take is that it probably depends on whether you specify an inner object id or not. This requires some spec language clarification as described in w3c/activitypub#438 above. to summarize:
- if the object has no id, it gets assigned a newly generated one, and the Create (or implicit Create, in case of POSTing the raw object) is a procedure call of sorts, as the server has to persist the resource that it just assigned an id for.
- if the object already has an id, this means that it is already persisted as a resource. the wrapping Create is the only thing that needs an id and persistence on the AP server. it has no side effects because the side effects were already handled externally.
to put it another way, the main side effect of a Create (as a procedure call) is to persist a resource at some identifier.
to put it yet another way, the following two C2S activities are equivalent:
type: Create
object: # some partial representation
- id: <something>
- ...
type: Create
object: <something> # reference by @id only
in such a case there might be a consideration for the AP server to verify or validate that any partial representation provided by the client will match the resource at that id provided, but it’s not as simple as fetching the resource (as it might be private to the AP server), and it’s maybe not as simple as stripping all partial reprs and leaving only the id (although this could work). this is why i think the use case of “Create an object with an id already provided” ought to be clarified and also explored for best practices, security considerations, etc.
I think part of the confusion might between “FEP as draft SWICG Report” and “FEP as documentation of single-implementation experiments”. As I understand it, FEPs describe how other implementations can interop with a feature being experimented by the author’s implementation; they are NOT ready for widespread adoption as-is and are NOT being proposed for Fediverse-wide adoption.
So while I agree completely with Evan that this approach forecloses interop with C2S implementations, and that future FEPs or future versions of this feature will hopefully want to loosen this requirement, I think it’s also fair-game for a FEP to only work for Mastodon-API or powerful-server-model implementations. (As Steve points out, many FEPs today take this narrower interop strategy implicitly, perhaps for lack of a good semantic for doing it explicitly, or for lack of awareness of interop problems with C2S implementations that they’re not testing against.)
(Personally, as a decentralization enjoyor, I’m a huge fan of cross-domain usecases even though they’re much harder to engineer and describe in protocol; it’s fine if people are prioritizing smooth compatibility with the Mastodon API-iverse, just state that upfront and stick a reference to the problems this creates in the “### Future Work” section or in-FEP issue tracker if you use one of those!)