outofband

package
v0.0.0-...-57c6170 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2023 License: Apache-2.0 Imports: 14 Imported by: 0

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

Examples

Constants

View Source
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 Action

type Action outofband.Action

Action contains helpful information about action.

type Client

type Client struct {
	service.Event
	// contains filtered or unexported fields
}

Client for the Out-Of-Band protocol: https://github.com/hyperledger/aries-rfcs/blob/master/features/0434-outofband/README.md

func New

func New(p Provider) (*Client, error)

New returns a new Client for the Out-Of-Band protocol.

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

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

func (c *Client) ActionStop(piID string, err error) error

ActionStop stops the protocol after an action event was triggered.

func (*Client) Actions

func (c *Client) Actions() ([]Action, error)

Actions returns unfinished actions for the async usage.

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

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

func (e *EventOptions) MyLabel() string

MyLabel will be shared with the other agent during the subsequent did-exchange.

func (*EventOptions) ReuseAnyConnection

func (e *EventOptions) ReuseAnyConnection() bool

ReuseAnyConnection signals whether to use any recognized DID in the services array for a reusable connection.

func (*EventOptions) ReuseConnection

func (e *EventOptions) ReuseConnection() string

ReuseConnection returns the DID to be used when reusing a connection.

func (*EventOptions) RouterConnections

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

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

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

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

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

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

func WithRouterConnections(conn ...string) MessageOption

WithRouterConnections allows you to specify the router connections.

type OobService

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.

type Provider

type Provider interface {
	ServiceEndpoint() string
	Service(id string) (interface{}, error)
	KMS() kms.KeyManager
	KeyType() kms.KeyType
	KeyAgreementType() kms.KeyType
	MediaTypeProfiles() []string
}

Provider provides the dependencies for the client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL