RAR vocabulary

Hey y’all!

I’m working on implementing OAuth Rich Authorization Requests in my new AP server, and I noticed the lack of a unified fediverse/activitystreams vocabulary. I was wondering if this is something folks would be interested in, or should it be left to server-specific implementation.

I’d argue that C2S becomes much less useful if each client has to know in advance every permission rather than a unified vocabulary, but I want to hear what people think.

cc @thisismissem @kopper since I know y’all have worked on this.

2 Likes

@reesericci you should check out the work going on here:

https://github.com/swicg/activitypub-api/

1 Like

I tried as did others, but the notion was rejected

Maybe others have the time & energy to care more, but I don’t care to be involved in a task force where the lead trots out their CV in response to a technical disagreement (this happened)

3 Likes

Was any vocab or concrete spec proposed for what the RARs should look like?

If so, it might be worthwhile then to put it into a FEP and call it day, and see if it gains traction with people outside of the CG if they’re being hostile. People may just pick it up and implement it, and we may have better luck encouraging that.

1 Like

RAR looks like a good fit to me, though I haven’t really thought through any of the details yet.

SHACL has been proposed - but I couldn’t immediately see how to go about rendering it for the user to approve.

it might be worthwhile then to put it into a FEP

+1 from me if someone else wants to write it :wink:

…what

How does SHACL fit in here, SHACL just describes shapes of RDF data. It has nothing to do with authz or distilling objects into actions.

Update: I get it now, SHACL would define what the RARs would look like and the metadata that goes along with it. Maybe that makes sense, personally I’m writing my RAR enforcement at the moment in Rego, which is designed for writing policies with OPA.

Whoops - that was the wrong link - the proposal I meant to refer to is here. I think the basic idea is to use it to describe constraints on what activities the client is allowed to post to the outbox, which doesn’t solve the entire problem, but certainly encompasses a large chunk of it.

Sure, but that isn’t a contract for what the caps/rules should look like, just how to describe them. What I’m describing is what is the base vocabulary for the rules that clients & servers can expect. The context of a policy document.

The question of what RAR structure does a client need to request to do XYZ actions needs solving.

SHACL could be how we describe that, where like an activitystream input is only valid if it constrains to the permissions requested, and we use validators.

However, I don’t love that - I think it’s a bit byzantine, and I’d much prefer using something simpler than allowing clients to essentially define their own permissions schema and/or doing overkill by authz against the entirety of an activitystream, when just running authz against the requested action is fine, and it will be validated separately by the server.

Enforcing constraining and validating shapes just seems unnecessarily complex, when we could get away a defined base vocabulary of permissions, and then leaving it to the server to choose how to validate them. This could just be JSON schema, and then people could enforce it at the authz boundary with something like Rego (which supports type checking against JSON schema!).

We have a problem in ActivityPub where the only way to do most anything is to submit an activity to the outbox and depend on “side effects” to carry out any actual state changes. As a consequence, you can imagine the ActivityPub API as (for the most part) having one endpoint accepting many arbitrarily-shaped body content shapes. You can’t figure out if a request should be authorized unless you parse the body content for its semantics.

https://github.com/w3c/activitypub/issues/510 gets into (and links out to) a lot of the prior discussion, but this sticking point has been raised tangentially to multiple other issues regarding “side effects” being carried out by the server. Essentially, you can’t tell if any given activity will trigger “side effects” or not – and whether the responsibility lies with the server itself or instead with one or more clients.

If you want to think about it more precisely, our Authorization is targeting the outbox endpoint, and the SHACL shape is targeting the body content. We can try to unwind the activities for their “side effects”, then define scopes or RARs or whatever targeting those “side effects”, because we can’t target the activities themselves without understanding what they do via the “side effects”. (This becomes more important when you recognize that arbitrary activities can be published with arbitrary side effects!)

The user stories I’d like to see possible via RARs are things like “Add and Remove objects to a specific Collection”, which can’t be done with scopes. I’m open to collaborating on developing a profile for RARs that can be used with the ActivityPub outbox!

2 Likes

I sort of got to that by the end of my all-over-the-place message lol. I meant by “enforcing constraining and validating shapes” that we shouldn’t prescribe it in the spec, but rather leave it to server operators to implement policy enforcement. If it’s not a contract, it shouldn’t be specified is my approach.

Anyways, so if I’m reading this right, then we should not be basing our permission structure solely based on the object shape anyway, because it can’t be trusted.

It seems to me like the best path would be extracting the authz surface into a RAR schema with a capabilities field, which would be the vocabulary, and then targets (think as:Note) and locations (actors, collections, as:Public?, etc) fields.

