Giter Club home page Giter Club logo

acapy-java-client's Introduction

ACA-PY Java Client Library

License CI/CD Maven Central

Convenience library based on okhttp and gson to interact with aries cloud agent python (aca-py) instances.

Use it in your project

<dependency>
   <groupId>network.idu.acapy</groupId>
   <artifactId>aries-client-python</artifactId>
   <version>0.10.0</version>
</dependency>

FAQ

  1. Why don't you use swagger codegen?

    For a long time aca-py's swagger.json was not really in sync with the code base. This has been hugely improved lately, so I started to generate model classes based on the stable releases found on dockerhub. There are still issues with complex structures, so one can not simply use the models 1:1, instead each one has to be checked manually before using it. This is tedious work and might take a while to complete. Also, the api is complex so that I found it useful to introduce helper methods directly in the model classes to make them more accessible.

  2. Why is endpoint X, or field Y missing?

    aca-py's api is changing rapidly with each release, and until most of the classes are using the generated models this can happen. So, if you are missing something create a PR with a fix or open an issue.

Version Compatibility

Client Version ACA-PY Version
0.10.0 0.10.x
0.8.0 0.8.0
0.7.0 0.7.0
0.7.6 0.7.1, 0.7.2
>= 0.7.18 0.7.3
>= 0.7.25 0.7.4
>= 0.7.32 0.7.5

Implemented Endpoints

