- From: Orie Steele <orie@transmute.industries>
- Date: Wed, 21 Jul 2021 09:59:14 -0500
- To: Justin Richer <jricher@mit.edu>
- Cc: W3C Credentials CG <public-credentials@w3.org>
- Message-ID: <CAN8C-_LsVCnXMCtq6jSSzE676GhZ3DpsdpriL8o7JWqw0Pn5Rw@mail.gmail.com>
Thanks Justin, these examples are very helpful. It seems like the primary value of RAR is extensibility. Objects are easier to extend than strings. I want to understand the security tradeoffs by leveraging these extensibility features. Your identifier security parameter example is very helpful. In that case, are you not exposing that identifier to the AS? Why does the AS need to know which DIDs a resource server can issue/present on behalf of? It would seem safe to make use of the extensibility as long as you don't leak subject identifiers to the AS, can you comment on these design considerations? I'm on my phone sorry for any spelling. OS On Wed, Jul 21, 2021, 9:42 AM Justin Richer <jricher@mit.edu> wrote: > On yesterday’s call, we briefly discussed the applicability of RAR for the > VC HTTP API, and I wanted to continue that conversation with some > additional concrete points. Apologies in advance for the long email, but > there’s a lot to untangle here as it seems a number of people have the > wrong idea. > > First, what are we proposing? I am suggesting, strongly, that when > defining this API, the group also define the different ways to control > access to it. Which is to say, what actions can you do, what kinds of data > do you want to switch on — in short, we’ve got an API, how do we want to > slice it? I am explicitly saying that we do not define anything about the > process of getting a token (so, specifying client credentials is a terrible > idea) or how to process the token (so specifying token formats is also a > terrible idea) because these don’t affect the API. What does affect the API > is the availability of different kinds of actions for a specific call. What > are the things that I can DO here? > > This is what OAuth 2 invented scopes for, instead of the OAuth 1 or HTTP > Basic method of “you just get everything”. So, why not just use > parameterized scopes? It seemed like a good idea when I first invented it a > decade ago: https://blue-button.github.io/blue-button-plus-pull/#scopes or > when it got pulled into other efforts like > https://openid.net/specs/openid-heart-fhir-oauth2-1_0-2017-05-31.html … > and Orie even suggested the following set of parameterized scopes for this > API: > > 'create:credentials': Grants permission to create credentials > 'derive:credentials': Grants permission to derive credentials > 'create:presentations': Grants permission to create presentations > 'verify:presentations': Grants permission to verify presentations > 'exchange:presentations': Grants permission to exchange presentations > > So what’s the problem? I can say with full confidence after years of > experience building and deploying systems to support parameterized scopes > like this that they are fragile, awkward, and lead to insecure corner > cases. For example, am I supposed to parse these strings to find the bits > on either side of that colon? Am I allowed to make up my own combinations > of elements, so for example do I get “derive:presentations” from that set > above, for free? What if I am protecting more than one API and they use a > different separator? Or what if there’s just a plain old namespace > collision and someone else has “exchange:presentations” on their unrelated > API that that the AS is protecting at the same time? And what if someone > has an additional dimension they need to convey, like the identifier for a > specific account— does that mean we add a parameter to the string above, or > do we need a separate scope to carry this, and how do we combine all of > that? While all of these questions can be answered, the answers make > something that seems simple on the surface spiral out of hand very quickly. > I know this because I’ve seen it time and again, and this experience is > exactly what led me to create what has become RAR. > > So let’s talk about how we’d address the above actions in RAR. First, > please note that I’m going to be fast and loose with JSON syntax here > because I’m typing it out by hand, so don’t try to compile this. :) Second, > for simplicity of following this argument, let’s say that all five actions > above are inside a single RAR type value, and we’ll just use “vha" as the > stand-in for that value (in reality it would be a URI, and there might be > multiple for a given API family like VC HTTP API). > > Picking apart the pieces above, we’ve got two elements that fit fairly > well into the “actions” and “datatypes” fields in a RAR request type: > > actions: [ create, derive, verify, exchange ] > datatypes: [ credentials, presentations ] > > Since RAR is about combining different dimensions into a single request, > we can easily get to the kind of expressions above. Let’s say I want to > create credentials and permissions, but nothing else — I can ask for the > following value: > > > { > type: “vha”, > actions: [ create ], > datatypes: [ credentials, presentations ] > } > > Or if I want to create and derive credentials but do nothing with > presentations, I can do: > > { > type: “vha”, > actions: [ create, derive ], > datatypes: [ credentials ] > } > > And how about a more complex cross-product? Let’s say I want to create and > derive credentials but only exchange presentations. All RAR requests are in > an array, just like OAuth 2 scopes are fundamentally an array, so that > request would look like this: > > [ > > { > type: “vha”, > actions: [ create, derive ], > datatypes: [ credentials ] > }, > { > type: “vha”, > actions: [ exchange ], > datatypes: [ presentations ] > } > > ] > > Unlike with scopes, the extent of each aspect is clear to developers of > both the API and the clients of the API. It’s very clear what I’m asking to > do and what I’m doing it to, without having to define or parse or > understand any kind of VC-HTTP-API-specific syntax to cram into an existing > field. This gets even better with GNAP because you can ask for multiple > different-scoped tokens at the same time. > > And what if someone requests a confusing or bad combination? > > { > type: “vha”, > actions: [ create, derive ], > datatypes: [ credentials, presentations ] > } > > RAR clearly defines each object as the union of all items within it, so > anything processing this would be able to tell that “derive” and > “presentations” doesn’t fit together and this is an invalid request. > There’s no need to guess and no need to put in special casing. Your > implementation would be configured with viable combinations, and anything > that falls outside of that is easily detected. > > It gets even clearer if we want to talk about other dimensions not > captured in the example scopes above. If I want to do this for a specific > account? We can define a security identifier in this request to contain > that information: > > { > type: “vha”, > actions: [ create, derive ], > datatypes: [ credentials ], > identifier: did:example:12345 > } > > Already we’re doing something that would be difficult with scope strings. > > And what if we are protecting multiple APIs that use similar syntax? They > sit in separate namespaces so there is no chance for confusion. > > [ > > { > type: “vha”, > actions: [ create, derive ], > datatypes: [ credentials ] > }, > { > type: “TOTALLY-NOT-VHA”, > actions: [ create, derive ], > datatypes: [ credentials ], > not-a-vha-attribute: do-some-weird-thing > } > > ] > > What does this mean for the VC HTTP API itself? It means that each call > defined as part of the API would have a section like: > > Security: > Type: “vha” > Action: “create” > Datatype: “credentials" > > … and that’s all the detail that would be needed. The core part of the > spec would say something like “use the fields defined in the ’security’ > section to make a RAR or GNAP request”, and that only applies if you’re > doing OAuth 2 (with any flow) or GNAP (with any mode). You’re still free to > protect the API with whatever fancy pants thing you want, but even then the > Security section will lay out the core dimensions that each call is > differentiated on. > > So yes, it’s possible to pull this off with scope strings. I’ve done > things more complex than this and have seen some really crazy stuff like > using a fully-qualified query language as the scope string value. However, > what I’m trying to convince this group is that there is a much simpler way > and that’s by embracing the object structures that RAR lets you define, to > talk about the dimensions of access that you want to allow or disallow. > That discussion is going to happen anyway, and is probably ALREADY > happening. I am saying this group should embrace that challenge as part of > the protocol definition, and do so sooner rather than later. If we wait, > it’ll end up a mess of proprietary solutions that don’t want to change, and > people will dig in their heels even more for either a loose spec or > patchwork of things so implementations don’t have to change. > > What I’m calling for is structure for interoperability and compatibility, > in a way that’s a very, very light lift on the spec itself and would in > fact help define the elements of the spec. Believe me — I’ve been down this > road before and I am hoping that this group could learn from my mistakes > instead of repeating them. > > — Justin >
Received on Wednesday, 21 July 2021 14:59:41 UTC