capabilities would loosely map to ActivityStreams actions, but we could take into account side effects at our discretion.

I was thinking a set of capabilities like this, activity types on the Y, object types on the X:

Activity Type Any Article Audio Document Event Image Note Page Place Profile Relationship Tombstone Video
Accept accept:any accept:article accept:audio accept:document accept:event accept:image accept:note accept:page accept:place accept:profile accept:relationship accept:tombstone accept:video
Add add:any add:article add:audio add:document add:event add:image add:note add:page add:place add:profile add:relationship add:tombstone add:video
Announce announce:any announce:article announce:audio announce:document announce:event announce:image announce:note announce:page announce:place announce:profile announce:relationship announce:tombstone announce:video
Arrive arrive:any arrive:article arrive:audio arrive:document arrive:event arrive:image arrive:note arrive:page arrive:place arrive:profile arrive:relationship arrive:tombstone arrive:video
Block block:any block:article block:audio block:document block:event block:image block:note block:page block:place block:profile block:relationship block:tombstone block:video
Create create:any create:article create:audio create:document create:event create:image create:note create:page create:place create:profile create:relationship create:tombstone create:video
Delete delete:any delete:article delete:audio delete:document delete:event delete:image delete:note delete:page delete:place delete:profile delete:relationship delete:tombstone delete:video
Dislike dislike:any dislike:article dislike:audio dislike:document dislike:event dislike:image dislike:note dislike:page dislike:place dislike:profile dislike:relationship dislike:tombstone dislike:video
Flag flag:any flag:article flag:audio flag:document flag:event flag:image flag:note flag:page flag:place flag:profile flag:relationship flag:tombstone flag:video
Follow follow:any follow:article follow:audio follow:document follow:event follow:image follow:note follow:page follow:place follow:profile follow:relationship follow:tombstone follow:video
Ignore ignore:any ignore:article ignore:audio ignore:document ignore:event ignore:image ignore:note ignore:page ignore:place ignore:profile ignore:relationship ignore:tombstone ignore:video
Invite invite:any invite:article invite:audio invite:document invite:event invite:image invite:note invite:page invite:place invite:profile invite:relationship invite:tombstone invite:video
Join join:any join:article join:audio join:document join:event join:image join:note join:page join:place join:profile join:relationship join:tombstone join:video
Leave leave:any leave:article leave:audio leave:document leave:event leave:image leave:note leave:page leave:place leave:profile leave:relationship leave:tombstone leave:video
Like like:any like:article like:audio like:document like:event like:image like:note like:page like:place like:profile like:relationship like:tombstone like:video
Listen listen:any listen:article listen:audio listen:document listen:event listen:image listen:note listen:page listen:place listen:profile listen:relationship listen:tombstone listen:video
Move move:any move:article move:audio move:document move:event move:image move:note move:page move:place move:profile move:relationship move:tombstone move:video
Offer offer:any offer:article offer:audio offer:document offer:event offer:image offer:note offer:page offer:place offer:profile offer:relationship offer:tombstone offer:video
Question question:any question:article question:audio question:document question:event question:image question:note question:page question:place question:profile question:relationship question:tombstone question:video
Reject reject:any reject:article reject:audio reject:document reject:event reject:image reject:note reject:page reject:place reject:profile reject:relationship reject:tombstone reject:video
Read read:any read:article read:audio read:document read:event read:image read:note read:page read:place read:profile read:relationship read:tombstone read:video
Remove remove:any remove:article remove:audio remove:document remove:event remove:image remove:note remove:page remove:place remove:profile remove:relationship remove:tombstone remove:video
TentativeReject tentativereject:any tentativereject:article tentativereject:audio tentativereject:document tentativereject:event tentativereject:image tentativereject:note tentativereject:page tentativereject:place tentativereject:profile tentativereject:relationship tentativereject:tombstone tentativereject:video
TentativeAccept tentativeaccept:any tentativeaccept:article tentativeaccept:audio tentativeaccept:document tentativeaccept:event tentativeaccept:image tentativeaccept:note tentativeaccept:page tentativeaccept:place tentativeaccept:profile tentativeaccept:relationship tentativeaccept:tombstone tentativeaccept:video
Travel travel:any travel:article travel:audio travel:document travel:event travel:image travel:note travel:page travel:place travel:profile travel:relationship travel:tombstone travel:video
Undo undo:any undo:article undo:audio undo:document undo:event undo:image undo:note undo:page undo:place undo:profile undo:relationship undo:tombstone undo:video
Update update:any update:article update:audio update:document update:event update:image update:note update:page update:place update:profile update:relationship update:tombstone update:video
View view:any view:article view:audio view:document view:event view:image view:note view:page view:place view:profile view:relationship view:tombstone view:video