Method Endpoint Implemented
action-menu
POST /action-menu/{conn_id}/close
POST /action-menu/{conn_id}/fetch
POST /action-menu/{conn_id}/perform
POST /action-menu/{conn_id}/request
POST /action-menu/{conn_id}/send-menu
basicmessage
POST /connections/{conn_id}/send-message
connection
GET /connections
POST /connections/create-invitation
POST /connections/create-static
POST /connections/receive-invitation
GET /connections/{conn_id}}
DELETE /connections/{conn_id}
POST /connections/{conn_id}/accept-invitation
POST /connections/{conn_id}/accept-request
GET /connections/{conn_id}/endpoints
POST /connections/{conn_id}/establish-inbound/{ref_id}
GET /connections/{conn_id}/metadata
POST /connections/{conn_id}/metadata
credential-definition
POST /credential-definitions
GET /credential-definitions/created
GET /credential-definitions/{cred_def_id}
POST /credential-definitions/{cred_def_id}/write_record
credentials
GET /credentials/mime-types/{credential_id}
GET /credentials/revoked/{credential_id}
GET /credential/w3c/{credential_id}
DELETE /credential/w3c/{credential_id}
GET /credential/{credential_id}
DELETE /credential/{credential_id}
GET /credentials
POST /credentials/w3c
did-exchange
POST /didexchange/create-request
POST /didexchange/receive-request
POST /didexchange/{conn_id}/accept-invitation
POST /didexchange/{conn_id}/accept-request
POST /didexchange/{conn_id}/reject
discover-features
GET /discover-features/query
GET /discover-features/records
discover-features v2.0
GET /discover-features-2.0/queries
GET /discover-features-2.0/records
endorse-transaction
POST /transaction/{tran_id}/resend
POST /transactions
POST /transactions/create-request
POST /transactions/{conn_id}/set-endorser-info
POST /transactions/{conn_id}/set-endorser-role
POST /transactions/{tran_id}
POST /transactions/{tran_id}/cancel
POST /transactions/{tran_id}/endorse
POST /transactions/{tran_id}/refuse
POST /transactions/{tran_id}/write
introduction
POST /connections/{conn_id}/start-introduction
issue-credential v1.0
POST /issue-credential/create
POST /issue-credential/create-offer
GET /issue-credential/records
GET /issue-credential/records/{cred_ex_id}
DELETE /issue-credential/records/{cred_ex_id}
POST /issue-credential/records/{cred_ex_id}/issue
POST /issue-credential/records/{cred_ex_id}/problem-report
POST /issue-credential/records/{cred_ex_id}/send-offer
POST /issue-credential/records/{cred_ex_id}/send-request
POST /issue-credential/records/{cred_ex_id}/store
POST /issue-credential/send
POST /issue-credential/send-offer
POST /issue-credential/send-proposal
issue-credential v2.0
POST /issue-credential-2.0/create
POST /issue-credential-2.0/create-offer
GET /issue-credential-2.0/records
GET /issue-credential-2.0/records/{cred_ex_id}
DELETE /issue-credential-2.0/records/{cred_ex_id}
POST /issue-credential-2.0/records/{cred_ex_id}/issue
POST /issue-credential-2.0/records/{cred_ex_id}/problem-report
POST /issue-credential-2.0/records/{cred_ex_id}/send-offer
POST /issue-credential-2.0/records/{cred_ex_id}/send-request
POST /issue-credential-2.0/records/{cred_ex_id}/store
POST /issue-credential-2.0/send
POST /issue-credential-2.0/send-offer
POST /issue-credential-2.0/send-proposal
POST /issue-credential-2.0/send-request
jsonld
POST /jsonld/sign
POST /jsonld/verify
ledger
GET /ledger/config
GET /ledger/did-endpoint
GET /ledger/did-verkey
GET /ledger/get-nym-role
GET /ledger/get-write-ledger
GET /ledger/get-write-ledgers
POST /ledger/register-nym
PATCH /ledger/rotate-public-did-keypair
GET /ledger/taa
POST /ledger/taa/accept
POST /ledger/{ledger_id}/set-write-ledger
mediation
GET /mediation/default-mediator
DELETE /mediation/default-mediator
GET /mediation/keylists
POST /mediation/keylists/{mediation_id}/send-keylist-query
POST /mediation/keylists/{mediation_id}/send-keylist-update
POST /mediation/request/{conn_id}
GET /mediation/requests
GET /mediation/requests/{mediation_id}
DELETE /mediation/requests/{mediation_id}
POST /mediation/requests/{mediation_id}/deny
POST /mediation/requests/{mediation_id}/grant
POST /mediation/update-keylist/{conn_id}
PUT /mediation/{mediation_id}/default-mediator
multitenancy
POST /multitenancy/wallet
GET /multitenancy/wallet/{wallet_id}
PUT /multitenancy/wallet/{wallet_id}
POST /multitenancy/wallet/{wallet_id}/remove
POST /multitenancy/wallet/{wallet_id}/token
GET /multitenancy/wallets
out-of-band
POST /out-of-band/create-invitation
POST /out-of-band/receive-invitation
present-proof v1.0
POST /present-proof/create-request
GET /present-proof/records
GET /present-proof/records/{pres_ex_id}
DELETE /present-proof/records/{pres_ex_id}
GET /present-proof/records/{pres_ex_id}/credentials
POST /present-proof/records/{pres_ex_id}/problem-report
POST /present-proof/records/{pres_ex_id}/send-presentation
POST /present-proof/records/{pres_ex_id}/send-request
POST /present-proof/records/{pres_ex_id}/verify-presentation
POST /present-proof/send-proposal
POST /present-proof/send-request
present-proof v2.0
POST /present-proof-2.0/create-request
GET /present-proof-2.0/records
GET /present-proof-2.0/records/{pres_ex_id}
DELETE /present-proof-2.0/records/{pres_ex_id}
GET /present-proof-2.0/records/{pres_ex_id}/credentials
POST /present-proof-2.0/records/{pres_ex_id}/problem-report
POST /present-proof-2.0/records/{pres_ex_id}/send-presentation
POST /present-proof-2.0/records/{pres_ex_id}/send-request
POST /present-proof-2.0/records/{pres_ex_id}/verify-presentation
POST /present-proof-2.0/send-proposal
POST /present-proof-2.0/send-request
resolver
GET /resolver/resolve/{did}
revocation
GET /revocation/active-registry/{cred_def_id}
POST /revocation/active-registry/{cred_def_id}/rotate
POST /revocation/clear-pending-revocations
POST /revocation/create-registry
GET /revocation/credential-record
POST /revocation/publish-revocations
GET /revocation/registries/created
DELETE /revocation/registry/delete-tails-file
GET /revocation/registry/{rev_reg_id}
PATCH /revocation/registry/{rev_reg_id}
POST /revocation/registry/{rev_reg_id}/definition
POST /revocation/registry/{rev_reg_id}/entry
PUT /revocation/registry/{rev_reg_id}/fix-revocation-entry-state
GET /revocation/registry/{rev_reg_id}/issued
GET /revocation/registry/{rev_reg_id}/issued/details
GET /revocation/registry/{rev_reg_id}/issued/indy_recs
PATCH /revocation/registry/{rev_reg_id}/set-state
PUT /revocation/registry/{rev_reg_id}/tails-file
GET /revocation/registry/{rev_reg_id}/tails-file
POST /revocation/revoke
schema
POST /schemas
GET /schemas/created
GET /schemas/{schema_id}
POST /schemas/{schema_id}/write_record
settings
PUT /settings
GET /settings
trustping
POST /connections/{conn_id}/send-ping
wallet
GET /wallet/did
POST /wallet/did/create
PATCH /wallet/did/local/rotate-keypair
GET /wallet/did/public
POST /wallet/did/public
GET /wallet/get-did-endpoint
POST /wallet/jwt/sign
POST /wallet/jwt/verify
POST /wallet/set-did-endpoint
server
GET /plugins
GET /shutdown
GET /status
GET /status/config
GET /status/live
GET /status/ready
POST /status/reset

Client Examples

Sending Requests: Create the aca-py Rest Client

The rest client is used to send requests against aca-py's admin rest endpoint.

Related aca-py config flags are: --admin <host> <port>, --admin-api-key <api-key>

The default assumes you are running against a single wallet. In case of multi tenancy with base and sub wallets the bearerToken needs to be set as well.

Example aca-py config flags:

--admin 0.0.0.0 8031
--admin-api-key secret

Example client builder:

AriesClient ac = AriesClient
        .builder()
        .url("http://localhost:8031") // optional - defaults to localhost:8031
        .apiKey("secret") // optional - admin api key if set
        .bearerToken("123.456.789") // optional - jwt token - only when running in multi tenant mode
        .build();

Receiving Events: Webhook and Websocket Handler Support

