Documentation
¶
Overview ¶
Package outofband provides support for the Out-of-Band protocols: https://github.com/hyperledger/aries-rfcs/blob/master/features/0434-outofband/README.md.
Create your client:
ctx := getFrameworkContext() client, err := outofband.New(ctx)
if err != nil {
panic(err)
}
You can create requests and invitations with client.CreateRequest() and client.CreateInvitation() respectively.
You can accept out-of-band requests and invitations received via out of band channels. If you have a request or an invitation on hand you can use the client.AcceptRequest() and client.AcceptInvitation() respectively. These return the ID of the newly-created connection record.
If you're expecting to receive out-of-band invitations or requests via a DIDComm channel then you should register to the action event stream and the state event stream:
events := make(chan service.DIDCommAction) err = client.RegisterActionEvent(events)
if err != nil {
panic(err)
}
states := make(chan service.StateMsg) err = client.RegisterMsgEvent(states)
if err != nil {
panic(err)
}
for {
select {
case event := <-events:
switch event.Message.Type() {
case outofband.RequestMsgType:
// inspect the request
req := &outofband.Invitation{}
err = event.Message.Decode(req)
if err != nil {
panic(err)
}
// accept the request:
event.Continue(&outofband.EventOptions{Label: "Bob"})
// OR you may reject this request:
// event.Stop(errors.New("rejected"))
case outofband.InvitationMsgType:
// inspect and handle the out-of-band invitation just like with the
// request message above
}
case state := <-states:
// the connection ID is delivered in a PostState
if state.Type == service.PostState {
props := state.Properties.(outofband.Event)
if props.Error() != nil {
panic(props.Error())
}
// the connection ID
connID := props.ConnectionID()
}
}
}
Note: the ouf-of-band protocol results in the execution of other protocols. You need to subscribe to the event and state streams of those protocols as well.
Index ¶
- Constants
- type Action
- type Client
- func (c *Client) AcceptInvitation(i *Invitation, myLabel string, opts ...MessageOption) (string, error)
- func (c *Client) ActionContinue(piID, label string, opts ...MessageOption) error
- func (c *Client) ActionStop(piID string, err error) error
- func (c *Client) Actions() ([]Action, error)
- func (c *Client) CreateInvitation(services []interface{}, opts ...MessageOption) (*Invitation, error)
- type Event
- type EventOptions
- type Invitation
- type MessageOption
- func ReuseAnyConnection() MessageOption
- func ReuseConnection(theirDID string) MessageOption
- func WithAccept(a ...string) MessageOption
- func WithAttachments(a ...*decorator.Attachment) MessageOption
- func WithGoal(goal, goalCode string) MessageOption
- func WithHandshakeProtocols(proto ...string) MessageOption
- func WithLabel(l string) MessageOption
- func WithRouterConnections(conn ...string) MessageOption
- type OobService
- type Provider
Examples ¶
Constants ¶
const ( // InvitationMsgType is the '@type' for the invitation message. InvitationMsgType = outofband.InvitationMsgType // HandshakeReuseMsgType is the '@type' for the handshake reuse message. HandshakeReuseMsgType = outofband.HandshakeReuseMsgType // HandshakeReuseAcceptedMsgType is the '@type' for the handshake reuse accepted message. HandshakeReuseAcceptedMsgType = outofband.HandshakeReuseAcceptedMsgType )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
Client for the Out-Of-Band protocol: https://github.com/hyperledger/aries-rfcs/blob/master/features/0434-outofband/README.md
func (*Client) AcceptInvitation ¶
func (c *Client) AcceptInvitation(i *Invitation, myLabel string, opts ...MessageOption) (string, error)
AcceptInvitation from another agent and return the ID of the new connection records.
Example ¶
Example of an edge agent accepting an out-of-band invitation from a router.
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/hyperledger/aries-framework-go/component/storageutil/mem"
"github.com/hyperledger/aries-framework-go/pkg/client/didexchange"
"github.com/hyperledger/aries-framework-go/pkg/client/mediator"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
didsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
routesvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband"
mockdidexchange "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/didexchange"
mockroute "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator"
mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
)
const (
// Router is a router that sends an out-of-band invitation to the edge agent.
Router = "Router"
// Bob is an edge agent.
Bob = "Bob"
)
var agentActions = make(map[string]chan service.DIDCommAction) //nolint:gochecknoglobals
// Example of an edge agent accepting an out-of-band invitation from a router.
func main() { //nolint:gocyclo,gocognit
// set up the router
routerCtx := getContext(Router)
router, err := New(routerCtx)
if err != nil {
panic(err)
}
routerDIDs, err := didexchange.New(routerCtx)
if err != nil {
panic(err)
}
routerClient, err := mediator.New(routerCtx)
if err != nil {
panic(err)
}
routerEvents := makeActionsChannel(Router)
err = routerDIDs.RegisterActionEvent(routerEvents)
if err != nil {
panic(err)
}
err = routerClient.RegisterActionEvent(routerEvents)
if err != nil {
panic(err)
}
// set up the edge agent
bobCtx := getContext(Bob)
bob, err := New(bobCtx)
if err != nil {
panic(err)
}
// router creates the route-request message
routeRequest, err := json.Marshal(mediator.NewRequest())
if err != nil {
panic(err)
}
// router creates outofband request and embeds the route-request message in it
inv, err := router.CreateInvitation(
nil,
WithLabel(Router),
WithAttachments(&decorator.Attachment{
ID: uuid.New().String(),
Data: decorator.AttachmentData{
Base64: base64.StdEncoding.EncodeToString(routeRequest),
},
}),
)
if err != nil {
panic(err)
}
fmt.Printf("%s creates an out-of-band invitation with an embedded route-request message\n", Router)
// the edge agent accepts the outofband invitation
bobConnID, err := bob.AcceptInvitation(inv, Bob)
if err != nil {
panic(err)
}
fmt.Printf(
"%s accepts the out-of-band invitation received via an out of band channel and created connectionID %s\n",
Bob, bobConnID)
done := make(chan struct{}) // ends this example
go func() {
for event := range routerEvents {
fmt.Printf("%s received %s from %s\n", Router, event.Message.Type(), Bob)
switch event.ProtocolName {
case didexchange.ProtocolName:
if event.Message.Type() == didexchange.RequestMsgType {
didExchangeRequest := &didsvc.Request{}
err = event.Message.Decode(didExchangeRequest)
if err != nil {
panic(err)
}
if didExchangeRequest.Label != Bob {
err = fmt.Errorf(
"%s expected a didexchange request from %s but got %s",
Router, Bob, didExchangeRequest.Label,
)
event.Stop(err)
panic(err)
}
props, ok := event.Properties.(didexchange.Event)
if !ok {
panic("failed to cast event properties (shouldn't happen)")
}
fmt.Printf("%s created connectionID %s\n", Router, props.ConnectionID())
event.Continue(nil)
}
case mediator.ProtocolName:
if event.Message.Type() == mediator.RequestMsgType {
event.Continue(nil)
done <- struct{}{}
}
}
}
}()
select {
case <-done:
case <-time.After(time.Second): // timeout varies in your environment
panic("timeout")
}
bobRoutes, err := mediator.New(bobCtx)
if err != nil {
panic(err)
}
config, err := bobRoutes.GetConfig("xyz")
if err != nil {
panic(err)
}
fmt.Printf(
"%s has registered a route on %s with routerEndpoint %s and routingKeys %+v\n",
Bob, Router, config.Endpoint(), config.Keys())
}
func getContext(agent string) *mockprovider.Provider {
return &mockprovider.Provider{
KMSValue: &mockkms.KeyManager{},
StorageProviderValue: mem.NewProvider(),
ProtocolStateStorageProviderValue: mem.NewProvider(),
ServiceMap: map[string]interface{}{
outofband.Name: &stubOOBService{
Event: nil,
acceptInvFunc: func(i *outofband.Invitation, options outofband.Options) (string, error) {
agentActions[i.Label] <- service.DIDCommAction{
ProtocolName: didsvc.DIDExchange,
Message: service.NewDIDCommMsgMap(&didsvc.Request{
Type: didsvc.RequestMsgType,
Label: agent,
}),
Continue: func(interface{}) {
agentActions[i.Label] <- service.DIDCommAction{
ProtocolName: mediator.ProtocolName,
Message: service.NewDIDCommMsgMap(mediator.NewRequest()),
Continue: func(interface{}) {},
}
},
Properties: &didexchangeEvent{connID: "xyz"},
}
return "xyz", nil
},
saveInvFunc: func(*outofband.Invitation) error { return nil },
},
didsvc.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
routesvc.Coordination: &mockroute.MockMediatorSvc{
RouterEndpoint: "http://routers-r-us.com",
RoutingKeys: []string{"key-1", "key-2"},
},
},
}
}
func makeActionsChannel(agent string) chan service.DIDCommAction {
c := make(chan service.DIDCommAction, 5)
agentActions[agent] = c
return c
}
type didexchangeEvent struct {
connID string
invID string
}
func (d *didexchangeEvent) ConnectionID() string {
return d.connID
}
func (d *didexchangeEvent) InvitationID() string {
return d.invID
}
func (d *didexchangeEvent) All() map[string]interface{} {
return map[string]interface{}{
"connectionID": d.ConnectionID(),
"invitationID": d.InvitationID(),
}
}
Output: Router creates an out-of-band invitation with an embedded route-request message Bob accepts the out-of-band invitation received via an out of band channel and created connectionID xyz Router received https://didcomm.org/didexchange/1.0/request from Bob Router created connectionID xyz Router received https://didcomm.org/coordinatemediation/1.0/mediate-request from Bob Bob has registered a route on Router with routerEndpoint http://routers-r-us.com and routingKeys [key-1 key-2]
func (*Client) ActionContinue ¶ added in v0.1.4
func (c *Client) ActionContinue(piID, label string, opts ...MessageOption) error
ActionContinue allows continuing with the protocol after an action event was triggered.
func (*Client) ActionStop ¶ added in v0.1.4
ActionStop stops the protocol after an action event was triggered.
func (*Client) CreateInvitation ¶
func (c *Client) CreateInvitation(services []interface{}, opts ...MessageOption) (*Invitation, error)
CreateInvitation creates and saves an out-of-band invitation. Services are required in the RFC, but optional in this implementation. If not provided, a default will be assigned. TODO HandShakeProtocols are optional in the RFC and as arguments to this function.
However, if not provided, a default will be assigned for you.
type Event ¶
type Event interface {
// ConnectionID of the connection record, once it's created.
// This becomes available in a post-state event unless an error condition is encountered.
ConnectionID() string
// Error is non-nil if an error is encountered.
Error() error
}
Event is a container of out-of-band protocol-specific properties for DIDCommActions and StateMsgs.
type EventOptions ¶ added in v0.1.4
type EventOptions struct {
// Label will be shared with the other agent during the subsequent did-exchange.
Label string
// Connections allows specifying router connections.
Connections []string
ReuseAny bool
ReuseDID string
}
EventOptions are is a container of options that you can pass to an event's Continue function to customize the reaction to incoming out-of-band messages.
func (*EventOptions) MyLabel ¶ added in v0.1.4
func (e *EventOptions) MyLabel() string
MyLabel will be shared with the other agent during the subsequent did-exchange.
func (*EventOptions) ReuseAnyConnection ¶ added in v0.1.7
func (e *EventOptions) ReuseAnyConnection() bool
ReuseAnyConnection signals whether to use any recognized DID in the services array for a reusable connection.
func (*EventOptions) ReuseConnection ¶ added in v0.1.7
func (e *EventOptions) ReuseConnection() string
ReuseConnection returns the DID to be used when reusing a connection.
func (*EventOptions) RouterConnections ¶ added in v0.1.5
func (e *EventOptions) RouterConnections() []string
RouterConnections return router connections.
type Invitation ¶
type Invitation outofband.Invitation
Invitation is this protocol's `invitation` message.
type MessageOption ¶
type MessageOption func(*message)
MessageOption allow you to customize the way out-of-band messages are built.
func ReuseAnyConnection ¶ added in v0.1.7
func ReuseAnyConnection() MessageOption
ReuseAnyConnection is used when accepting an invitation with either AcceptInvitation or ActionContinue. The `services` array will be scanned until it finds a recognized DID entry and send a `handshake-reuse` message to its did-communication service endpoint. Cannot be used together with ReuseConnection.
func ReuseConnection ¶ added in v0.1.7
func ReuseConnection(theirDID string) MessageOption
ReuseConnection is used when accepting an invitation with either AcceptInvitation or ActionContinue. 'did' must be an entry in the Invitation's 'services' array. A `handshake-reuse` message is sent to its did-communication service endpoint. Cannot be used together with ReuseAnyConnection.
func WithAccept ¶ added in v0.1.7
func WithAccept(a ...string) MessageOption
WithAccept will set the given media type profiles in the Invitation's `accept` property. Only valid values from RFC 0044 are supported.
func WithAttachments ¶ added in v0.1.7
func WithAttachments(a ...*decorator.Attachment) MessageOption
WithAttachments allows you to include attachments in the Invitation.
func WithGoal ¶
func WithGoal(goal, goalCode string) MessageOption
WithGoal allows you to specify the `goal` and `goalCode` for the message.
func WithHandshakeProtocols ¶ added in v0.1.7
func WithHandshakeProtocols(proto ...string) MessageOption
WithHandshakeProtocols allows you to customize the handshake_protocols to include in the Invitation.
func WithLabel ¶
func WithLabel(l string) MessageOption
WithLabel allows you to specify the label on the message.
func WithRouterConnections ¶ added in v0.1.5
func WithRouterConnections(conn ...string) MessageOption
WithRouterConnections allows you to specify the router connections.
type OobService ¶ added in v0.1.4
type OobService interface {
service.Event
AcceptInvitation(*outofband.Invitation, outofband.Options) (string, error)
SaveInvitation(*outofband.Invitation) error
Actions() ([]outofband.Action, error)
ActionContinue(string, outofband.Options) error
ActionStop(string, error) error
}
OobService defines the outofband service.