hyperledger / identus-cloud-agent

Identus Cloud Agent
https://identus.io/
Apache License 2.0
77 stars 20 forks source link

Edge-client initiated connections #1163

Open mixmix opened 2 months ago

mixmix commented 2 months ago

Proposed feature

We have a Catalyst grant around reducing the overhead for edge-clients initiating connections with cloud-agents.

We've looked into how the SDK handles OOB invites, and discovered that the OOB is just converted into a Message which is then sent to the Agent. We would like the Agent to accept messages made by the SDK purely from the peerDID.

Feature description

Either:

mixmix commented 2 months ago

here's what we've done so far:

  1. spin up an Issuer Cloud Agent (AGENT_VERSION=1.33.0 PRISM_NODE_VERSION=2.2.1)
  2. get its DID
  3. create a message to send from the typescript SDK (v5.2.0) :
    Message {
    body: '{"accept":[]}',
    id: '9146542c-7820-475e-95fa-f6adc95c3e34',   // I made this
    piuri: 'https://atalaprism.io/mercury/connections/1.0/request',
    from: DID {
    schema: 'did',
    method: 'peer',
    methodId: '2.Ez6LSm26PTnkN1e7eoQRgx7pEVwUuDH2p3B8rKdky8uEV7biV.Vz6Mkf5JHypNgXjiHwjYPsnykhiKeG2aMpFCZVfqWJPKqDWMG.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6ImRpZDpwZWVyOjIuRXo2TFNnaHdTRTQzN3duREUxcHQzWDZoVkRVUXpTanNIemlucFgzWEZ2TWpSQW03eS5WejZNa2hoMWU1Q0VZWXE2SkJVY1RaNkNwMnJhbkNXUnJ2N1lheDNMZTRONTlSNmRkLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1T0RrNk9EQTRNQ0lzSW1FaU9sc2laR2xrWTI5dGJTOTJNaUpkZlgwLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW5kek9pOHZNVGt5TGpFMk9DNHhMamc1T2pnd09EQXZkM01pTENKaElqcGJJbVJwWkdOdmJXMHZkaklpWFgxOSIsInIiOltdLCJhIjpbXX19',
    uuid: 'did:peer:2.Ez6LSm26PTnkN1e7eoQRgx7pEVwUuDH2p3B8rKdky8uEV7biV.Vz6Mkf5JHypNgXjiHwjYPsnykhiKeG2aMpFCZVfqWJPKqDWMG.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6ImRpZDpwZWVyOjIuRXo2TFNnaHdTRTQzN3duREUxcHQzWDZoVkRVUXpTanNIemlucFgzWEZ2TWpSQW03eS5WejZNa2hoMWU1Q0VZWXE2SkJVY1RaNkNwMnJhbkNXUnJ2N1lheDNMZTRONTlSNmRkLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1T0RrNk9EQTRNQ0lzSW1FaU9sc2laR2xrWTI5dGJTOTJNaUpkZlgwLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW5kek9pOHZNVGt5TGpFMk9DNHhMamc1T2pnd09EQXZkM01pTENKaElqcGJJbVJwWkdOdmJXMHZkaklpWFgxOSIsInIiOltdLCJhIjpbXX19'
    },
    to: DID {
    schema: 'did',
    method: 'peer',
    methodId: '2.Ez6LSkEcsuLjfjcaJAmfSyKVBFmmVvB4vR2iK2foxoQBA7uoT.Vz6MkoaSJGzQxjY6o1TadtduAr6iLe7FdnvwNmych5X1miKEg.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuODk6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0',
    uuid: 'did:peer:2.Ez6LSkEcsuLjfjcaJAmfSyKVBFmmVvB4vR2iK2foxoQBA7uoT.Vz6MkoaSJGzQxjY6o1TadtduAr6iLe7FdnvwNmych5X1miKEg.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuODk6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0'
    },
    attachments: [],
    thid: '9146542c-7820-475e-95fa-f6adc95c3e34', // copy of id
    extraHeaders: [],
    createdTime: '1717987598365',
    expiresTimePlus: '171798759836586400',
    ack: [],
    direction: 1,
    fromPrior: undefined,
    pthid: undefined,
    uuid: '8dadca9c-2976-46f8-b0f9-0d110408f3a1'
    }
  4. have a look at the docker logs docker logs issuer-cloud-agent-1 , see:
    2024-06-10_02:46:38.394 INFO  o.h.i.a.s.DidCommHttpServer@L169:[epollEventLoopGroup-3-4] {request-id=e67a2dc9-2a9c-479e-a0d0-148dc370bd79} - zio-fiber-11508 Received new message
    2024-06-10_02:46:38.395 INFO  o.h.i.a.s.DidCommHttpServer@L169:[epollEventLoopGroup-3-4] {request-id=e67a2dc9-2a9c-479e-a0d0-148dc370bd79} - zio-fiber-11508 Extracted recipient Did => did:peer:2.Ez6LSkEcsuLjfjcaJAmfSyKVBFmmVvB4vR2iK2foxoQBA7uoT.Vz6MkoaSJGzQxjY6o1TadtduAr6iLe7FdnvwNmych5X1miKEg.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuODk6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0
    2024-06-10_02:46:38.399 ERROR o.h.i.a.s.D.didCommServiceEndpoint@L177:[zio-default-blocking-7] {} - zio-fiber-11508 Error processing incoming DIDComm message
    org.hyperledger.identus.agent.walletapi.model.error.DIDSecretStorageError$WalletNotFoundError: null
    at org.hyperledger.identus.agent.walletapi.model.error.DIDSecretStorageError$WalletNotFoundError$.apply(DIDSecretStorageError.scala:9)
    at org.hyperledger.identus.agent.server.DidCommHttpServer$.unpackMessage$$anonfun$2$$anonfun$3$$anonfun$1$$anonfun$1$$anonfun$2(DidCommHttpServer.scala:112)
    at zio.Cause.map$$anonfun$1(Cause.scala:418)
    at zio.Cause.flatMap$$anonfun$2(Cause.scala:173)
    at zio.Cause$$anon$9.failCase(Cause.scala:288)
    at zio.Cause$$anon$9.failCase(Cause.scala:287)
    at zio.Cause.loop$2(Cause.scala:221)
    at zio.Cause.foldContext(Cause.scala:248)
    at zio.Cause.foldLog(Cause.scala:305)
    at zio.Cause.flatMap(Cause.scala:179)
    at zio.Cause.map(Cause.scala:418)
    at zio.ZIO.mapError$$anonfun$1(ZIO.scala:981)
    at zio.ZIO.mapErrorCause$$anonfun$1(ZIO.scala:993)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1147)
    at zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:385)
    at zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:508)
    at zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:223)
    at zio.internal.FiberRuntime.run(FiberRuntime.scala:141)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