With aca-py you have three options on how to receive status changes:

  1. Poll the rest API - this is not recommended
  2. Register a webhook URL
  3. Connect to aca-py's websocket

Webhook

Related aca-py config flag: --webhook-url <url#api_key>

Single Tenant Controller Example

If running a single wallet and not in multi tenant mode.

Example aca-py config flag: --webhook-url http://localhost:8080/webhook

@Controller
public class WebhookController {

   private EventHandler handler = new EventHandler.DefaultEventHandler();

   @Post("/webhook/topic/{topic}")
   public void handleWebhookEvent(
           @PathVariable String topic,
           @Body String payload) {
      handler.handleEvent(topic, payload);
   }
}
Multi Tenant Example

If running in multi tenant mode.

Example aca-py config flags:

--webhook-url http://localhost:8080/webhook
--multitenant
--jwt-secret 1234
--multitenant-admin

Example multi tenant webhook controller

@Controller
public class WebhookController {

   private EventHandler handler = new TenantAwareEventHandler.DefaultTenantAwareEventHandler();

   @Post("/webhook/topic/{topic}")
   public void handleWebhookEvent(
           @PathVariable String topic,
           @Body String payload,
           HttpRequest request) {
      String walletId = request.getHeaders().get("x-wallet-id");
      handler.handleEvent(walletId, topic, payload);
   }
}

Websocket

If the admin api is enabled aca-py also supports a websocket endpoint under ws(s)://<host>:<admin-port>/ws

Example aca-py config flag: --admin 0.0.0.0 8031

To connect with the websocket you can use the AriesWebSocketClient like:

@Factory
public class AriesSocketFactory {

   @Value("${acapy.ws.url}")
   private String url;
   @Value("${acapy.admin.apiKey}")
   private String apiKey;
   
   @Singleton
   @Bean(preDestroy = "closeWebsocket")
   public AriesWebSocketClient ariesWebSocketClient() {
      return AriesWebSocketClient
              .builder()
              .url(url) // optional - defaults to ws://localhost:8031/ws
              .apiKey(apiKey) // optional - admin api key if set
              .handler(new EventHandler.DefaultEventHandler()) // optional - your handler implementation
              // .bearerToken(bearer) // optional - jwt token - only when running in multi tenant mode
              .build();
   }
}

Writing Custom Event Handlers

To add your own event handler implementation to use in webhooks or in the websocket client, you can either extend or instantiate one of the following classes:

  1. EventHandler
  2. TenantAwareEventHandler
  3. ReactiveEventHandler

All classes take care of type conversion so that you can immediately start implementing your business logic.

Basic EventHandler

@Singleton
public class MyHandler extends EventHandler {
    @Override
    public void handleProof(PresentationExchangeRecord proof) {
        if (proof.roleIsVerifierAndVerified()) {    // received a validated proof
            MyCredential myCredential = proof.from(MyCredential.class);
            // If the presentation is based on multiple credentials this can be done multiple times
            // given that the POJO is annotated with @AttributeGroup e.g.
            MyOtherCredential otherCredential = proof.from(MyOtherCredential.class);
        }
    }
}

Reactive Event Handler

As the websocket client already implements the EventHandler interface you can directly use it like:

AriesWebSocketClient ws = AriesWebSocketClient.builder().build();

// do some stuff, create a connection, or receive invitation

// blocking wait 
ConnectionRecord active = ws.connection()
    .filter(ConnectionRecord::stateIsActive)
    .blockFirst(Duration.ofSeconds(5));
// none blocking
ws.connection()
    .filter(ConnectionRecord::stateIsActive)
    .subscribe(System.out::println);

A Word on Credential POJO's

The library assumes credentials are flat Pojo's like:

@Data @NoArgsConstructor @Builder
@AttributeGroupName("referent") // the referent that should be matched in the proof request
public final class MyCredential {
   private String street;

   @AttributeName("e-mail")
   private String email;       // schema attribute name is e-mail

   @AttributeName(excluded = true)
   private String comment;     // internal field
}

How fields are serialised/deserialized can be changed by using the @AttributeName or @AttributeGroupName annotations.

Create a connection

ac.connectionsReceiveInvitation(
        ReceiveInvitationRequest.builder()
            .did(did)
            .label(label)
            .build(), 
        ConnectionReceiveInvitationFilter
            .builder()
            .alias("alias")
            .build())    
.ifPresent(connection -> {
    log.debug("{}", connection.getConnectionId());
});

Issue a Credential

MyCredential myCredential = MyCredential
        .builder()
        .email("[email protected]")
        .build();
ac.issueCredentialSend(
        new V1CredentialProposalRequest(connectionId, credentialdefinitionId, myCredential));

Present Proof Request

PresentProofRequest proofRequest = PresentProofRequestHelper.buildForEachAttribute(
    connectionId,
    MyCredential.class,
    ProofRestrictions.builder()
        .credentialDefinitionId(credentialDefinitionId)
        .build());
ac.presentProofSendRequest(proofRequest);

Build Connectionless Proof Request

Connectionless proofs are more a thing of mobile wallets, because mostly they involve something that is presented to a human like a barcode, but the java client supports this by providing models and builders.

