Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: didweb #17

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
DID_WEB_SEED=qxerQNrTbyrrNQeAhwhJuOLfYIvTSRyG
WALLET_KEY=EAyhwhZJmdzHenyDnDCFBfFxPyc9F4zXsTwzWueHHjgH
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_HOST=
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
uses: ./.github/actions/deploy
env:
WALLET_KEY: ${{ secrets.WALLET_KEY }}
DID_WEB_SEED: ${{ secrets.DID_WEB_SEED}}
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ The `POSTGRES_` variables won't be used in development mode (`NODE_ENV=developme
| `AGENT_NAME` | The name of the agent. This will be used in invitations and will be publicly advertised. |
| `AGENT_PORT` | The port that is exposed for incoming traffic. Both the HTTP and WS inbound transport handlers are exposes on this port, and HTTP traffic will be upgraded to the WebSocket server when applicable. |
| `WALLET_NAME` | The name of the wallet to use. |
| `WALLET_KEY` | The key to unlock the wallet. |
| `WALLET_KEY` | The raw wallet key to unlock the wallet. |
| `DID_WEB_SEED` | The seed to deterministically generate the did web. |
| `INVITATION_URL` | Optional URL that can be used as the base for the invitation url. This would allow you to render a certain web page that can extract the invitation form the `oob` parameter, and show the QR code, or show useful information to the end-user. Less applicable to mediator URLs. |
| `POSTGRES_HOST` | Host of the database to use. Should include both host and port. |
| `POSTGRES_USER` | The postgres user. |
Expand Down
1 change: 1 addition & 0 deletions docker-compose-animo-mediator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ services:
AGENT_NAME: Animo Mediator
WALLET_NAME: animo-mediator
WALLET_KEY: ${WALLET_KEY}
DID_WEB_SEED: ${DID_WEB_SEED}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_HOST: ${POSTGRES_HOST}
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ services:
AGENT_NAME: Mediator
WALLET_NAME: mediator
WALLET_KEY: ${WALLET_KEY}
DID_WEB_SEED: ${DID_WEB_SEED}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_HOST: ${POSTGRES_HOST}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@types/node-fetch": "^2.6.4",
"dotenv": "^16.0.1",
"jest": "^29.2.2",
"ngrok": "^4.3.1",
"ngrok": "^5.0.0-beta.2",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"typescript": "^4.8.4"
Expand Down
98 changes: 92 additions & 6 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@ import { AskarModule, AskarMultiWalletDatabaseScheme } from '@aries-framework/as
import {
Agent,
CacheModule,
ConnectionEventTypes,
ConnectionState,
ConnectionStateChangedEvent,
ConnectionsModule,
DidCommMimeType,
DidCommV1Service,
DidDocument,
DidExchangeState,
HttpOutboundTransport,
InMemoryLruCache,
KeyDerivationMethod,
KeyType,
MediatorModule,
OutOfBandRole,
OutOfBandState,
TypedArrayEncoder,
WalletConfig,
WsOutboundTransport,
getEd25519VerificationKey2018,
} from '@aries-framework/core'
import { HttpInboundTransport, WsInboundTransport, agentDependencies } from '@aries-framework/node'
import { ariesAskar } from '@hyperledger/aries-askar-nodejs'
Expand All @@ -19,7 +29,16 @@ import type { Socket } from 'net'
import express from 'express'
import { Server } from 'ws'

import { AGENT_ENDPOINTS, AGENT_NAME, AGENT_PORT, LOG_LEVEL, POSTGRES_HOST, WALLET_KEY, WALLET_NAME } from './constants'
import {
AGENT_ENDPOINTS,
AGENT_NAME,
AGENT_PORT,
DID_WEB_SEED,
LOG_LEVEL,
POSTGRES_HOST,
WALLET_KEY,
WALLET_NAME,
} from './constants'
import { askarPostgresConfig } from './database'
import { Logger } from './logger'
import { StorageMessageQueueModule } from './storage/StorageMessageQueueModule'
Expand All @@ -45,6 +64,57 @@ function createModules() {
return modules
}

async function createAndImportDid(agent: MediatorAgent) {
const httpEndpoint = agent.config.endpoints.find((e) => e.startsWith('http')) as string
const wsEndpoint = agent.config.endpoints.find((e) => e.startsWith('ws')) as string
const domain = httpEndpoint.replace(/^https?:\/\//, '')
const did = `did:web:${domain}`

const createdDids = await agent.dids.getCreatedDids({ did })
if (createdDids.length > 0) {
const { did, didDocument } = createdDids[0]
return { did, didDocument }
}

// TODO: if there is a did:web in the wallet and the did was not found previously
// the url on which the did:web is hosted has probably changed.
const didWebs = await agent.dids.getCreatedDids({ method: 'web' })
if (didWebs.length > 0) {
// TODO: remake
await agent.wallet.delete()
await agent.shutdown()
await agent.initialize()
}

const privateKey = TypedArrayEncoder.fromString(DID_WEB_SEED)
const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519, privateKey })

const verificationMethod = getEd25519VerificationKey2018({ id: `${did}#${key.fingerprint}`, key, controller: did })

const didDocument = new DidDocument({
id: did,
context: ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'],
verificationMethod: [verificationMethod],
keyAgreement: [verificationMethod.id],
service: [
new DidCommV1Service({
id: `did:web:${domain}#animo-mediator`,
serviceEndpoint: wsEndpoint,
recipientKeys: [verificationMethod.id],
}),
],
})

await agent.dids.import({
did,
didDocument,
overwrite: true,
privateKeys: [{ keyType: KeyType.Ed25519, privateKey }],
})