mixmix commented 2 months ago

So there's an error. Logging out with we see the SDK sending when handling an OOB invite, we see the message is almost exactly the same format. As far as I can see the only difference is the id and thid fields. My guess is that that error in the Cloud Agent is it failing to look up the thid? It's unclear as I don't know scala though

Message {
  body: '{"accept":[]}',
  id: '4f945cae-84cc-4e6b-bc89-d013bf393c2b',
  piuri: 'https://atalaprism.io/mercury/connections/1.0/request',
  from: DID {
    schema: 'did',
    method: 'peer',
    methodId: '2.Ez6LSoVg5pFjb4vgexgixRKVMw83xTmncqDxju7wrqmV7RY6d.Vz6MktJMBzSFL9M9ktK4oqE9YnGuXrUHDj7BCVjP3nNgVM3XS.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6ImRpZDpwZWVyOjIuRXo2TFNnaHdTRTQzN3duREUxcHQzWDZoVkRVUXpTanNIemlucFgzWEZ2TWpSQW03eS5WejZNa2hoMWU1Q0VZWXE2SkJVY1RaNkNwMnJhbkNXUnJ2N1lheDNMZTRONTlSNmRkLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1T0RrNk9EQTRNQ0lzSW1FaU9sc2laR2xrWTI5dGJTOTJNaUpkZlgwLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW5kek9pOHZNVGt5TGpFMk9DNHhMamc1T2pnd09EQXZkM01pTENKaElqcGJJbVJwWkdOdmJXMHZkaklpWFgxOSIsInIiOltdLCJhIjpbXX19',
    uuid: 'did:peer:2.Ez6LSoVg5pFjb4vgexgixRKVMw83xTmncqDxju7wrqmV7RY6d.Vz6MktJMBzSFL9M9ktK4oqE9YnGuXrUHDj7BCVjP3nNgVM3XS.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6ImRpZDpwZWVyOjIuRXo2TFNnaHdTRTQzN3duREUxcHQzWDZoVkRVUXpTanNIemlucFgzWEZ2TWpSQW03eS5WejZNa2hoMWU1Q0VZWXE2SkJVY1RaNkNwMnJhbkNXUnJ2N1lheDNMZTRONTlSNmRkLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1T0RrNk9EQTRNQ0lzSW1FaU9sc2laR2xrWTI5dGJTOTJNaUpkZlgwLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW5kek9pOHZNVGt5TGpFMk9DNHhMamc1T2pnd09EQXZkM01pTENKaElqcGJJbVJwWkdOdmJXMHZkaklpWFgxOSIsInIiOltdLCJhIjpbXX19'
  },
  to: DID {
    schema: 'did',
    method: 'peer',
    methodId: '2.Ez6LSfWjjGNqWUTpLptjpDSWDZ5HyPhQHjG3wqKEyGwZjogbm.Vz6MknZApExASbSgFXtEBjj2ma2kkhQu2MK9VdCYpCvDxUfpP.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuODk6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0',
    uuid: 'did:peer:2.Ez6LSfWjjGNqWUTpLptjpDSWDZ5HyPhQHjG3wqKEyGwZjogbm.Vz6MknZApExASbSgFXtEBjj2ma2kkhQu2MK9VdCYpCvDxUfpP.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuODk6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0'
  },
  attachments: [],
  thid: '70607554-04c7-4ab5-9de1-f7321d2a37c0', // looks like this is a response as thid !== id
  extraHeaders: [],
  createdTime: '1717978833074',
  expiresTimePlus: '171797883307486400',
  ack: [],
  direction: 1,
  fromPrior: undefined,
  pthid: undefined,
  uuid: 'e045192a-355b-441e-9290-b38d019c1e7d'
}
mixmix commented 2 months ago

