Private Capability Revoke

Currently granting and revoking a private capability requires linking separate paths per use. (except some workaround cases like FUSD [0])

But as we don’t have Path constructor from String there is no easy way to do that. (As FUSD team already mentioned on Line: 172)

If we go over a real case, FUSD:

They are trying to encapsulate Minter Capability in a resource (MinterProxy), which can be accessed only by the resource itself.

The problem here is, while granting is not a problem, revoking still requires public resource to be exposed by a link.

Although Path from String seems the obvious solution, I feel it is also a bit limited.

Maybe what we may need is a bit higher level, access controls on links, instead of public/private. (I think public/private still can be considered as defaults, public=blacklist, private=whitelist kind of)

So if I link something to /public I can additionally block some accounts from accessing, if I link /private, I can allow some accounts to access.

I am curious about your thoughts.

[0] Flow View Source

One of the features that we wanted to support for Paths was that you could use Addresses, Types, or Integers as path components (as well as string variables). I still consider that the cleanest way to solve this problem.

The reason why we don’t have access controls on Capabilities is because Cadence code doesn’t have any notion of “who is running the code”. This is by design! I’m convinced that having some kind of implicit “who is asking” model of security is harder to audit and enforce.

Instead. we’re trying to lean into the Capability-Based Security model, and allow individual Capabilities to be revoked (by breaking the link) instead of resorting to an Access-Control List on links.

I’ll be honest: This is the part of Cadence that feels the roughest to me, but I haven’t yet seen/thought of a better solution. My hope is that “parameterized” paths will make most of this much smoother, but I fear it will still be a bit awkward. I’d love to hear other ideas!

1 Like

I agree that ACL is ugly. I tried to think a lot on this, but the problem is anything ‘name’ based (even with unlinking) is a bit dangerous.

Imagine the scenario:

  • I created some capability, then linked privately to /private/dete and shared with you.
  • Time passes, I wanted to revoke: I need to remember where I linked (let’s go with optimistic path ( I can query links etc ) I unlink the /private/dete

But now I also have to write or log (/private/dete) to not to use it ever again forever.

Address and Type as component with some string actually looks promising. /private/0xabcdef/flowVault/flowVault{FlowToken.Provider} with some link enumeration can be user friendly.

Also inspired by this, maybe easier solution can be like this:

We can add Capabilities member (array) to link object. When I generate capability from that link, reference to this capability is automatically added to Capabilities array.

Later I can query link’s capabilities array, each Capability will have owner (as they will need to stored in user storage somewhere) and borrowType. So I can see who has access.

If I remove the capability reference from this array, it is not valid anymore (we don’t need to remove from storage, only check if still valid on borrow time). (easy revoke)

Also by adding uuid to capability like resources have, as we discussed with @turbolent on office hour yesterday, we can handle uniqueness, so same path on my scenario is not problem anymore.

1 Like

Yeah, using a Capability ID might actually work well. And it might help clean up the confusion between Capabilities, Links and Paths.

So, I’m thinking that instead of creating a Capability that points to a link, you create a Capability that points directly to a path in storage. That Capability would then have a unique identifier that could be tracked on- or off-chain. The creator of that Capability could then revoke or retarget that Capability at any time by referencing the Capability ID.

It gives us the same abilities we have now (the creator of the Capability can retarget or revoke at any time), without adding the complexity of tracking private links and the accidental reuse of named links that could “resurrect” a Capability that should remain unused…

2 Likes

Another idea, and one that maybe fits better with the CBS model we’re trying to promote in Cadence:

When you create a Capability, you actually create two items, a Capability and a Capability Controller (CapCon). The CapCon would be a Resource that has methods on it that would let you retarget and revoke the associated Capability. Unlike most references in Cadence, a CapCon would follow the Capability even as it was moved around between accounts, and the holder of the CapCon would have the ability to retarget or revoke the Capability wherever it existed.

I like both ideas, having capabilities with unique identifiers pointing directly to storage, and the ability to revoke them, and also reifying that functionality.

The reified variant reminds me of the sealer/unsealer pairs in prior art.

I like both of them too, a little bit more CapCon model.

But it is also a bit tricky and can be abused:

if we remove unlink, as CapCon is a resource, I can get it from the user, then forever I have access to that capability and the user will have no way of removing that.

Even if we keep unlink, they will not be able to enumerate the capabilities they have granted.

Somehow my feeling is CapCon should be stored in AuthAccount somehow (something like grantedCapabilities)

Actually, @bluesign, I think your argument bolsters the idea that we should use a numeric ID. Basically, you’re making the case (successfully, IMHO!) that control over the capability should remain with the account holder of the storage that is being referenced, and it should not be something that can be moved out of that account. I think that makes sense, which means there’s not much value in representing it as a Resource instance.

1 Like