A flow has the usually following steps:

  1. The user is presented with a QRCode that contains an invitation URL like: https://myhost.com/url/1234
  2. The server side HTTP handler of this URL responds with an HTTP.FOUND response that has the proof request encoded in the m parameter
  3. The mobile wallet tries to match a stored credential, and then responds with a proof presentation if possible
  4. The server side WebhookHandler waits for the proof and then triggers further actions
@Get("/url/{requestId}")
public HttpResponse<Object> connectionLessProof(@QueryValue String requestId) {
    boolean matchingRequest = false; // TODO manage request states
    String proofRequestBase64 = ""; // TODO on how to build this see the example below
    if (matchingRequest) {
        return HttpResponse
                .status(HttpStatus.FOUND)
                .header("location", deploymentUri + "?m=" + proofRequestBase64;
    }
    return HttpResponse.notFound();
}

Proof Request Builder Example

ProofRequestPresentationBuilder builder = new ProofRequestPresentationBuilder(ariesClient);

PresentProofRequest presentProofRequest = PresentProofRequestHelper.buildForEachAttribute(
        connectionId,
        List.of("name", "email"),
        ProofRestrictions
            .builder()
            .schemaId("WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0")
            .build());

Optional<String> base64 = builder.buildRequest(presentProofRequest);

acapy-java-client's People

Contributors

dependabot[bot] avatar etschelp avatar ianco avatar l-wegner avatar majdt51 avatar pixelschnitzel avatar schlagtim avatar tdiesler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acapy-java-client's Issues

Support extended VerifiableCredential

A Verifiable Credential could have extra properties as described in https://www.w3.org/TR/vc-data-model/#extensibility .
Example:

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://carla.dih-cloud.com/chained-credential-v1.jsonld",
    "https://carla.dih-cloud.com/participant-credential-v1.jsonld"
  ],
  "credentialSubject": {
    "participantDetails": {
      "name": "string"
    },
    "identityDetails": {
      "did": "did:sov:ArqouCjqi4RwBXQqjAbQrG",
      "uniqueIds": [
        {
          "type": "TEST",
          "value": "100"
        }
      ]
    },
    "id": "did:sov:ArqouCjqi4RwBXQqjAbQrG"
  },
  "provenanceProof": [
    {
      "algorithm": "RSA_RAW",
      "value": [
        "bbbbnmWPldib7jnG2k1TN0fTCtX........gvvLs="
      ]
    }
  ],
  "id": "urn:uuid:23711187-dec1-4bf6-2227-55710f977333",
  "issuanceDate": "2019-12-03T12:19:52Z",
  "issuer": "did:sov:7rB93fLvW5kgujZ4E57ZxL",
  "type": [
    "VerifiableCredential",
    "ParticipantCredential",
    "ChainedCredential"
  ]
}

When I need to extract the issued credential from the exchange record I use this method credExRecord.resolveLDCredential().credential. However, the class VerifiableCredential supports no extra properties (in my case provenanceProof) and it get removed without any warning

From Discord chat
etschelp: "As the provenanceProof is a proof and not the credentialSubject it will not be returned with .credential() ,as it is one node further up in the structure. The client library currently only supports retrieving custom credentialSubjects, and is based on a not extended VerifiableCredential otherwise. What you need is basically to extend the VerifiableCredential.class model on your end and then pass it on as a type to the client when retrieving or creating v2 ld credentials or proofs. This is doable but requires some reworking of the client's api."

Consider using enum instead of string for RevocationEvent.state

