aries-cloudagent-python
aries-cloudagent-python copied to clipboard
Spike: Investigate/document how to use OOB Connection Reuse with did:peer
Now that we have did:peer:2/4
support in ACA-Py, we should be able to support connection reuse with:
- OOB multi-use invitations
- using
did:peer:2/4
This task is to investigate this use of connections to determine how a Bifold Wallet might be able to use this.
@ianco, could you please take a look at this one? I think it should be easy to try out with the demo and the Swagger API.
I can look if IDLab can provide a web tool to conveniently and repeatedly generate an OOB credential offer QR code. Would this be useful @ianco ?
Just started to look at this today. I'm assuming this will use the /out-of-band/create-invitation
endpoint?
How do you setup the payload to specify did:peer:2/4
(or is it an internal coding change)?
{
"accept": [
"didcomm/aip1",
"didcomm/aip2;env=rfc19"
],
"alias": "Barry",
"attachments": [
{
"id": "attachment-0",
"type": "present-proof"
}
],
"goal": "To issue a Faber College Graduate credential",
"goal_code": "issue-vc",
"handshake_protocols": [
"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0"
],
"mediation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {},
"my_label": "Invitation to Barry",
"protocol_version": "1.1",
"use_public_did": false
}
(above is the sample body on the swagger page)
There is a on option for triggering emitting a DID peer 2 in these scenarios —emit-did-peer-2
. Integration test using did-peer-2 can be found here: https://github.com/hyperledger/aries-cloudagent-python/blob/9031a4a59505719c6446a55dfc0437e5a6f40e30/demo/features/0160-connection.feature#L15
I think that is what you need to use. The trick I’m wondering about is being able to establish a connection, and then being being able to use reuse
when establishing a second connection (so you only have one connection…).
My hope is that it works out of the box, and we just need to describe how to do it. But if it doesn’t work out of the box, what does happen and how do we fix it?
See PR #2713 ... I created an integration test to issue a credential and send a proof request and it fails :-(
(I think due to an overly aggressive DID validation)
Faber.agent | 2024-01-15 19:51:13,533 aries_cloudagent.messaging.models.base ERROR V20CredRequest message validation error:
Faber.agent | Traceback (most recent call last):
Faber.agent | File "/home/aries/aries_cloudagent/messaging/models/base.py", line 200, in deserialize
Faber.agent | schema.loads(obj) if isinstance(obj, str) else schema.load(obj),
Faber.agent | File "/home/aries/.local/lib/python3.9/site-packages/marshmallow/schema.py", line 723, in load
Faber.agent | return self._do_load(
Faber.agent | File "/home/aries/.local/lib/python3.9/site-packages/marshmallow/schema.py", line 910, in _do_load
Faber.agent | raise exc
Faber.agent | marshmallow.exceptions.ValidationError: {'prover_did': ['Value did:peer:2.Vz6MkupZQ2wUQxARjfCQfXEZksrenDvuv2erBN66MxR9tZ3cQ.SeyJpZCI6IiNkaWRjb21tLTAiLCJ0IjoiZGlkLWNvbW11bmljYXRpb24iLCJwcmlvcml0eSI6MCwicmVjaXBpZW50S2V5cyI6WyIja2V5LTEiXSwiciI6W10sInMiOiJodHRwOi8vaG9zdC5kb2NrZXIuaW50ZXJuYWw6ODA0MCJ9 is not an indy decentralized identifier (DID)']}
Both askar and askar-anoncreds fail, but with different errors
@Jsyro FYI ^^^^
@ianco — can you write up the reuse part what you did to enable reuse? How does it work — what does the invitee do, and what does the invited do to make this work? I think I see some complications, so am wondering about it.
Ignore the issue with the integration test. It’s a trivial fix, I’m pretty sure.
@ianco — can you write up the reuse part what you did to enable reuse?
On the demo I just added the --reuse-connections
parameter - for example in https://github.com/hyperledger/aries-cloudagent-python/blob/main/demo/features/0160-connection.feature:
@GHA @UnqualifiedDids
Examples:
| Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra |
| --public-did --did-exchange --reuse-connections | --emit-did-peer-2 | --did-exchange |--emit-did-peer-4 |
this sets up the appropriate aca-py flags (I don't recall offhand, maybe just --public-invites
?) and then the invite uses the /out-of-band
endpoints with the reuse flag.
I can write this up in more detail, where should I put the docs?
Some questions about all the complications I can think of around this:
- Just to confirm — you established two connections, and on the second, the invitee replied with the “reuse” message and everything worked?
- So the trick is
—public-did —emit-did-peer-2
and set multi-use on the invite? - Does the multi-use invitation use a DID Peer 2? Or does it use the invitee’s public DID that is on a ledger (presumably, a
did:sov
)? - When the connection is actually established, does the inviter’s DID have the same DID peer 2 as the invite, or does it create a new one when it sends the
response
message.
Thanks!
- Just to confirm — you established two connections, and on the second, the invitee replied with the “reuse” message and everything worked?
Correct - I manually checked that the same connection was being reused
- So the trick is
—public-did —emit-did-peer-2
and set multi-use on the invite?
Yes
- Does the multi-use invitation use a DID Peer 2? Or does it use the invitee’s public DID that is on a ledger (presumably, a
did:sov
)?
I believe it uses the agent's public DID (presumably a did:sov
), is it possible to setup a DID using a different format?
- When the connection is actually established, does the inviter’s DID have the same DID peer 2 as the invite, or does it create a new one when it sends the
response
message.
I'll need to check this ...
V20CredRequest
https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/models/cred_request.py#L53
Looks like this is the class that is likely inherited by V20CredRequest that defines the prover_did regex. this needs to be updated to handle a wider range of dids. I'd probably make a new validator that imports and use the regex defined in pydid, assuming we are still leveraging that library.
https://github.com/Indicio-tech/pydid/blob/main/pydid/did.py
Let me know if i need to do this, and how we build tests for this usually in the future.
Sorry @Jsyro — I thought the comments got onto this. The correction needed is covered here, along with the explanation #2714. We need to remove the checking entirely. @ianco has that issue. It’s a trivial change (I could make it almost!) except for I want to make sure the testing is appropriately updated.
@swcurran is there any work outstanding on this issue or can we close it?
I can take this. Need to summarize and get it back to BC Wallet team. Thanks!
@ianco -- a couple of things in this:
- In step 7 of the demo, there is an error generated in the Faber log. Works fine when I do step 5 (paste in the same invitation a second time).
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-60' coro=<AriesAgent.handle_connections() done, defined at /home/aries/demo/runners/agent_container.py:132> exception=KeyError('rfc23_state')>
Traceback (most recent call last):
File "/home/aries/demo/runners/agent_container.py", line 149, in handle_connections
if message["rfc23_state"] in ["completed", "response-sent"]:
KeyError: 'rfc23_state'
The process actually works, but the error should probably be cleaned up. Do you want me to create a new issue for that?
- Also, this task was intended to be that we use a did:peer:2 (or 4) as the DID in the invitation, and that we use the same did:peer:2 (or 4) in every invitation. The demo implementation instead uses the existing
did:sov
for the invitation. Could you do a run through (using Swagger) where you use adid:peer:2
in the invitations just to make sure that works? Once you have done, perhaps you could update the--reuse-connection
implementation in Faber to use a did:peer:2 (or 4). If that is done, we have to make sure to use the SAME DID on every invitation.
If this needs clarification, or I have it wrong, lets discuss.
Thanks!
The connection reuse logic in the demo is a bit messed up, hence the error. I'm looking into it ...
Is there anything in ACA-Py for reuse? IMHO — the reuse should be happening in ACA-Py automagically.
I think aca-py is ok but the demo needs to respond to the webhooks etc. It looks like the demo is making some assumptions about when it should expect a new (vs existing) connection.
Opened a PR to fix some issues with the demo (PR #2813) however there are now issues with did:peer
.
When running the demo using did:peer
and connection reuse, alice gets the following error when accepting the connection:
Alice | 2024-02-23 18:42:17,990 aries_cloudagent.core.conductor ERROR Exception in message handler:
Alice | Traceback (most recent call last):
Alice | File "/usr/local/lib/python3.9/asyncio/tasks.py", line 256, in __step
Alice | result = coro.send(None)
Alice | File "/home/aries/aries_cloudagent/core/dispatcher.py", line 253, in handle_message
Alice | await handler(context, responder)
Alice | File "/home/aries/aries_cloudagent/protocols/didexchange/v1_0/handlers/response_handler.py", line 29, in handle
Alice | conn_rec = await mgr.accept_response(
Alice | File "/home/aries/aries_cloudagent/protocols/didexchange/v1_0/manager.py", line 845, in accept_response
Alice | await self.record_did(response.did)
...
Alice | File "/home/aries/.local/lib/python3.9/site-packages/pydid/verification_method.py", line 52, in __init__
Alice | super().__init__(**data)
Alice | File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
Alice | pydantic.error_wrappers.ValidationError: 1 validation error for Ed25519VerificationKey2018
Alice | publicKeyBase58
Alice | none is not an allowed value (type=type_error.none.not_allowed)
Also it looks like the DID in the invitation isn't a did:peer
.
@swcurran would appreciate some guidance here, should the connection automatically use a did:peer
or does the controller need to create one explicitly? (This also doesn't work right now, when to create a wallet DID and specify did:peer:2
it raises an error, and it looks like did:peer:4
isn't supported?)
I think the goals are:
- we likely want to have a default flow that uses a
did:peer:2
ordid:peer:4
(based on theemit…
setting) for OOB invitations.-
did:4…
if no setting
-
- the same DID is used for all invitations to enable reuse.
- perhaps an override could be given on generating an invitation. But if not already there, don’t bother.
- the DID should be automatically created if it doesn’t exist when creating an invitation (e.g., the first one)
- If that is not in the existing flow, perhaps we don’t need that? E.g. if the controller already has to do it explicitly.
- the Controller could create their own "invitation" DID for use as “the DID” for invitations before the first. That would allow them to, for example, create/use a
did:sov
DID for invitations if they really want to.
What does the current flow look like?
Thanks
I think the goals are:
- we likely want to have a default flow that uses a
did:peer:2
ordid:peer:4
(based on theemit…
setting) for OOB invitations.
did:4…
if no setting
I don't think this is a big deal, we just need to generate a did:peer:*
DID for the invitation
- the same DID is used for all invitations to enable reuse.
You mean for all invitations? Or just for re-use? I'm not sure the best way to do this, other than checking (when they create a new multi-use invitation) to see if there is already an existing multi-use invitation (and then re-using it). Otherwise we need to create a special DID in the wallet and tag it somehow.
- perhaps an override could be given on generating an invitation. But if not already there, don’t bother.
the DID should be automatically created if it doesn’t exist when creating an invitation (e.g., the first one)
- If that is not in the existing flow, perhaps we don’t need that? E.g. if the controller already has to do it explicitly.
- the Controller could create their own "invitation" DID for use as “the DID” for invitations before the first. That would allow them to, for example, create/use a
did:sov
DID for invitations if they really want to.
The "create DID" API currently doesn't work for did:peer:*
DID's ...
What does the current flow look like?
I'll put some more details in another comment.
Reuse should work for all invitations by default, single- and multi-use. All agents should want to support reuse, so we want to make that path easy, and an agent can go out of their way to not support reuse. I don’t know of a use case where they don’t want to support reuse.
Otherwise we need to create a special DID in the wallet and tag it somehow.
Yes — I think that is what we need to do.
I was kind of worried we didn’t support this, especially with did:peer:2/4
, hence this ticket :-). So it is not just a clarify, but real work...
- Add support for creating
did:peer:2
anddid:peer:4
to thePOST /wallet/did/create
endpoint. Also add an optional tag to specify the did's "role" (for now the only possible role is to make the did the default for use in invitations). - For the OOB invitation, by default use the tagged did when creating an invitation (unless "public did" is selected). If there is no existing did already tagged for use in invitations, then create one. Provide an optional flag - "use unique did" - for any use cases where the inviter wants to issue a unique did per invitation (for example they don't want the invitee to reuse connections, possibly for privacy purposes).
- When creating a did for invitations (when it has to be done automatically), use the overall aca-py setting (
--emit-did-peer-2
,--emit-did-peer-4
, or by default use adid:key
) - The invitee should always check if the invitation contains an existing did (i.e. already used to make a connection) in which case it should reuse the existing connection.