Skip to content

Commit ab8f184

Browse files
committed
Start implementing optional interactions feature.
1 parent e86cef1 commit ab8f184

File tree

6 files changed

+140
-4
lines changed

6 files changed

+140
-4
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# bedrock-profile-http ChangeLog
22

3+
## 23.1.0 - 2024-mm-dd
4+
5+
### Added
6+
- Add optional `interactions` feature, allowing an account to create
7+
VC exchanges based on predefined workflows. Currently, these
8+
exchanges do not store any VCs that might be received in any
9+
particular profile's storage, however, this might change in the
10+
future.
11+
312
## 23.0.0 - 2023-10-12
413

514
### Changed

lib/config.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
2+
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
33
*/
44
import * as bedrock from '@bedrock/core';
55

@@ -47,3 +47,20 @@ const meterServiceName = `${namespace}.meterService`;
4747
cc(`${meterServiceName}.url`, () => `${bedrock.config.server.baseUri}/meters`);
4848
// ensure meter service config is overridden in deployments
4949
config.ensureConfigOverride.fields.push(meterServiceName);
50+
51+
// optional interaction config
52+
config.interactions = {
53+
enabled: false,
54+
// optional named workflows for interactions
55+
/* Spec:
56+
{
57+
<workflow name>: {
58+
...,
59+
zcaps: {
60+
createExchange: <zcap for creating an exchange>
61+
}
62+
}
63+
}
64+
*/
65+
workflows: {}
66+
};

lib/http.js

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
2+
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
33
*/
44
import * as bedrock from '@bedrock/core';
55
import * as schemas from '../schemas/bedrock-profile-http.js';
@@ -17,6 +17,7 @@ const {config, util: {BedrockError}} = bedrock;
1717
let APP_ID;
1818
let EDV_METER_CREATION_ZCAP;
1919
let WEBKMS_METER_CREATION_ZCAP;
20+
let WORKFLOWS_MAP;
2021
let ZCAP_CLIENT;
2122

2223
bedrock.events.on('bedrock.init', () => {
@@ -31,13 +32,37 @@ bedrock.events.on('bedrock.init', () => {
3132
});
3233

3334
const cfg = bedrock.config['profile-http'];
35+
3436
const {edvMeterCreationZcap, webKmsMeterCreationZcap} = cfg;
3537
if(edvMeterCreationZcap) {
3638
EDV_METER_CREATION_ZCAP = JSON.parse(edvMeterCreationZcap);
3739
}
3840
if(webKmsMeterCreationZcap) {
3941
WEBKMS_METER_CREATION_ZCAP = JSON.parse(webKmsMeterCreationZcap);
4042
}
43+
44+
// parse workflow configs when interactions are enabled
45+
const {interactions} = cfg;
46+
if(interactions?.enabled) {
47+
const {workflows = {}} = interactions;
48+
WORKFLOWS_MAP = new Map();
49+
for(const workflowName in workflows) {
50+
const workflow = {
51+
name: workflowName,
52+
zcaps: new Map()
53+
};
54+
const {zcaps} = workflows[workflowName];
55+
for(const zcapName in zcaps) {
56+
const zcap = zcaps[zcapName];
57+
workflow.zcaps.set(zcapName, JSON.parse(zcap));
58+
}
59+
if(!workflow.zcaps.has('createExchange')) {
60+
throw new TypeError(
61+
'"bedrock.config.profile-http.interactions.workflows" must each ' +
62+
'have "zcaps.createExchange".');
63+
}
64+
}
65+
}
4166
});
4267

