openwallet-foundation / credo-ts Goto Github PK
View Code? Open in Web Editor NEWTypescript framework for building decentralized identity and verifiable credential solutions
Home Page: https://credo.js.org
License: Apache License 2.0
Typescript framework for building decentralized identity and verifiable credential solutions
Home Page: https://credo.js.org
License: Apache License 2.0
When a record is not found sometimes this will throw an error, sometimes this will return null. We should avoid this:
FrameworkError
or a RecordNotFoundError
.findXXX
can return value or null. getXXX
can only return value. If value can't be returned error should be thrown.Issue Credential protocol is currently at v1.1. This adds some fields in comparison to v1.0, but is completely backwards compatible with the version in AIP 1.0.
So we should implement the current version (1.1) from the master branch.
Checklist
Resources
Branch with work in-progress
Currently we import everything from the file it is declared in itself. This works 'fine', and helps in preventing circular dependencies. However as the repo is getting bigger and we need to import a lot of files I think it can be useful to group some of the imports with index files.
I already made a start with this is PR #108 where the diddoc properties all have an index file that exports all subclasses. This prevents other files from having an enormously long import list.
did/
something/
index.ts
publicKey/
index.ts
PublicKey.ts
Sig2018.ts
AnotherPublicKeyMethod.ts
AndAnotherPublicKeyMethod.ts
As a user of the framework I expect the API's to be consistent. The methods in ConnectionsModule
all expect the connectionId
parameter. However the CredentialsModule
expects the credential
itself instead of a credentialRecordId
.
This is inconsistent and I think we should pick either one.
#90 added linting to the CI test job. However this should be a separate job.
However the jobs need some common setup for libindy. Valid options:
As @TimoGlastra mentioned in #53 "Currently all connections use the first key with the first service so it should work fine, but i think it would be better to look for the IndyAgent service." And also suggested this link https://github.com/decentralized-identity/did-common-typescript/blob/master/lib/DidDocument.ts as one of the possible solutions.
We can take it as an inspiration, or maybe, add the whole package as a dependency. Although, I'm not sure about the work needed for the persistence of such an object with our current storage mechanism
Re-export indy-sdk
type or, better, move indy-sdk
types into https://github.com/DefinitelyTyped/DefinitelyTyped project. I've tried it to move it into DefinitelyTyped, but our current format is not compatible with its linter.
The current solution caused me an issue when I tried to use the framework library within other TS project, as a workaround I created https://github.com/jakubkoci/types-indy-sdk which I'm adding to the project as dependency, but this should be just temporary solution.
As a developer I sometimes want to apply custom logic for all incoming messages. We already have handlers that allow to implement logic for a custom message type. I think it would be beneficial to introduce the concept of middleware into aries-framework-javascript.
aries-framework-dotnet also implements the concept of middleware which we can take as an example
AgentMiddleware
interfaceDispatcher
would be the best place to call middlewareAgent
(likeregisterHandlers
)
Probably a lot more, but this are two I can think of at the moment.
Basically the same as #60, but for credentials.
Checklist:
It's great to see a javascript implementation that can be targeted for making an Edge Agent. I've been trying to include this in a react-native project but I reckon it's not possible to use indy-sdk
on react-native because:
A better alternative might be to interface the API offered by indy-sdk
and have 2 implementations for it - one provided by indy-sdk
itself and the other can be a react-native module that uses the Java/iOS wrappers internally.
If this feels like a welcome addition then I can volunteer to work on the Java implementation of the react-native module that adheres to the indy-sdk
API ;) Thoughts?
Current thoughts on implementing mediation for Edge Agents into Aries Framework JavaScript. I think in the beginning especially the Edge Agent role would be nice to implement, but eventually implementing the Mediator role would also be nice. This approach allows for interoperable mediaton with other agents that support the protocols mentioned in this document. Still some unresolved questions at the end. Would appreciate some other thoughts on this.
The Mediator creates a Connection Invitation
and sends it to the Edge Agent. If the Edge Agent wishes to connect to the Mediator it should respond with a Connection Request
. Because the Edge Agent has no endpoint to include in the Connection Request
, an endpoint URI of didcomm:transport/queue
will indicate to the Mediator that the Edge Agent does not have a direct endpoint and messages should be queued.
The Connection Request
sent by the Edge Agent should include the Transport Return Route Decorator to allow the Mediator to directly respond with a Connection Response
message.
The Edge Agent and Mediator now have a connection. The Mediator will queue all messages it wants to send to the Edge Agent due to the didcomm:transport/queue
endpoint.
The Mediator and Edge Agent now have a connection, but it is not clear yet whether the Mediator would like to mediate for the Edge Agent.
Using the Mediator Coordination Protocol the Edge Agent can send a Mediation Request
to the Mediator. If the Mediator wishes to accept it will respond with a Mediation Grant
message. The Mediation Grant
will include the endpoint
and routing_keys
properties. The Edge Agent should store these values as they will be used when creating new connections with other agents.
Now that the Edge Agent made an agreement with the Mediator to act as an endpoint to receive messages, the Edge Agent can create connections with other agents using this endpoint.
It is not important whether the Edge Agent takes the role of Inviter or Invitee in the connection process. It is however important that before sending a message to the other agent the key used for this message is known by the Mediator. The Edge Agent should send a Keylist Update
message to the Mediator containing the key that will be used. The Mediator now knows that whenever a Forward
message is received for this key, it is intended for the Edge Agent.
After the key is registered the connection flow can continue like normal. The Edge Agent can use the endpoint
and routing_keys
from the Requesting Mediation step when exchanging DID Docs.
As the Edge Agent has no endpoint the Mediator can't directly forward the messages to the Edge Agent when it receives them. The Creating A Connection with the Mediator step allowed the Mediator to send a message to the Edge Agent using return routing. This is the implicit way for the Edge Agent to retrieve Messages. The explicit way is by using the Pickup Protocol
Messages can be retrieved by the Edge Agent implicitly by connecting using the Return Routing Protocol. It is recommended that a WebSocket transport be used between the Edge Agent and the Mediator for efficient message passing, and responsive message delivery when the Edge Agent app is open.
When WebSocket transport is not available and HTTP is used, return routing can only include one message in the HTTP response for each message the Edge Agent sends to the Mediator. This is where the Pickup Protocol can be used. Return routing will still be used, but by sending a Batch Pickup
message as Edge Agent the Mediator can respond with a single Batch
message containing multiple messages.
~transport
decorator without including a message @type
. If this is the case we could maybe use Noop message from the Pickup Protocoladd-device-info
message especially useful for push notifications. This is not possible with this approach. Is this desired?basic-routing
protocol that achieves approximately the same. Main differences are:
didcomm:transport/queue
endpoint. The Route Coordination Protocol got renamed to Mediator Coordination Protocol and got some small updates. With some adjustments Aries Framework Go could be used as a Mediator for Aries Framework JavaScript Edge AgentsFor the sake of consistency, the same as with #115
As a user of the framework I expect the API's to be consistent. The processXXX
methods in ConnectionService all return the connection afterwards. However the processXXX
methods in CredentialService return nothing.
This is inconsistent and I think we should pick either one.
My preference would go to returning the credential object, as we already have it. But open to either.
It would be also nice to define some structure or rules for error handling answering questions:
Currently the package is not published to NPM. For users to be able to easily use the framework they should be able to download it from NPM.
Questions:
Currently the framework only supports invitations that directly include the serviceEndpoint
, recipientKeys
and routingKeys
. However the connection protocol RFC states that an invitation can use use a publicly resolvable DID. With the new ledger service from #82 we can now resolve public Indy / Sov DIDs.
Indy doesn't support DID docs yet, but we can retrieve the service information using an ATTRIB request I think? This information can be used to respond to the invitation.
The code now refers to mediator agents as agencies. Although this is a common term used to describe mediators, especially when used for mobile routing, it is not the 'official' term.
We should change all occurrences of agency with mediator.
See: https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0046-mediators-and-relays
When updating tests to work with refactored code I had a hard time tracking what was going on due to all the console.logs
in the test file.
They do add a lot of useful information, but when debugging they add a lot of overhead. Also the await-poll
library has the console logging built in unable to turn this off.
My suggestion is to remove all console.logs
as they are printend on top of all tests anyway, losing track of which log belongs to which test case. I don't have a proper alternative at the moment sadly. maybe we could use the internal logger to make it optional?
package.json says MIT, but Apache-2 is checked into source.
Interested to understand what the contributing guidelines are for this repo / project / what legal documents apply to aries framework javascript.
Currently when receiving a trust ping or ack message we check whether the connection state is not yet completed. If this is the case we set the connection state to Complete.
However per the connection RFC on connection acknowledgement: "As any message will confirm the connection, any message will do."
So we need to move this implementation to a place where it will be run for every incoming message. I think the best way would be to allow for agent middleware, see #97 . After this is implemented we could address this issue.
// TODO: This is better addressed in a middleware of some kind because
// any message can transition the state to complete, not just an ack or trust ping
if (connection.state == ConnectionState.Responded && connection.role === ConnectionRole.Inviter) {
await this.connectionService.updateState(connection, ConnectionState.Complete);
}
It would be better to collocate handlers by protocol as mentioned in #17.
I'm still not sure whether to keep them in handlers
folder and group by "protocol" folders or put them directly into protocols
to given protocol. I tried to keep minimal dependencies between protocols and the second option would make it worse.
When a message from another agent is received it is transformed from JSON into a class instance of that message. Most agent message classes already contain decorators from the class-validator
package that specify how we want to validate properties. For example the ConnectionInvitationMessage
contains decorators to validate.
The Dispatcher class transforms the JSON of a message to a class instance. After the transformation we should use the validate
function of the class-validator
package to check if the message is valid.
In the future we can send a problem report message from the problem report protocol (#58) if the message is not valid according to the validation (which should be based on the requirements of the RFCs)
class-transormer
package after transformation from JSONResources
All records now use custom serialize functions that don't integrate well with classes decorated using class-transformer
. We should also transform record classes using class transformer to make working with didcomm messages inside records easier
As a developer I want to be able to transform classes to their respective json representation from outside the framework.
We think adding a toJSON()
method to all classes that can be transformed from and to JSON would be a convenient way for framework users to do this. As an added benefit, JSON.stringify
checks if the class has a toJSON()
method and will use this if present. This means classes could directly be passed to JSON.stringify()
.
As there are multiple types of classes that would need the toJSON
method, we need to find a goody way to do this without too much duplication and effort.
Types of classes:
toJSON
method)There are four approaches I can think of at the moment.
We'll create a base class that all other classes would extend from.
abstract class Transformable {
toJSON() {
return JsonTransformer.toJSON(this);
}
}
class MyClass extends Transformable {}
Pros:
toJSON
onceCons:
Another approach that deviates a bit from the above method is defining multiple base classes for each type of class. E.g. BaseRecord
will contain toJSON
, AgentMessage
will contain toJSON
, etc...
Pros:
Cons:
BaseRecord
and AgentMessage
both have a base class that we can add the toJSON
method to, for the other classes we'll still end up with the generic base classTransformable
mixinJust like we do with the agent message decorators, we could use mixins to make a class Transformable
.
Pros:
Cons:
Create an interface that defines the toJSON
method.
interface Transformable {
toJSON(): Record<string, unknown>
}
class MyClass implements Transformable {
toJSON() {
return JsonTransformer.toJSON(this);
}
}
Pros:
Cons:
No interface, just add a toJSON
method to all classes that need it. This is the current approach
Pros:
Cons:
Checklist:
Currently getCredentialDefinition
and getSchema
from LedgerService
return an array with [id, object]
(e.g. [schemaId, schema]
) however as you need to pass the id of the cred def / schema it doesn't make sense to also return the id.
Add mediator coordination protocol as mediator role.
Resources:
The goal of this issue is to discuss the design of a high-level framework API consumed by the developer using this library.
As I've been thinking about the framework architecture I see it consisting of the following parts/areas:
acceptInvitation
, provision
, downloadMessages
, sendMessageToConnection
, creating credential, ...)The idea is to be able to add/remove protocols which developer want to use without changing the core framework part (open-close principle)
...
...
Framework functionality should be provided via an instance of Agent
class:
const agent = new Agent(...)
When a developer creates such instance, then she can access modules. Modules works like facade around similar features of the framework:
agent.connections.createConnection()
agent.connections.acceptInvitation(...)
agent.routing.provision(...)
agent.routing.downloadMessages(...)
...
A module can also provide an event emitter:
agent.connections.events.on('newState', connection => { ... })
Other parts of the framework should be hidden and ideally not possible to export and use outside.
NOTE: This description is still in progress, I'll put some updates soon.
As a developer, I want to have an option to create a connection by accepting an invitation but wait with connection establishment until some form of confirmation happens to preview connection for approval by a user for example.
Currently, calling agent.acceptInvitation(invitation)
creates connection and automatically starts DID exchange process responding with ConnectionRequest
and accepting consequently incoming ConnectionResponse
message until the connection is established.
autoAcceptConnection
) that states whether the connection should automatically be accepted. This can be passed as option when creating / receiving invitation, but also modified whenever you want.In the future this will work the same for issue-cred / present-proof
Specific connection config overrides global config. If not set on connection, global will be used.
Checklist
We currently use Azure pipelines for CI. This works pretty well, but moving to GitHub actions would allow for more control of the CI process.
MessageReceiver assumes that only one connection would exist for a given recipient verkey which is not necessarily true if the agent is using a public DID.
An alternative would be to use the sender and recipient verkey together to find a connection uniquely?
Hello,
I understand that this project is in very early stages compared to aries dotnet framework and other aries projects. However, I am curious to know if there are there any plans with this project to make it compatible with frontend frameworks like React, Angular or Vue? and if there is any timeline to add credentials support in terms of validating proofs and revocation.
Thanks
Currently the framework connects to the indy pool using the agent.ledger.connect(poolName, poolConfig)
function outside of the framework.
I think it would be convenient to allow to set the genesis path using the agent config. This is also present in framework .NET and would make the setup of the framework easier.
const agentConfig = {
// ... rest of agent config
genesisPath: process.env.GENESIS_TXN_PATH
};
If the genesisPath
is present in the agent config, it will be used to automatically connect to the ledger when needed. If not set, the framework consumer can get more control by using the already present agent.ledger.connect(poolName, poolConfig)
. This will make it easier to use the framework, while still allowing to control over ledger setup.
We could also allow to pass genesisUrl
to automatically download the genesis.
Current logger can only log messages and json. However to allow framework users to gain insights in the activity of the framework at different levels of detail we should have multiple log levels.
I think something like:
Questions:
Winston seems like a good fit. It has a lot of customisability and is very mature.
We should at least abstract the logger away in a Logger class that doesn't expose the logging library used.
Currently when using aries-framework-javascript as a mediator the messages are still send to the outbound transporter. In the example mediator this is a storage outbound transporter that stores the message in memory.
When adding the transport context into the InboundMessageContext
per #81, we can check whether we can send a message to a connection, and store it otherwise.
Refer #29
We already have a MessageTransformer
class that transforms AgentMessages
(DIDComm messages) from and to JSON. However now that we're also (going to) using class-transformer for other purposes (records, diddocs, ...) we should have a general JsonTransformer
class that transforms from and to JSON.
This way we can avoid direct usage of classToPlain
and plainToClass
in the codebase. Especially now that we're going to use some custom settings when calling these functions.
Currently the agent can only be initialized with a single inbound and a single outbound transporter. Related to #81 to know which transport to use for outbound messages.
As a Developer, I want to have information about the transport layer to dispatch the outbound wire message properly. For example, if an inbound wire message was sent via WebSocket I need that information together with Socket ID and other details to send the outbound message by emitting event to the same WebSocket.
Then I could implement OutboundTransporter
interface in a similar way (take the example only for demonstration purposes):
class WebSocketOutboundTransporter implements OutboundTransporter {
...
async sendMessage(outboundPackage: OutboundPackage) {
const { socketId } = outboundPackage.transport
const socket = socketById(socketId);
socket.emit('ariesMessage', outboundPackage.wireMessage);
}
...
}
Ideally, the framework should not contain any mention about the transport layer, I mean, we should just past Transport
(or TransportContext
) object from MessageReceiver
through the framework to MessageSender
without revealing or processing information inside.
As discussed previously in one of our community calls, I propose to migrate the current source code structure to NestJs based code organization structure in order to have a convention based approach to the architecture. Also it will help new contributors to follow well-defined coding patterns.
To test interoperability with other agents we should create a backchannel for Aries Agent Test Harness.
Checklist:
Resources:
Module SignatureDecoratorUtils
is using base64url package which apparently doesn't work on React Native. There is an issue about that in package repo brianloveswords/base64url#50.
I looked into the code and the problem is probably dependency on Buffer
which is a globally available object in Node.js. I see two solutions:
In both cases, I would suggest introducing a class JsonEncoder
which would hide implementation details and provide the same API across the framework independently on a particular package.
Class BaseRecord
calls this.createdAt = Date.now();
in constructor
and that updates the attribute when we call fromPersistence
.
Per the connection response message in the connection RFC:
The signature data must be used to verify against the invitation's recipientKeys for continuity.
This means we should check if the connection~sig
field is signed with the same (one of?) the key(s) as the recipientKeys
from the invitation.
As we discussed on the call:
theirDidDoc
until the signed connection response is verified against recipientKeys
from the invitation . Until then use the recipientKeys
from the invitation thats stored with the connection record. (#98)connection~sig
from the connection response matches recipientKeys
from the invitation (#98)Currently when a connection invitation is received in the InvitationHandler
the key of the did used for that connection is always registered with the mediator. This should only happen in the case a mediator is used.
This is already correctly handled when creating a connection invitation (instead of receiving one): https://github.com/hyperledger/aries-framework-javascript/blob/master/src/lib/modules/ConnectionsModule.ts#L36-L40
But this is not handled correctly here:
https://github.com/hyperledger/aries-framework-javascript/blob/master/src/lib/handlers/connections/InvitationHandler.ts#L22-L23
rn-indy-sdk is an implementation of the indy-sdk that matches the API of the NodeJS Wrapper.
We should add support and documentation to use Aries Framework JavaScript in a React Native application.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.