Skip to content

Latest commit

 

History

History
359 lines (308 loc) · 18.4 KB

README.md

File metadata and controls

359 lines (308 loc) · 18.4 KB

ACA-PY Java Client Library

License CI/CD

Convenience library based on okhttp and gson to interact with aries cloud agent python (aca-py) instances.
It is currently work in progress and not all endpoints of the agent are present in the client.

Use it in your project

<dependency>
   <groupId>network.idu.acapy</groupId>
   <artifactId>aries-client-python</artifactId>
   <version>0.7.0-pre.3.1</version>
</dependency>

For a aca-py 0.6.0 compatible client version you can use the following repository:

<repositories>
    <repository>
        <id>acapy-java-client</id>
        <url>https://nexus.bosch-digital.com/repository/bds-all</url>
    </repository>
</repositories>
<dependency>
    <groupId>org.hyperledger</groupId>
    <artifactId>aries-client-python</artifactId>
    <version>0.23.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.7.0-pre.2 0.7.0-pre.2
0.7.0-pre.3.1 0.7.0-pre.3

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}
credentials
GET /credentials/mime-types/{credential_id}
GET /credentials/revoked/{credential_id}
GET /credential/{credential_id}
DELETE /credential/{credential_id}
GET /credentials
did-exchange
POST /didexchange/create-request
POST /didexchange/receive-request
POST /didexchange/{conn_id}/accept-invitation
POST /didexchange/{conn_id}/accept-request
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
issue-credential v1.0
POST /issue-credential/create
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
jsonld
POST /jsonld/sign
POST /jsonld/verify
ledger
GET /ledger/did-endpoint
GET /ledger/did-verkey
GET /ledger/taa
POST /ledger/taa/accept
mediation
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
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
resolver
GET /resolver/resolve/{did}
revocation
GET /revocation/active-registry/{cred_def_id}
POST /revocation/clear-pending-revocations
POST /revocation/create-registry
POST /revocation/publish-revocations
GET /revocation/registries/created
GET /revocation/registry/{rev_reg_id}
PATCH /revocation/registry/{rev_reg_id}
POST /revocation/revoke
schema
POST /schemas
GET /schemas/{schema_id}
server
GET /status/config
GET /status/live
GET /status/ready
trustping
POST /connections/{conn_id}/send-ping
wallet
GET /wallet/did
POST /wallet/did/create
GET /wallet/did/public
GET /wallet/get-did-endpoint
POST /wallet/set-did-endpoint

Client Examples

Create the aca-py rest client

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.

AriesClient ac = AriesClient
        .builder()
        .url("https://myacapy.com:8031")
        .apiKey("secret") // optional - admin api key if set
        .bearerToken("123.456.789") // optional - jwt token - only when running in multi tennant mode
        .build();

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);

Webhook/Websocket Handler Support

Webhook controller example

@Controller
public class WebhookController {

    @Inject private EventHandler handler;

    @Post("/webhook/{topic}")
    public void ariesEvent(
            @PathVariable String topic,
            @Body String message) {
        handler.handleEvent(topic, message);
    }
}

Websocket client example

import org.hyperledger.aries.config.GsonConfig;
import org.hyperledger.aries.webhook.WebsocketMessage;

public class AcyPyWebsocketClient extends WebSocketClient {
    
    private Gson gson = GsonConfig.defaultConfig();
    private EventHandler handler = new MyHandler();
    
    @OnMessage
    public void onMessage(String message) {
        WebsocketMessage msg = gson.fromJson(message, WebsocketMessage.class);
        handler.handleEvent(msg.getTopic(), msg.getPayload());
    }

    public static void main(String[] args) throws URISyntaxException {
        AcyPyWebsocketClient c = new AcyPyWebsocketClient(new URI(
                "ws://localhost:8031/ws"));
        c.connect();
    }
}

Your event handler can then extend the abstract EventHandler class which takes care of type conversion so that you can immediately implement your business logic.

@Singleton
public class MyHandler extends EventHandler {
    @Override
    public void handleProof(PresentProofPresentation proof) {
        if (PresentationExchangeRole.VERIFIER.equals(proof.getRole())
                && PresentationExchangeState.VERIFIED.equals(proof.getState())) {    // 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);
        }
    }
}

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);