More debugging:

Conclusion

So we've discovered that some change in logic in the Cloud Agent would be required, either :

This would mean relaxing the Cloud Agent constraint for a known thid to be found. I think we could check to see if thid === id and if it does that means it's an SDK initiated connection, and if those are set to be allowed, proceed

elribonazo commented 2 months ago

I think this needs to be better defined before the work is started or that's what I'd suggest.

One of the reasons you would do this, is to then later start a issuance of verification request, and in order for the cloud agent to know who it is sending the Offer, Issued credential, Presentation Request... it needs to have a record in the database that has a recognisable name, connectionId, etc.

If the service automatically accepts the connection it will store the connection with a name that is either auto-generated or send by the person who initiates the connection. So how do you really certify that it's that holder and not some other trying to impersonate and keeping in mind that they still don't know each other that may not be a risk u want to be assuming.

Instead, I would create a ConnectionRequest (or similar) which the agent would need to reject or approve through an API call. In that API call, the agent will setup a name and see where the connection is coming from, etc. The rest of flows would still be used as earlier, the agent would need this ConnectionID to start issuance/verification

mixmix commented 2 months ago

Another alternative would be "allow the createConnectionInvitation" API (POST /connections) to be not behind an API_KEY, or to have a lesser key that allows you to hand out the capability to create OOBs.

Having said that, every time we add an http leg we move a step away from being offline-friendly... which means having to have some memory of things we tried but could not connect, and to persist a list of things to keep trying till a connection works. This is just the standard most of Ahau work on, I know it's not the goal of Identus yet, I'm just naming it so there's visibility on how and what prioritize.

mixmix commented 2 months ago

how do you really certify that it's that holder and not some other trying to impersonate and keeping in mind that they still don't know each other that may not be a risk u want to be assuming

I haven't got inside the DIDComm / the https://atalaprism.io/mercury/connections/1.0/request protocols. Am I wrong to assume that those to/from field are not in part signing keys and that these messages carry signatures which make is possible to verify the sender authored the message? i.e. If you can receive a message and use the from field to check it was signed by from ... then what's the problem?

Are connectionIds not used as memories about a handshake that's been done...

Yeah I'm assuming all these messages are designed with certain properties (projecting the affordances scuttlebutt messaging offers) which is a mistake.

essbante-io commented 2 months ago

Hi @mattklepp, please review this item to assess if it can be prioritized.