With properties we could use jsonpointer to dig deep in terms of allowing specific nested props.

I’m not quite sure what to do with links though.

Just to share where my head’s at, a sample authorization_details could look like this:

"action": "update",
"object": "profile",
"properties": ["/summary"],
"location": "self"

which comes out to “Update your profile’s summary”

x_x that’s a lot! and even then, it doesn’t account for extensions…

the actions i would want to control access to are things like:

  • GET ldp:inbox
  • POST as:outbox
  • POST as:proxyUrl

the modulation of these actions is where the dragons are. say for example you want only some POSTed payloads to pass through the as:outbox. we don’t have a common way of describing those restrictions, but in plain english you can imagine some restrictions/conditions like “it is a Like activity” ([a as:Like;]). this is inherently a shape constraint, hence why i suggested SHACL at all.

english: “allow POSTing to the outbox any activity that matches this shape”

json(-ld) description that could be a RAR:

"authorization_details": [{
  "@context": {...},
  "path": "outbox", // this is @type: @vocab -> as:outbox
  "method": "POST", // this is @type: @vocab -> http:POST
  "shape": {...} // this is applied to the content/body
}]

we would need to agree on a context for terms used in the RAR just like we (hopefully!) agree on a context for terms used in an activity.

if the “API” used different endpoints (i.e. different collections you could POST to), then perhaps we wouldn’t strictly need to understand shapes. say for example that liking an object involved POSTing a Like activity not to your outbox, but instead to an endpoint that forwards the Like activity to one or more of the following dispatch targets:

  • the object’s as:likes collection
  • your as:outbox collection
  • your as:liked collection or similar

this is basically what mastodon does when you POST /api/v1/statuses/:id/favourite, although a more streamlined design would be a single endpoint a la /api/v1/favourites. you could advertise this endpoint in the as:endpoints mapping:

{
  "type": "Person",
  "name": "Alice",
  "endpoints": {
    "proxyUrl": "...",
    "_:like": {"id": "..."},  // POST a Like activity here as described above
    "_:share": {"id": "..."},
    "//": "..."  // and so on
  }
}

the outbox would still accept arbitrary activities but not necessarily tie the “side effects” to those. but again, unfortunately, activitypub’s current design does attach the “side effects” to the outbox activities and forces everything to flow through the outbox, thus necessitating this discussion about shape constraints of some kind.

i think you could try to extract ux-level concerns such as “like any object”, “share any object”, etc, but you wouldn’t be able to modulate any further than that without shapes, e.g. you can’t request access to “like and unlike objects within a specific context” or so on. there’s a tradeoff in how complex you want to allow these details to be, which both empowers and confuses people. (it’d be easier to extract these as separate endpoints, though…)

My issue is that allowing arbitrary shape constraints risks creating a model that while flexible, is difficult for servers to display to users conceptually and developers to understand. This is why I’m a proponent of a “cap” system with a defined set of capabilities mapped to constraints as enforced by the server (which could use SHACL).

I don’t think we need to “understand” shapes in the spec, but we can get away with merely providing a smaller subset of constraints that can be represented as JSON (action, object, properties, location)

Also re: HTTP verbs, I think it would be easier to just define read/write semantics on actions, like an action could be as:Like:[r,w,rw]

1 Like

I think some of these might be different depending on conditions, like being a known actor signing the request, or coming while bearing token from OAuth? Please don’t try to make this fit the unix access permissions, the scope is wider.

Well we could add a conditions property on the RAR entry, since it would apply there, correct?

Also, what other scopes are needed than just r/w/rw?

I think things like unsigned/open, Authorization based on Actor (identified), Bearer-Token, matrixed with the applicable HTTP Methods? Or do we want to include the exhaustive list of Verbs this object will process?.

Hey, yes, that was me! I talked about my previous API work and my stake in ActivityPub.

I think it would be great if someone who wants to implement RARs for ActivityPub API writes them up as a FEP or a report in the ActivityPub API task force. As lead of the task force, I’d be happy to support that work.

I don’t think they’re a useful technology; I think OAuth scopes are much better supported in toolsets, and they provide more than enough help. I would probably not implement them, and I definitely don’t consider them a requirement for using the API.

There’s a good set of OAuth scopes for the ActivityPub API in the Basic Profile for Social API Servers. They were hammered out in this issue and on this wiki page.