Discussion on EIP-5573: a mechanism on top of Sign-In With Ethereum for informed consent to delegate capabilities with an extensible scope mechanism.
See more details on EIP-5573 here: ERC-5573: Sign-In with Ethereum Capabilities, ReCaps
Discussion on EIP-5573: a mechanism on top of Sign-In With Ethereum for informed consent to delegate capabilities with an extensible scope mechanism.
See more details on EIP-5573 here: ERC-5573: Sign-In with Ethereum Capabilities, ReCaps
Can this be modified so that it is calling its own personal_sign_swie method, probaly not that method name though, so we can both enforce the data structure and so that, from the extension level, the request could be modified to represent the contract wallet if one is in place?
Can you elaborate what you mean by βcalling its own personal_sign_siwe methodβ? The spec is fully personal_sign and SIWE compliant which means it uses the same data structure, same JSON RPC signing methods.
Its not about it not being compliant. Its about this being compatible with ERC 4337 account abstraction adding in separate sign where the form is further conscribed would allow for the extension simply modify the request via injecting the correct address for the SIWE request.
I think that is out of scope for EIP-5573. EIP-5573 is simply describing a common SIWE format that can be used to encode object capabilities. It is not prescriptive on how such a message should be constructed or signed.
If a personal_sign_siwe
method is needed for SIWE to be compatible with ERC 4337, then that should be addressed in another EIP in my opinion.
It has been why most are against 4361 so it tracks that it will be continually recurring issue and shouldnβt be separated considering the eventual road map that includes the of removing EOAs altogether. Unless I am misunderstanding the meaning of that.
Sorry Iβm not sure I understand what you mean by this. What road map are you talking about, and how does this affect 5573?
I am not going to lie this might be either an internal document or something more ethereal in Vitalikβs head either way he has mentioned it several times being on the βroadmapβ, but I can find no public roadmap that goes out that far.
4337 wallets will become very quickly adopted not only as a better wallet option but also because they provide a better UX. Any SIWE EIP that does not support them will ultimately not be widely adopted.
EIP-5573 is not concerned with signing or verification of a SIWE message (it simply uses the methods described in EIP-4361), so I would argue there is no need to concern it with the requirements of EIP-4337.
I agree there will need to be work (possibly an extension EIP) to make SIWE compatible with EIP-4337, however that is not a concern for this 5573. From my understanding there are ongoing conversations on how this could be done. 5573 would not disrupt this work, nor make 4361 less compatible with 4337.
Any link to those aforementioned conversations?
I donβt think thereβs been anything in the public yet, but the internal conversations will start bringing things up.
Hi there, Iβve lightly reviewed the proposal. Iβm a pretty big object capability enthusiast, and there are some things here that I like and some things that I think are going to make it challenging to adopt or expensive to use. Anyways, here are my notes, syndicated from my Roam graph:
URI
as the delegate in the signature
ReCap URI Scheme
.
my.resource.1
.
Thanks for sharing your extensive notes. Hopefully I can address at least some of the issues you raise. Iβll start with the easier ones!
We have an implementation here.
Yes a holder can delegate any of the capabilities in a ReCap, but the ReCap spec doesnβt define a method for how that should be done. If the secondary delegation is also a ReCap, we have an expectation that a link/cid/encoding of the primary delegation would be inserted in ext
(extra fields).
One of the problems we are trying to solve with ReCap is βHow can an ethereum account authorise actions with a system that supports ocaps?β. Itβs clearly bad UX if an end-user is required to directly sign a capability invocation message for every action that needs to be performed. We want to use ReCap to delegate capabilities to a session key, which invokes those capabilities on behalf of the ethereum account. ReCap only defines a delegation format. How that delegation is invoked is dependent on the βtypeβ of delegee and the authorisation system that is being interacted with.
Absolutely, with a caveat. Since ReCap uses SIWE for the signature portion of verification, this is the same problem as βCan contract accounts use SIWE?β. We have been working with Gnosis to provide a method for a Gnosis Safe to sign in with ethereum. In this case the ethereum address in the SIWE message (i.e. the delegator) is the Gnosis Safe, the message is signed with an EOA, and as part of SIWE verification we perform an on-chain lookup to check the binding between the EOA and Gnosis Safe in a βdelegation registryβ. [Note: I personally havenβt actually worked on this so some of the terminology might be inaccurate].
How this should be done more widely is likely the concern of later work to make SIWE compatible with EIP-4337.
Apologies if Iβm wrong, but you may have missed that each capabilities are grouped into namespaces. Does that resolves these concerns? If not, what is ReCap lacking?
Along with SIWE signature verification, to verify a ReCap you must also verify that the human-readable statement is correct. This is done by performing the translation from resources (ReCap URIs) to recap-transformed-statement
, and checking that the statement is suffixed with recap-transformed-statement
. This is described in more detail in the algorithm sections.
Iβm not familiar with any such formats, but would it be enough to insert those signatures (plus any other required data) into ext
(extra fields)? Or could you give an example of such a format?
Would you be able to elaborate on this? Is the βdesignationβ the target resource in this context? And what do you mean by it βis not being conveyed as a programming language objectβ?
We tend to use it to avoid the confusion of using delegate
, being both a verb and a noun
Thanks a lot for reviewing the article and providing feedback. @cobward commented already on most of your points. But here are some additional comments below.
SIWE ReCap will rely on the same JSON-RPC API as SIWE, therefore strictly speaking there is no new JSON-RCP API needed.
The delegate/delegee has to be a URI to be identifiable. DIDs are just examples since they make it easy to authenticate the delegate/delegee but any URI can be used. The documentation of the resource service API has to define what URI schemas are acceptable and that would include DID methods if they even wanted to support DIDs.
SIWE ReCap capabilities can be represented as objects: SIWE message incl. ReCaps + Signature. We expected people will use CACAOs as their container format for this since CACAOs can already represent SIWE messages + Signatures.
There is no strict requirement that each ReCap has to include a resource identifier. We intentionally made the spec open to cater for systems that donβt have URIs identifying their resources. The proposed EIP also allows untargeted (not tied to a resource identifier) ReCaps as well.
(Wow itβs been a while since Iβve been in a magicians thread )
Hey @awoie I just got off a CASA call where weβre exploring make UCAN and SIWx/ReCap interop.
I just wanted to quickly shared something that we learned in the UCAN process that may or may not be helpful for ReCap:
"my.resource.1":[
"append",
"delete"
],
"my.resource.2":[
"append"
],
"my.resource.3":[
"append"
]
UCAN resources looked almost exactly like this early on. We started in languages that use parsing libraries β this is readable and the parser combinators make this stuff very easy. Once folks using JS & TS started writing libraries, we got a bunch of complaints that it was hard to parse when theyβre used to working directly in JSON. This is why we switched to objects:
[
{
"with": "my.resource.2",
"can": "append"
},
{
"with": "my.resource.3",
"can": "append"
}
]
This is totally isomorphic, just laid out differently. It also reads nicely like an English sentence βwith noun
, you can verb
β.
Anyhow, I hope thatβs helpful!
I threw together a quick comparison of ReCap and UCAN. I think there are some changes we could make to ReCap to make it more compatible with UCAN.
Thanks @oed !
More specifically it needs to be some kind of URI. We started with βany stringβ, but Irakli quite rightly pointed out that it makes interop very difficult because the same string can have different meanings
This is possible in UCAN as {with: "*", can: "*/*"}
. We use it regularly for e.g. device linking
We had this at one point, but the complaint was that itβs easier with one per I guess also if you need extensible fields, they may cross unintentionally
The other big difference is that we namespace the can
field too.
More specifically it needs to be some kind of URI. We started with βany stringβ, but Irakli quite rightly pointed out that it makes interop very difficult because the same string can have different meanings
Typo, should have said URI. Updated!
This is possible in UCAN as
{with: "*", can: "*/*"}
. We use it regularly for e.g. device linking
Is *
really a URI? Or is this just a special case.
Either way i think it would make sense for ReCap to use the same approach for the resource string.
We had this at one point, but the complaint was that itβs easier with one per I guess also if you need extensible fields, they may cross unintentionally
Easier in what way? Imo it would be better to avoid duplication here. Tooling can abstract the underlaying data structure and make it easier to use.
The other big difference is that we namespace the
can
field too.
Oh good point. Updating comparison doc to reflect this.
Indeed, good catch: we changed this in 0.9 to be a URI and scoped to a specific DIDβs capabilities. This now looks like: own://did:key:zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV/*
Some folks want to use the DID itself, but I think itβs ambiguous that youβre delegating the rights of things owned by a DID vs rights to update the DID doc itself.
We previously used my:*
for this to disambiguate especially in chains, but the own
URI is more general and easier to work with.
It makes the delegation logic more complex because now you have a nested array, but the same capability could still be delegated in another entry. For example, thereβs nothing stopping you from writing the following:
"my.resource.1":["append","delete"],
"my.resource.1":["read"]
So now you have to deal with the nested array in your code and still look at the level above for others caps on the same resource. If you model this as a JSON object, you have to either merge them, or pick which one is valid, or invalidate the entire credential. Thereβs more that an implementer can mess up, basically.
// what do?
{
"my.resource.1": ["append","delete"],
"my.resource.1": ["read"]
}
If you pick the merge case, then extensible fields get dangerous (easy to mess up what refers to what) unless you reformat them as objects:
{
"my.resource.1": [
{can: "append", on: "Tuesdays"}, // silly nonsense example
"delete"
],
"my.resource.1": ["read"]
}
In UCAN, we solve for this by allowing for hierarchy (which yes, is a tradeoff). This is desirable for a bunch of reasons, including enabling rights amplification (incl. joining caps from different sources) and letting applications extend otherβs semantics, which gives consumers a lot of (safe) flexibility over how they consume and reuse capabilities between apps/services.
Hereβs the diagram from the 0.9 UCAN spec:
βββββββββ
β β
β * β
β β
ββ²βββ²βββ²β
β β β
ββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββββββββ
β β β
ββββββ΄βββββ ββββββ΄ββββββ βββββ΄ββββ
β β β β β β
β msg/* β β crud/* β β ... β
β β β β β β
βββ²ββββββ²ββ βββ²βββββββ²ββ βββββββββ
β β β β
β β β β
β β β β
βββββββββββββ΄β ββββ΄βββββββββββββ ββββββββββββ΄βββ βββ΄ββββββββββββββ
β β β β β β β β
β msg/send β β msg/receive β β crud/read β β crud/mutate β
β β β β β β β β
ββββββββββββββ βββββββββββββββββ βββββββββββββββ βββ²ββββββ²ββββββ²ββ
β β β
ββββββββ β ββββββββ
β β β
ββββββββββββββ΄βββ βββββββββ΄ββββββββ ββββ΄ββββββββββββββ
β β β β β β
β crud/create β β crud/update β β crud/destroy β
β β β β β β
βββββββββββββββββ βββββββββββββββββ ββββββββββββββββββ
Here you can delegate crud/mutate
and avoid the resource duplication. If you squint, this is the same βshapeβ as rights amplification, but where no new authority is conferred. Itβs also the same relationship as between *
and crud/read
(where read
is a subset of *
/βeverythingβ). I will highlight that not everyone loves this, but itβs the same idea as being able to attenuate a resource, but on the ability. The open extensibility (I guess really βjustβ the open-closed principle) of this kind of system means that we can ship a standard library of capabilities and give them really good support in libraries without limiting users to specific use cases.
Phew that was a lot longer than I expected to write Looking forward to the call next week btw!