Create a connection
This section assumes the following items:
A valid environment for development
basic knowledge of the required fields in the agent config
In this tutorial we will create a connection as Acme Corp with Bob. We will start with setting up both their agents with the minimal configuration required to follow this tutorial. After the initialization we will then create an invitation as Acme Corp and send it over to Bob. Bob will then accept this invitation and at that point they have established a connection and they know how to reach each other for sending a basic message, issuing a credential, verifying a proof, etc.
1. Setting up the agents
First for both agents we must setup and initialize an agent to work with. Depending on your target, React Native or Node.js, it might vary.
In this tutorial Bob will be in a React Native environment and Acme Corp in a Node.js environment.
Bob
For bob we need to setup a basic agent with a wallet, mediator and outbound transport.
const initializeBobAgent = async () => {
// Simple agent configuration. This sets some basic fields like the wallet
// configuration and the label. It also sets the mediator invitation url,
// because this is most likely required in a mobile environment.
const config: InitConfig = {
label: 'demo-agent-bob',
walletConfig: {
id: 'mainBob',
key: 'demoagentbob00000000000000000000',
},
autoAcceptConnections: true,
}
// A new instance of an agent is created here
const agent = new Agent({ config, dependencies: agentDependencies })
// Register a simple `WebSocket` outbound transport
agent.registerOutboundTransport(new WsOutboundTransport())
// Register a simple `Http` outbound transport
agent.registerOutboundTransport(new HttpOutboundTransport())
// Initialize the agent
await agent.initialize()
return agent
}
Acme
For Acme we need to setup a basic agent with a wallet, inbound and outbound transport.
const initializeAcmeAgent = async () => {
// Simple agent configuration. This sets some basic fields like the wallet
// configuration and the label.
const config: InitConfig = {
label: 'demo-agent-acme',
walletConfig: {
id: 'mainAcme',
key: 'demoagentacme0000000000000000000',
},
autoAcceptConnections: true,
endpoints: ['http://localhost:3001'],
}
// A new instance of an agent is created here
const agent = new Agent({ config, dependencies: agentDependencies })
// Register a simple `WebSocket` outbound transport
agent.registerOutboundTransport(new WsOutboundTransport())
// Register a simple `Http` outbound transport
agent.registerOutboundTransport(new HttpOutboundTransport())
// Register a simple `Http` inbound transport
agent.registerInboundTransport(new HttpInboundTransport({ port: 3001 }))
// Initialize the agent
await agent.initialize()
return agent
}
2. Creating an invitation
Now that we have setup both agents, we can create an invitation from Acme Corp.
- New
- Legacy
This method will create an invitation using the legacy method according to 0434: Out-of-Band Protocol 1.1.
const createNewInvitation = async (agent: Agent) => {
const outOfBandRecord = await agent.oob.createInvitation()
return {
invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.org' }),
outOfBandRecord,
}
}
This method will create an invitation using the legacy method according to 0160: Connection Protocol.
const createLegacyInvitation = async (agent: Agent) => {
const { invitation } = await agent.oob.createLegacyInvitation()
return invitation.toUrl({ domain: 'https://example.org' })
}
3. Receiving the invitation
After we have created the invitation we have to transmit it to the other
agent. Common practise, when sending it to a holder, it to embed the url inside
a QR code. This QR code can then be scanned by the holder, in this case Bob.
After this, because both have set autoAcceptConnections
to true
, the
connection is established.
const receiveInvitation = async (agent: Agent, invitationUrl: string) => {
const { outOfBandRecord } = await agent.oob.receiveInvitationFromUrl(invitationUrl)
return outOfBandRecord
}
4. (additional) listen to incoming connection responses
When you quickly want to use the event or the data of a response to a connection request, you can start an TODO: agent event listener.
Another use case for this would be to get the connectionRecord
of the
connection as it is only created when the invitation has been received by the
other agent. The connectionRecord
is very essential in processes like TODO:
issuing a credential or TODO: verifying a
proof.
The connectionRecord
can also be retrieved with
agent.connections.findAllByOutOfBandId(id)
, but with this method there is no
way of knowing if the invitation has been received.
const setupConnectionListener = (agent: Agent, outOfBandRecord: OutOfBandRecord, cb: (...args: any) => void) => {
agent.events.on<ConnectionStateChangedEvent>(ConnectionEventTypes.ConnectionStateChanged, ({ payload }) => {
if (payload.connectionRecord.outOfBandId !== outOfBandRecord.id) return
if (payload.connectionRecord.state === DidExchangeState.Completed) {
// the connection is now ready for usage in other protocols!
console.log(`Connection for out-of-band id ${outOfBandRecord.id} completed`)
// Custom business logic can be included here
// In this example we can send a basic message to the connection, but
// anything is possible
cb()
// We exit the flow
process.exit(0)
}
})
}
5. Full code snippets
Below are both code snippets for each agent. These can be used as base but
should be editted to fit your use case. The
walletConfig.key
must be changed as it can
lead to other people knowing your "password" to your wallet.
import {
Agent,
InitConfig,
ConnectionEventTypes,
ConnectionStateChangedEvent,
WsOutboundTransport,
HttpOutboundTransport,
DidExchangeState,
OutOfBandRecord,
} from '@aries-framework/core'
import { agentDependencies, HttpInboundTransport } from '@aries-framework/node'
const initializeBobAgent = async () => {
// Simple agent configuration. This sets some basic fields like the wallet
// configuration and the label. It also sets the mediator invitation url,
// because this is most likely required in a mobile environment.
const config: InitConfig = {
label: 'demo-agent-bob',
walletConfig: {
id: 'mainBob',
key: 'demoagentbob00000000000000000000',
},
autoAcceptConnections: true,
}
// A new instance of an agent is created here
const agent = new Agent({ config, dependencies: agentDependencies })
// Register a simple `WebSocket` outbound transport
agent.registerOutboundTransport(new WsOutboundTransport())
// Register a simple `Http` outbound transport
agent.registerOutboundTransport(new HttpOutboundTransport())
// Initialize the agent
await agent.initialize()
return agent
}
const initializeAcmeAgent = async () => {
// Simple agent configuration. This sets some basic fields like the wallet
// configuration and the label.
const config: InitConfig = {
label: 'demo-agent-acme',
walletConfig: {
id: 'mainAcme',
key: 'demoagentacme0000000000000000000',
},
autoAcceptConnections: true,
endpoints: ['http://localhost:3001'],
}
// A new instance of an agent is created here
const agent = new Agent({ config, dependencies: agentDependencies })
// Register a simple `WebSocket` outbound transport
agent.registerOutboundTransport(new WsOutboundTransport())
// Register a simple `Http` outbound transport
agent.registerOutboundTransport(new HttpOutboundTransport())
// Register a simple `Http` inbound transport
agent.registerInboundTransport(new HttpInboundTransport({ port: 3001 }))
// Initialize the agent
await agent.initialize()
return agent
}
const createNewInvitation = async (agent: Agent) => {
const outOfBandRecord = await agent.oob.createInvitation()
return {
invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.org' }),
outOfBandRecord,
}
}
const createLegacyInvitation = async (agent: Agent) => {
const { invitation } = await agent.oob.createLegacyInvitation()
return invitation.toUrl({ domain: 'https://example.org' })
}
const receiveInvitation = async (agent: Agent, invitationUrl: string) => {
const { outOfBandRecord } = await agent.oob.receiveInvitationFromUrl(invitationUrl)
return outOfBandRecord
}
const setupConnectionListener = (agent: Agent, outOfBandRecord: OutOfBandRecord, cb: (...args: any) => void) => {
agent.events.on<ConnectionStateChangedEvent>(ConnectionEventTypes.ConnectionStateChanged, ({ payload }) => {
if (payload.connectionRecord.outOfBandId !== outOfBandRecord.id) return
if (payload.connectionRecord.state === DidExchangeState.Completed) {
// the connection is now ready for usage in other protocols!
console.log(`Connection for out-of-band id ${outOfBandRecord.id} completed`)
// Custom business logic can be included here
// In this example we can send a basic message to the connection, but
// anything is possible
cb()
// We exit the flow
process.exit(0)
}
})
}
const run = async () => {
console.log('Initializing Bob agent...')
const bobAgent = await initializeBobAgent()
console.log('Initializing Acme agent...')
const acmeAgent = await initializeAcmeAgent()
console.log('Creating the invitation as Acme...')
const { outOfBandRecord, invitationUrl } = await createNewInvitation(acmeAgent)
console.log('Listening for connection changes...')
setupConnectionListener(acmeAgent, outOfBandRecord, () =>
console.log('We now have an active connection to use in the following tutorials')
)
console.log('Accepting the invitation as Bob...')
await receiveInvitation(bobAgent, invitationUrl)
}
export default run
void run()