... assuming those states are defined (couldn't find information about that). So far, I see state=revoked

Alice: [@Acme] issuer_cred_rev RevocationEvent(state=revoked, createdAt=2022-04-27T10:22:21.856908Z, revRegId=KfUGQ8cJceaRiBfBeAAjYv:4:KfUGQ8cJceaRiBfBeAAjYv:3:CL:434:default:CL_ACCUM:3718e227-92e5-446f-8e8e-68c88d92218e, recordId=cbcb26ff-35ed-4f6e-a16d-26d9b0648609, credDefId=KfUGQ8cJceaRiBfBeAAjYv:3:CL:434:default, credExId=89106a4d-09d6-4876-8a40-5849f8efe9cf, uploadedAt=null, credRevId=1)

Revocation may not work as expected

This isn't necessarily an issue with the Java client ... perhaps we could look at this here first anyway

Thrift sends a Proof Request with non-revoked

thrift.presentProofSendRequest(PresentProofRequest.builder()
        .connectionId(thriftAliceConnectionId)
        .proofRequest(ProofRequest.builder()
                .name("Loan-Application")
                .nonce("1")
                .requestedAttribute("attr1_referent", restrictedProofReqAttr.apply("employee_status", jobCertificateCredDefId))
                .requestedPredicate("pred1_referent", restrictedProofReqPred.apply("salary >= 2000", jobCertificateCredDefId))
                .requestedPredicate("pred2_referent", restrictedProofReqPred.apply("experience >= 1", jobCertificateCredDefId))
                .nonRevoked(ProofNonRevoked.builder()
                        .to(Instant.now().getEpochSecond())
                        .build())
                .build())
        .build()).get();

Alice provides the Proof from the Credentials she holds in her wallet ...

alice.presentProofRecordsSendPresentation(presentationExchangeId, PresentationRequest.builder()
        .requestedAttributes(indyRequestedAttr.apply("attr1_referent", true))
        .requestedPredicates(indyRequestedPred.apply("pred1_referent"))
        .requestedPredicates(indyRequestedPred.apply("pred2_referent"))
        .build());

Thrift verifies the Proof ...

presentationExchangeId = verifierExchangeRecord[0].getPresentationExchangeId();
thrift.presentProofRecordsVerifyPresentation(presentationExchangeId).get();

All works well, and Alice gets the loan. The above also works without nonRevoked. Not sure if that is a bug already or just a sensible default.

Later, Acme revokes the Job-Credential ...

acme.revocationRevoke(RevokeRequest.builder()
        .credExId(credex.getCredentialExchangeId())
        .connectionId(connectionId)
        .publish(true)
        .notify(true)
        .build()).get();

Alice then tries to get another loan, now without having a job.

Surprisingly, this works as well (i.e. Alice can provide a Proof that Thrift verifies sucessfully)

2022-04-27 15:00:12 INFO  [io.nessus.aries.test.GettingStartedTest] - Acme revokes the Job-Certificate Credential ============================================================================
2022-04-27 15:00:12 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice: [@Acme] revoked RevocationEvent(state=revoked, createdAt=2022-04-27T13:00:10.937082Z, revRegId=TwdTw38rdAgG2Ag...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice: [@Acme] ISSUER CREDENTIAL_REVOKED V1CredentialExchange(super=BaseCredExRecord(autoIssue=null, autoOffer=false,...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice applies for a Loan with Thrift ===================================================================================
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Thrift: [@Thrift] VERIFIER REQUEST_SENT PresentationExchangeRecord(autoPresent=false, connectionId=32e2afbd-d9aa-4af6-...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice: [@Alice] PROVER REQUEST_RECEIVED PresentationExchangeRecord(autoPresent=null, connectionId=f4ebfd3a-8478-43e0-8...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice: [@Alice] PROVER PRESENTATIONS_SENT PresentationExchangeRecord(autoPresent=null, connectionId=f4ebfd3a-8478-43e0...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Thrift: [@Thrift] VERIFIER PRESENTATION_RECEIVED PresentationExchangeRecord(autoPresent=false, connectionId=32e2afbd-d...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Thrift: [@Thrift] VERIFIER VERIFIED PresentationExchangeRecord(autoPresent=false, connectionId=32e2afbd-d9aa-4af6-b615...
2022-04-27 15:00:13 INFO  [io.nessus.aries.test.GettingStartedTest] - Alice: [@Alice] PROVER PRESENTATION_ACKED PresentationExchangeRecord(autoPresent=null, connectionId=f4ebfd3a-8478-43e0...

AFAICS, its not a timing issue.

Cannot use DID Exchange between multitenant wallets

After we successfully tackled RFC 0160: Connection Protocol with multitenant wallets, I'd like to move on to RFC 0023: DID Exchange Protocol 1.0.

The idea is that Alice (who does not have a public DID) wants to establish a connection with Faber.
Here is what I have so far ...

ConnectionRecord aliceRecord = alice.didExchangeCreateRequest(DidExchangeCreateRequestFilter.builder()
        .theirPublicDid(faberPublicDid.getDid())
        .myEndpoint(ACAPY_USER_URL)
        .myLabel("Alice/Faber")
        .build()).get();
log.info("Alice: {}", aliceRecord);

assertConnectionState(aliceWallet, ConnectionState.REQUEST);

/* Faber receives the request
 * 
 * Fails: DIDXRequest.DIDXThread not initialised ???
 * 
faber.didExchangeReceiveRequest(DIDXRequest.builder()
        .id(connectionRecord.getRequestId())
        .label("Alice/Faber") // label required, but not part of Alice's record
        //.did("Alice's DID ???")
        //.type("???")
        //.didDocAttach(???)
        .build(), DidExchangeReceiveRequestFilter.builder()
            .autoAccept(true)
            .build()).get();
*/

/* Alice accepts connection request
 * 
 * Fails with code=404 message=Record not found
alice.didExchangeAcceptRequest(aliceConnectionId, DidExchangeAcceptRequestFilter.builder().build()).get();
 */

/* Faber accepts the request
 *
 * Fails, because Faber does not (yet) have a connection id
 faber.didExchangeAcceptRequest(faberConnectionId, null);
 */

Acapy is started with --auto-ping-connection and --auto-accept-requests

Wrong @SerializedName in V1CredentialXxxRequest

Dear developers,
we are using the Bosch Aca-py client to build a controller that connects to another agent and issues a credential to that agent.
When using V1CredentialOfferRequest.java to call the method issueCredentialSendOffer on AriesClient we encounter a problem with the serialization of this object to JSON.
From the specification Hyperledger Aries RFC 0036 it is expected that the credential definition id contained in the request should be serialized to cred_def_id but in V1CredentialOfferRequest.java the code says

    @SerializedName(value = CredDefId.CREDENTIAL_DEFINITION_ID, alternate = CredDefId.CRED_DEF_ID)
    private String credentialDefinitionId;

which means that the credential definition id will be serialized to a JSON attribute with key credential_definition_id.
(Same for V1CredentialProposalRequest).

Currently, we patched these classes locally but this is not a solution, of course.
Please let us know how to proceed.

Thanks in advance,
Hans.

Exception when calling AriesClient.outOfBandCreateInvitation

I have found a when calling AriesClient.outOfBandCreateInvitation

There is a demo project which shows the problem with a unit test (using Jersey for the REST server):

https://github.com/jhoar/ajc-oob-invite

There is no exception dump in 0.7.26 and 0.7.27, but in 0.7.28 I get back:

...
[qtp201556483-20] INFO RestHandler - topic: out_of_band, payload: {"created_at": "2022-08-27T13:21:42.529712Z", "invitation": {"@type": "https://didcomm.org/out-of-band/1.0/invitation", "@id": "ce5048da-190c-40e2-946a-95a175528ef9", "label": "oobinv.agent", "services": [{"id": "#inline", "type": "did-communication", "recipientKeys": ["did:key:z6MkvabGhkcSKSZ9jcxevULqUnmmez8kww8p92chKF9xCLZp"], "serviceEndpoint": "http://172.17.0.1:8020/"}], "handshake_protocols": ["https://didcomm.org/didexchange/1.0", "https://didcomm.org/connections/1.0"]}, "connection_id": "41356875-7902-4d9c-b938-6dfe61325a32", "trace": false, "invi_msg_id": "ce5048da-190c-40e2-946a-95a175528ef9", "state": "await-response", "role": "sender", "oob_id": "8801ba6f-df9f-4429-ac08-21b2132f7893", "updated_at": "2022-08-27T13:21:42.529712Z", "our_recipient_key": "H8LE7WMzyu4gd87xEuNzdhDmqQruY3tTT1hmUyBwH7nS"}
[qtp201556483-17] INFO RestHandler - topic: connections, payload: {"created_at": "2022-08-27T13:21:42.505967Z", "connection_protocol": "didexchange/1.0", "connection_id": "41356875-7902-4d9c-b938-6dfe61325a32", "their_role": "invitee", "state": "invitation", "invitation_key": "H8LE7WMzyu4gd87xEuNzdhDmqQruY3tTT1hmUyBwH7nS", "rfc23_state": "invitation-sent", "accept": "auto", "invitation_msg_id": "ce5048da-190c-40e2-946a-95a175528ef9", "routing_state": "none", "updated_at": "2022-08-27T13:21:42.505967Z", "invitation_mode": "once", "alias": "foo"}
[qtp201556483-20] ERROR org.hyperledger.aries.webhook.EventParser - Could not format json body
com.google.gson.JsonSyntaxException: Failed parsing 'H8LE7WMzyu4gd87xEuNzdhDmqQruY3tTT1hmUyBwH7nS' as UUID; at path $.our_recipient_key
at com.google.gson.internal.bind.TypeAdapters$24.read(TypeAdapters.java:565)
at com.google.gson.internal.bind.TypeAdapters$24.read(TypeAdapters.java:554)
....

The ACAPY config is in runAcapy.sh (using docker and a local von-network instance, also docker).

Any suggestions?

Thanks!

john

Cannot establish connection between multitenant agents

With a call sequence like this ...

            // Alice receives the connection request
            ConnectionRecord connectionRecord = alice.connectionsReceiveInvitation(ReceiveInvitationRequest.builder()
                    .did(faberPublicDID.getDid())
                    .build(), null).get();
            String aliceConnectionId = connectionRecord.getConnectionId();
            log.info("Alice: {}", connectionRecord);
            
            logWalletConnections(aliceWallet);
            
            // Alice accepts the connection invitation
            connectionRecord = alice.connectionsAcceptInvitation(aliceConnectionId, null).get();
            log.info("Alice: {}", connectionRecord);
            
            logWalletConnections(aliceWallet);
            
            // Alice accepts the connection request
            connectionRecord = alice.connectionsAcceptRequest(aliceConnectionId, null).get();
            log.info("Alice: {}", connectionRecord);
              
             logWalletConnections(faberWallet);

I get ...

2022-04-07 18:01:41 INFO  [io.nessus.aries.test.ConnectionTest] - Alice: ConnectionRecord(accept=MANUAL, alias=null, connectionId=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a, connectionProtocol=CONNECTION_V1, createdAt=2022-04-07T16:01:41.947161Z, errorMsg=null, inboundConnectionId=null, invitationKey=null, invitationMode=ONCE, invitationMsgId=29167fa8-ba91-4f55-a3c1-13d08c9759a0, myDid=null, requestId=null, rfc23Sate=null, routingState=NONE, state=INVITATION, theirDid=null, theirLabel=null, theirPublicDid=null, theirRole=INVITER, updatedAt=2022-04-07T16:01:41.947161Z)
2022-04-07 18:01:41 INFO  [io.nessus.aries.test.ConnectionTest] - Alice: cid=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a state=INVITATION - ConnectionRecord(accept=MANUAL, alias=null, connectionId=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a, connectionProtocol=CONNECTION_V1, createdAt=2022-04-07T16:01:41.947161Z, errorMsg=null, inboundConnectionId=null, invitationKey=null, invitationMode=ONCE, invitationMsgId=29167fa8-ba91-4f55-a3c1-13d08c9759a0, myDid=null, requestId=null, rfc23Sate=null, routingState=NONE, state=INVITATION, theirDid=null, theirLabel=null, theirPublicDid=null, theirRole=INVITER, updatedAt=2022-04-07T16:01:41.947161Z)
2022-04-07 18:01:42 INFO  [io.nessus.aries.test.ConnectionTest] - Alice: ConnectionRecord(accept=MANUAL, alias=null, connectionId=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a, connectionProtocol=CONNECTION_V1, createdAt=2022-04-07T16:01:41.947161Z, errorMsg=null, inboundConnectionId=null, invitationKey=null, invitationMode=ONCE, invitationMsgId=29167fa8-ba91-4f55-a3c1-13d08c9759a0, myDid=4MXNq97f95o6MmgjiZW9WM, requestId=011d8322-fb1a-42a3-b4de-36d593bf9f88, rfc23Sate=null, routingState=NONE, state=REQUEST, theirDid=null, theirLabel=null, theirPublicDid=null, theirRole=INVITER, updatedAt=2022-04-07T16:01:42.054948Z)
2022-04-07 18:01:42 INFO  [io.nessus.aries.test.ConnectionTest] - Alice: cid=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a state=REQUEST - ConnectionRecord(accept=MANUAL, alias=null, connectionId=4dd2ad4d-6fb0-40fe-80f7-114fcabe202a, connectionProtocol=CONNECTION_V1, createdAt=2022-04-07T16:01:41.947161Z, errorMsg=null, inboundConnectionId=null, invitationKey=null, invitationMode=ONCE, invitationMsgId=29167fa8-ba91-4f55-a3c1-13d08c9759a0, myDid=4MXNq97f95o6MmgjiZW9WM, requestId=011d8322-fb1a-42a3-b4de-36d593bf9f88, rfc23Sate=null, routingState=NONE, state=REQUEST, theirDid=null, theirLabel=null, theirPublicDid=null, theirRole=INVITER, updatedAt=2022-04-07T16:01:42.054948Z)
2022-04-07 18:02:28 ERROR [org.hyperledger.aries.BaseClient] - code=404 message=Record not found.

Code to reproduce this is here ...
https://github.com/tdiesler/nessus-aries/blob/acapy-java-client-ghi19/itests/smoke/src/test/java/io/nessus/aries/test/ConnectionTest.java

Cannot handle non-payload WebSocket events

The ping topic that I get from the base wallet has no payload. Also, it'd be nice to have a generic WebSocketListener that does the parsing and delegation to the EventHandler.

/connections/create-invitation params mismatch the ACA-Py API

acapy-java-client version: 0.7.33
acapy version: 0.7.5

I’m currently using the library in a Spring Boot project and have encountered an issue with the CreateInvitationParams class when trying to use it for creating invitation.

I’m trying to set the isPublic field in CreateInvitationParams, expecting it to serialize to a URL parameter named public. However, it serializes to is_public instead, which the API I’m calling does not recognize.

Here’s a simple code snippet showing how I’m using it:

CreateInvitationRequest request = CreateInvitationRequest.builder()
                    .serviceEndpoint("http://1.2.3.4:8101")
                    .myLabel("AccessPoint")
                    // .mediationId(null)
                    // .metadata(null)
                    // .recipientKeys(null)
                    // .routingKeys(null)
                    .build();

            CreateInvitationParams params = CreateInvitationParams.builder()
                    .alias(null) // or provide a value
                    .autoAccept(false)
                    .multiUse(false)
                    .isPublic(true)
                    .build();

            Optional<CreateInvitationResponse> response = ac.connectionsCreateInvitation(request, params);

Is there a recommended way to handle this situation, or could this be considered for a future update? Any guidance or temporary solutions would be greatly appreciated.

Thank you in advance!

Unsupported event topic: out_of_band

ACAPy send messages with this topic, that currently cannot be handled
T001-RFC0025

[camel] [XNIO-1 task-1] INFO route1 - Request: POST agent/command/out-of-band/receive-invitation/ {"data": {"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation", "@id": "71f6b8b2-02ff-47c2-b241-9d98ac91e410", "label": "aca-py.Acme", "services": [{"id": "#inline", "type": "did-communication", "recipientKeys": ["did:key:z6MkpjQnfym7n2DcWrVzefJxpMdMz1nTBWCQSv4AGrMpdgcK"], "serviceEndpoint": "http://host.docker.internal:9021"}], "handshake_protocols": ["did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0"], "use_existing_connection": false}}

RevocationRegistryState should return values in lower case with toString method

Hello,
I got an error message when calling revocationRegistriesCreated(credDefId, RevocationRegistryState.ACTIVE) on AriesClient.
The error message said that only states in lower case are expected.
I patched RevocationRegistryState locally with

    ACTIVE {
    	@Override
    	public String toString() {
    		return "active";
    	}
    },

and it worked.
I suggest to add that kind of a toString() method to all enumeration entries.

Constructing `SendPresentationRequest` with predicates

I'm trying to construct SendPresentationRequest with predicates but only properties that I can set are

public static final class IndyRequestedCredsRequestedPred {
    private String credId;
    private Integer timestamp;
}

shouldn't this predicate have operator (<,>,<=,>=) and value (20)

Unsupported event topic: revocation_registry

This happens following a call to /revocation/create-registry

{
  "revoc_reg_id": "QaxibhCutS4PtETfP155nr:4:QaxibhCutS4PtETfP155nr:3:CL:155:default:CL_ACCUM:ca2ac0a8-8421-437c-aa6a-98cfd7b08b03",
  "pending_pub": [],
  "record_id": "ca2ac0a8-8421-437c-aa6a-98cfd7b08b03",
  "cred_def_id": "QaxibhCutS4PtETfP155nr:3:CL:155:default",
  "issuer_did": "QaxibhCutS4PtETfP155nr",
  "tails_local_path": "/home/indy/.indy_client/tails/QaxibhCutS4PtETfP155nr:4:QaxibhCutS4PtETfP155nr:3:CL:155:default:CL_ACCUM:ca2ac0a8-8421-437c-aa6a-98cfd7b08b03/28w51hm7FaaY5gVQXyW9UEGt5vyxXaCjKkuiQ3zuHaJ9",
  "revoc_def_type": "CL_ACCUM",
  "tag": "ca2ac0a8-8421-437c-aa6a-98cfd7b08b03",
  "tails_hash": "28w51hm7FaaY5gVQXyW9UEGt5vyxXaCjKkuiQ3zuHaJ9",
  "revoc_reg_def": {
    "ver": "1.0",
    "id": "QaxibhCutS4PtETfP155nr:4:QaxibhCutS4PtETfP155nr:3:CL:155:default:CL_ACCUM:ca2ac0a8-8421-437c-aa6a-98cfd7b08b03",
    "revocDefType": "CL_ACCUM",
    "tag": "ca2ac0a8-8421-437c-aa6a-98cfd7b08b03",
    "credDefId": "QaxibhCutS4PtETfP155nr:3:CL:155:default",
    "value": {
      "issuanceType": "ISSUANCE_BY_DEFAULT",
      "maxCredNum": 1000,
      "publicKeys": {
        "accumKey": {
          "z": "1 0523809379DA1E738FAAF35D44B29EF4EE7294671DE577F4C5EC6E89927DFDFC 1 0228AABB111F54CBB2E29D45D176F6B038DE7199FEC0DDCFAFD9E65E0D0E84C9 1 099139E2E81ECC3ADCF91BA0F99C29E9E3FCD9DE7C0A726F29CF024A83451A8A 1 0710910C79D44376EE7AF61D603B994DC464ECD22E172A81BCFEBBAA1DCB7683 1 1C76E251FE7420155BA6F0F6D6C915D290D661856F6B7BE32504A813CCC4D680 1 13474D2D94C549B179921CAF0F4AAF2C7204B1F9519140361F2288CA712696C3 1 02259433F80AEAFE4FD4EC72DF2BEADDE99498C10A3331F2109AB21441E6FF94 1 028D2148310894FFFD05E5FAB1EAF0755004A71C8A4E6E5D0F41F40C78A32D63 1 23B26D03F39B562CF6DE7C1BB1C209041493992EC4D6638BA770791B190D9EBF 1 19566EED4946DA2DCAE5AD6AC811B8616F262798DA06EA2907B090CB37C9FE3E 1 1823BAC4609467345163253D39892B1F83441A5685CD81B2194AF105187D6F07 1 24FE720D1476A0C6E2F9F4A66C7D1B98D372C83C801433D1DA5099ACB2D3DB6C"
        }
      },
      "tailsHash": "28w51hm7FaaY5gVQXyW9UEGt5vyxXaCjKkuiQ3zuHaJ9",
      "tailsLocation": "/home/indy/.indy_client/tails/.hopper/28w51hm7FaaY5gVQXyW9UEGt5vyxXaCjKkuiQ3zuHaJ9"
    }
  },
  "max_cred_num": 1000,
  "created_at": "2022-04-25T15:24:12.624635Z",
  "state": "generated",
  "revoc_reg_entry": {
    "ver": "1.0",
    "value": {
      "accum": "21 1175BB1B6A58AD08E5B27D8F7F1EB76DE63D3A4EBF14E074870FD0AB37317E006 21 111F8C0ED5EBA894E403A56E5F1F2786D2C291403B21D762A13673BE2D5795F8A 6 644145E96EA9F7EFB59A22FD91B3664C8D8BB962349C5E9FF57145432AF8571B 4 38403F6C1E2C53C6450C4B2DE2CEE50D0B78D5E2CBCF8C133C2CE6B3E33F99EB 6 6663CF39AABC038F4093D1E178774A463AD824E3DCB4B473BA15AEE1A8CE1E69 4 126687E73902C39FB4A3512B19514AB4C22EB4C146B6F55CF910AFA86AC83AE5"
    }
  },
  "updated_at": "2022-04-25T15:24:14.041265Z"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.