return { did, didDocument }
}

export async function createAgent() {
// We create our own instance of express here. This is not required
// but allows use to use the same server (and port) for both WebSockets and HTTP
Expand All @@ -60,6 +130,7 @@ export async function createAgent() {
id: WALLET_NAME,
key: WALLET_KEY,
storage: storageConfig,
keyDerivationMethod: KeyDerivationMethod.Raw,
}

if (storageConfig) {
Expand All @@ -86,9 +157,7 @@ export async function createAgent() {
didCommMimeType: DidCommMimeType.V0,
},
dependencies: agentDependencies,
modules: {
...createModules(),
},
modules: createModules(),
})

// Create all transports
Expand All @@ -103,6 +172,20 @@ export async function createAgent() {
agent.registerInboundTransport(wsInboundTransport)
agent.registerOutboundTransport(wsOutboundTransport)

await agent.initialize()

agent.events.on<ConnectionStateChangedEvent>(ConnectionEventTypes.ConnectionStateChanged, async (event) => {
const { connectionRecord } = event.payload
if (connectionRecord.state !== DidExchangeState.RequestReceived) return

agent.config.logger.info(
`Accepting connection request for connection '${connectionRecord.id}' for '${connectionRecord.theirLabel}'`
)
await agent.connections.acceptRequest(connectionRecord.id)
})

const { didDocument } = await createAndImportDid(agent)

// eslint-disable-next-line @typescript-eslint/no-misused-promises
httpInboundTransport.app.get('/invite', async (req, res) => {
if (!req.query._oobid || typeof req.query._oobid !== 'string') {
Expand All @@ -121,7 +204,10 @@ export async function createAgent() {
return res.send(outOfBandRecord.outOfBandInvitation.toJSON())
})

await agent.initialize()
// eslint-disable-next-line @typescript-eslint/no-misused-promises
httpInboundTransport.app.get('/.well-known/did.json', async (_req, res) => {
return res.send(didDocument)
})

// When an 'upgrade' to WS is made on our http server, we forward the
// request to the WS server
Expand All @@ -134,4 +220,4 @@ export async function createAgent() {
return agent
}

export type MediatorAgent = ReturnType<typeof createAgent>
export type MediatorAgent = Awaited<ReturnType<typeof createAgent>>
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { LogLevel } from '@aries-framework/core'
export const AGENT_PORT = process.env.AGENT_PORT ? Number(process.env.AGENT_PORT) : 3000
export const AGENT_NAME = process.env.AGENT_NAME || 'Animo Mediator'
export const WALLET_NAME = process.env.WALLET_NAME || 'animo-mediator-dev'
export const WALLET_KEY = process.env.WALLET_KEY || 'animo-mediator-dev'
export const WALLET_KEY = process.env.WALLET_KEY || 'EAyhwhZJmdzHenyDnDCFBfFxPyc9F4zXsTwzWueHHjgH'
export const AGENT_ENDPOINTS = process.env.AGENT_ENDPOINTS?.split(',') ?? [
`http://localhost:${AGENT_PORT}`,
`ws://localhost:${AGENT_PORT}`,
]
export const DID_WEB_SEED = process.env.DID_WEB_SEED || 'qxerQNrTbyrrNQeAhwhJuOLfYIvTSRyG'

export const POSTGRES_HOST = process.env.POSTGRES_HOST
export const POSTGRES_USER = process.env.POSTGRES_USER
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ void createAgent().then(async (agent) => {
})

agent.config.logger.info(`Out of band invitation url: \n\n\t${mediatorInvitationUrlLong}`)
agent.config.logger.info(`Did Web Url '${httpEndpoint}/.well-known/did.json'`)
})
24 changes: 9 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1029,11 +1029,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.46.tgz#9f2102d0ba74a318fcbe170cbff5463f119eab59"
integrity sha512-Mnq3O9Xz52exs3mlxMcQuA7/9VFe/dXcrgAyfjLkABIqxXKOgBRjyazTxUbjsxDa4BP7hhPliyjVTP9RDP14xg==

"@types/node@^8.10.50":
version "8.10.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3"
integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==

"@types/qs@*":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
Expand Down Expand Up @@ -3202,17 +3197,16 @@ next-tick@^1.1.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==

ngrok@^4.3.1:
version "4.3.3"
resolved "https://registry.yarnpkg.com/ngrok/-/ngrok-4.3.3.tgz#c51a1c4af2271ac3c9092ede3b0975caf7833217"
integrity sha512-a2KApnkiG5urRxBPdDf76nNBQTnNNWXU0nXw0SsqsPI+Kmt2lGf9TdVYpYrHMnC+T9KhcNSWjCpWqBgC6QcFvw==
ngrok@^5.0.0-beta.2:
version "5.0.0-beta.2"
resolved "https://registry.yarnpkg.com/ngrok/-/ngrok-5.0.0-beta.2.tgz#7976690a592b27de9a91ff93892cfabda24360ad"
integrity sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ==
dependencies:
"@types/node" "^8.10.50"
extract-zip "^2.0.1"
got "^11.8.5"
lodash.clonedeep "^4.5.0"
uuid "^7.0.0 || ^8.0.0"
yaml "^1.10.0"
yaml "^2.2.2"
optionalDependencies:
hpagent "^0.1.2"

Expand Down Expand Up @@ -4184,10 +4178,10 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==

yaml@^1.10.0:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.2.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144"
integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==

yargs-parser@^21.0.1, yargs-parser@^21.1.1:
version "21.1.1"
Expand Down
Loading