4368
bedrock.events.on('bedrock-express.configure.routes', app => {
@@ -47,6 +72,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
4772
const profileAgentsPath = '/profile-agents';
4873
const profileAgentPath = `${profileAgentsPath}/:profileAgentId`;
4974
const routes = {
75+
interactions: `/interactions`,
5076
profiles: basePath,
5177
profileAgents: `${profileAgentsPath}`,
5278
profileAgent: `${profileAgentPath}`,
@@ -402,6 +428,52 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
402428

403429
res.status(204).end();
404430
}));
431+
432+
// optional interactions feature
433+
const {interactions} = cfg;
434+
if(interactions?.enabled) {
435+
// create an interaction to exchange VCs
436+
app.post(
437+
routes.interactions,
438+
ensureAuthenticated,
439+
validate({bodySchema: schemas.createInteraction}),
440+
asyncHandler(async (req, res) => {
441+
const {id: accountId} = req.user.account || {};
442+
const {workflowName, exchange: {variables}} = req.body;
443+
444+
const workflow = WORKFLOWS_MAP.get(workflowName);
445+
if(!workflow) {
446+
throw new BedrockError(`Workflow "${workflowName}" not found.`, {
447+
name: 'NotFoundError',
448+
details: {
449+
httpStatusCode: 404,
450+
public: true
451+
}
452+
});
453+
}
454+
455+
// create exchange with given variables
456+
const exchange = {
457+
// 15 minute expiry in seconds
458+
ttl: 60 * 15,
459+
// template variables
460+
variables: {
461+
...variables,
462+
accountId
463+
}
464+
};
465+
const capability = workflow.zcaps.get('createExchange');
466+
const response = await ZCAP_CLIENT.write({json: exchange, capability});
467+
const exchangeId = response.headers.get('location');
468+
// reuse `localExchangeId` as interaction ID
469+
const localExchangeId = exchangeId.slice(exchangeId.lastIndexOf('/'));
470+
const id = `${config.server.baseUri}/${routes.interactions}` +
471+
localExchangeId;
472+
res.json({id, exchangeId});
473+
}));
474+
475+
// FIXME: implement GET `/interactions/<localExchangeId>`
476+
}
405477
});
406478

407479
// return select properties in the profileAgent record which does NOT include

schemas/bedrock-profile-http.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
2+
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
33
*/
44
const account = {
55
title: 'Account',
@@ -162,10 +162,47 @@ const delegateCapability = {
162162
}
163163
};
164164

165+
const createInteraction = {
166+
title: 'Create Interaction',
167+
type: 'object',
168+
required: ['workflowId', 'exchange'],
169+
additionalProperties: false,
170+
properties: {
171+
workflowName: {
172+
type: 'string'
173+
},
174+
exchange: {
175+
type: 'object',
176+
required: ['variables'],
177+
variables: {
178+
type: 'object',
179+
additionalProperties: false,
180+
oneOf: [{
181+
required: ['verifiablePresentation']
182+
}, {
183+
required: ['verifiablePresentationRequest']
184+
}],
185+
properties: {
186+
allowUnprotectedPresentation: {
187+
type: 'boolean'
188+
},
189+
verifiablePresentation: {
190+
type: 'object'
191+
},
192+
verifiablePresentationRequest: {
193+
type: 'object'
194+
}
195+
}
196+
}
197+
}
198+
}
199+
};
200+
165201
export {
166202
profileAgent,
167203
profileAgents,
168204
accountQuery,
169205
delegateCapability,
206+
createInteraction,
170207
zcaps
171208
};

test/mocha/10-api.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
2+
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
33
*/
44
import * as helpers from './helpers.js';
55
import {config} from '@bedrock/core';

test/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@bedrock/ssm-mongodb": "^10.1.0",
3737
"@bedrock/test": "^8.0.5",
3838
"@bedrock/validation": "^7.1.0",
39+
"@bedrock/vc-verifier": "digitalbazaar/bedrock-vc-verifier#update-vc-2.0",
3940
"@bedrock/veres-one-context": "^15.0.0",
4041
"@bedrock/zcap-storage": "^8.0.0",
4142
"@digitalbazaar/zcap": "^9.0.0",

0 commit comments

Comments
 (0)