Skip to content

Commit 74069f7

Browse files
discovery-protocol handler (#294)
* init discovery-protocol handler
1 parent 812ce46 commit 74069f7

11 files changed

+689
-5
lines changed

.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = {
1010
...spellcheckerRule,
1111
cspell: {
1212
...cspellConfig,
13-
ignoreWords: ['unmarshal', 'JUvpllMEYUZ2joO59UNui_XYDqxVqiFLLAJ8klWuPBw', 'gdwj', 'fwor', 'multichain', "ETHWEI", "ETHGWEI"]
13+
ignoreWords: ['unmarshal', 'JUvpllMEYUZ2joO59UNui_XYDqxVqiFLLAJ8klWuPBw', 'gdwj', 'fwor', 'multichain', "ETHWEI", "ETHGWEI", "didcomm"]
1414
}
1515
}
1616
]

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@0xpolygonid/js-sdk",
3-
"version": "1.25.1",
3+
"version": "1.26.0",
44
"description": "SDK to work with Polygon ID",
55
"main": "dist/node/cjs/index.js",
66
"module": "dist/node/esm/index.js",

src/iden3comm/constants.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AcceptProfile } from './types';
22

33
const IDEN3_PROTOCOL = 'https://iden3-communication.io/';
4+
const DIDCOMM_PROTOCOL = 'https://didcomm.org/';
45
/**
56
* Constants for Iden3 protocol
67
*/
@@ -48,7 +49,13 @@ export const PROTOCOL_MESSAGE_TYPE = Object.freeze({
4849
// PaymentRequestMessageType is type for payment-request message
4950
PAYMENT_REQUEST_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment-request` as const,
5051
// PaymentMessageType is type for payment message
51-
PAYMENT_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment` as const
52+
PAYMENT_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment` as const,
53+
// DiscoveryProtocolQueriesMessageType is type for didcomm discovery protocol queries
54+
DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE:
55+
`${DIDCOMM_PROTOCOL}discover-features/2.0/queries` as const,
56+
// DiscoveryProtocolDiscloseMessageType is type for didcomm discovery protocol disclose
57+
DISCOVERY_PROTOCOL_DISCLOSE_MESSAGE_TYPE:
58+
`${DIDCOMM_PROTOCOL}discover-features/2.0/disclose` as const
5259
});
5360

5461
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import { PROTOCOL_MESSAGE_TYPE } from '../constants';
2+
3+
import { BasicMessage, IPackageManager, ProtocolMessage } from '../types';
4+
5+
import * as uuid from 'uuid';
6+
import {
7+
DiscoverFeatureDiscloseMessage,
8+
DiscoverFeatureDisclosure,
9+
DiscoverFeatureQueriesMessage,
10+
DiscoverFeatureQuery,
11+
DiscoverFeatureQueryType,
12+
DiscoveryProtocolFeatureType
13+
} from '../types/protocol/discovery-protocol';
14+
import {
15+
AbstractMessageHandler,
16+
BasicHandlerOptions,
17+
IProtocolMessageHandler
18+
} from './message-handler';
19+
import { getUnixTimestamp } from '@iden3/js-iden3-core';
20+
import { verifyExpiresTime } from './common';
21+
22+
/**
23+
* @beta
24+
* DiscoveryProtocolOptions contains options for DiscoveryProtocolHandler
25+
* @public
26+
* @interface DiscoveryProtocolOptions
27+
*/
28+
export interface DiscoveryProtocolOptions {
29+
packageManager: IPackageManager;
30+
protocols?: Array<ProtocolMessage>;
31+
goalCodes?: Array<string>;
32+
headers?: Array<string>;
33+
}
34+
35+
/**
36+
*
37+
* Options to pass to discovery-protocol handler
38+
*
39+
* @beta
40+
* @public
41+
* @type DiscoveryProtocolHandlerOptions
42+
*/
43+
export type DiscoveryProtocolHandlerOptions = BasicHandlerOptions & {
44+
disclosureExpiresDate?: Date;
45+
};
46+
47+
/**
48+
* @beta
49+
* createDiscoveryFeatureQueryMessage is a function to create didcomm protocol discovery-feature query message
50+
* @param opts - discovery-feature query options
51+
* @returns `DiscoverFeatureQueriesMessage`
52+
*/
53+
export function createDiscoveryFeatureQueryMessage(
54+
queries: DiscoverFeatureQuery[],
55+
opts?: {
56+
from?: string;
57+
to?: string;
58+
expires_time?: number;
59+
}
60+
): DiscoverFeatureQueriesMessage {
61+
const uuidv4 = uuid.v4();
62+
return {
63+
id: uuidv4,
64+
thid: uuidv4,
65+
type: PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE,
66+
body: {
67+
queries
68+
},
69+
from: opts?.from,
70+
to: opts?.to,
71+
created_time: getUnixTimestamp(new Date()),
72+
expires_time: opts?.expires_time
73+
};
74+
}
75+
76+
/**
77+
* @beta
78+
* createDiscoveryFeatureDiscloseMessage is a function to create didcomm protocol discovery-feature disclose message
79+
* @param {DiscoverFeatureDisclosure[]} disclosures - array of disclosures
80+
* @param opts - basic message options
81+
* @returns `DiscoverFeatureQueriesMessage`
82+
*/
83+
export function createDiscoveryFeatureDiscloseMessage(
84+
disclosures: DiscoverFeatureDisclosure[],
85+
opts?: {
86+
from?: string;
87+
to?: string;
88+
expires_time?: number;
89+
}
90+
): DiscoverFeatureDiscloseMessage {
91+
const uuidv4 = uuid.v4();
92+
return {
93+
id: uuidv4,
94+
thid: uuidv4,
95+
type: PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_DISCLOSE_MESSAGE_TYPE,
96+
body: {
97+
disclosures
98+
},
99+
from: opts?.from,
100+
to: opts?.to,
101+
created_time: getUnixTimestamp(new Date()),
102+
expires_time: opts?.expires_time
103+
};
104+
}
105+
106+
/**
107+
* Interface to work with discovery protocol handler
108+
*
109+
* @beta
110+
* @public
111+
* @interface IDiscoveryProtocolHandler
112+
*/
113+
export interface IDiscoveryProtocolHandler {
114+
/**
115+
* handle discovery query message
116+
*
117+
* @param {DiscoverFeatureQueriesMessage} message - discover feature queries message
118+
* @param {{ expires_time?: number}} opts - discover feature handle options
119+
* @returns {Promise<DiscoverFeatureDiscloseMessage>} - discover feature disclose message
120+
*/
121+
handleDiscoveryQuery(
122+
message: DiscoverFeatureQueriesMessage,
123+
opts?: DiscoveryProtocolHandlerOptions
124+
): Promise<DiscoverFeatureDiscloseMessage>;
125+
}
126+
127+
/**
128+
*
129+
* Handler for discovery protocol
130+
*
131+
* @public
132+
* @beta
133+
* @class DiscoveryProtocolHandler
134+
* @implements implements DiscoveryProtocolHandler interface
135+
*/
136+
export class DiscoveryProtocolHandler
137+
extends AbstractMessageHandler
138+
implements IDiscoveryProtocolHandler, IProtocolMessageHandler
139+
{
140+
/**
141+
* Creates an instance of DiscoveryProtocolHandler.
142+
* @param {DiscoveryProtocolOptions} _options - discovery protocol options
143+
*/
144+
constructor(private readonly _options: DiscoveryProtocolOptions) {
145+
super();
146+
const headers = [
147+
'id',
148+
'typ',
149+
'type',
150+
'thid',
151+
'body',
152+
'from',
153+
'to',
154+
'created_time',
155+
'expires_time'
156+
];
157+
if (!_options.headers) {
158+
_options.headers = headers;
159+
}
160+
}
161+
162+
/**
163+
* @inheritdoc IProtocolMessageHandler#handle
164+
*/
165+
public async handle(
166+
message: BasicMessage,
167+
context: { [key: string]: unknown }
168+
): Promise<BasicMessage | null> {
169+
switch (message.type) {
170+
case PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE:
171+
return await this.handleDiscoveryQuery(message as DiscoverFeatureQueriesMessage, context);
172+
default:
173+
return super.handle(message, context as { [key: string]: unknown });
174+
}
175+
}
176+
177+
/**
178+
* @inheritdoc IDiscoveryProtocolHandler#handleDiscoveryQuery
179+
*/
180+
async handleDiscoveryQuery(
181+
message: DiscoverFeatureQueriesMessage,
182+
opts?: DiscoveryProtocolHandlerOptions
183+
): Promise<DiscoverFeatureDiscloseMessage> {
184+
if (!opts?.allowExpiredMessages) {
185+
verifyExpiresTime(message);
186+
}
187+
188+
const disclosures: DiscoverFeatureDisclosure[] = [];
189+
for (const query of message.body.queries) {
190+
disclosures.push(...this.handleQuery(query));
191+
}
192+
193+
return Promise.resolve(
194+
createDiscoveryFeatureDiscloseMessage(disclosures, {
195+
to: message.from,
196+
from: message.to,
197+
expires_time: opts?.disclosureExpiresDate
198+
? getUnixTimestamp(opts.disclosureExpiresDate)
199+
: undefined
200+
})
201+
);
202+
}
203+
204+
private handleQuery(query: DiscoverFeatureQuery): DiscoverFeatureDisclosure[] {
205+
let result: DiscoverFeatureDisclosure[] = [];
206+
switch (query[DiscoverFeatureQueryType.FeatureType]) {
207+
case DiscoveryProtocolFeatureType.Accept:
208+
result = this.handleAcceptQuery();
209+
break;
210+
case DiscoveryProtocolFeatureType.Protocol:
211+
result = this.handleProtocolQuery();
212+
break;
213+
case DiscoveryProtocolFeatureType.GoalCode:
214+
result = this.handleGoalCodeQuery();
215+
break;
216+
case DiscoveryProtocolFeatureType.Header:
217+
result = this.handleHeaderQuery();
218+
break;
219+
}
220+
221+
return this.handleMatch(result, query.match);
222+
}
223+
224+
private handleAcceptQuery(): DiscoverFeatureDisclosure[] {
225+
const acceptProfiles = this._options.packageManager.getSupportedProfiles();
226+
return acceptProfiles.map((profile) => ({
227+
[DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Accept,
228+
id: profile
229+
}));
230+
}
231+
232+
private handleProtocolQuery(): DiscoverFeatureDisclosure[] {
233+
return (
234+
this._options.protocols?.map((protocol) => ({
235+
[DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol,
236+
id: protocol
237+
})) ?? []
238+
);
239+
}
240+
241+
private handleGoalCodeQuery(): DiscoverFeatureDisclosure[] {
242+
return (
243+
this._options.goalCodes?.map((goalCode) => ({
244+
[DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.GoalCode,
245+
id: goalCode
246+
})) ?? []
247+
);
248+
}
249+
250+
private handleHeaderQuery(): DiscoverFeatureDisclosure[] {
251+
return (
252+
this._options.headers?.map((header) => ({
253+
[DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Header,
254+
id: header
255+
})) ?? []
256+
);
257+
}
258+
259+
private handleMatch(
260+
disclosures: DiscoverFeatureDisclosure[],
261+
match?: string
262+
): DiscoverFeatureDisclosure[] {
263+
if (!match || match === '*') {
264+
return disclosures;
265+
}
266+
const regExp = this.wildcardToRegExp(match);
267+
return disclosures.filter((disclosure) => regExp.test(disclosure.id));
268+
}
269+
270+
private wildcardToRegExp(match: string): RegExp {
271+
// Escape special regex characters, then replace `*` with `.*`
272+
const regexPattern = match.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
273+
return new RegExp(`^${regexPattern}$`);
274+
}
275+
}

src/iden3comm/handlers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export * from './common';
77
export * from './credential-proposal';
88
export * from './message-handler';
99
export * from './payment';
10+
export * from './discovery-protocol';

src/iden3comm/packageManager.ts

+13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ export class PackageManager implements IPackageManager {
2121
this.packers = new Map<MediaType, IPacker>();
2222
}
2323

24+
/** {@inheritDoc IPackageManager.getSupportedProfiles} */
25+
getSupportedProfiles(): string[] {
26+
const acceptProfiles: string[] = [];
27+
const mediaTypes = this.getSupportedMediaTypes();
28+
for (const mediaType of mediaTypes) {
29+
const p = this.packers.get(mediaType);
30+
if (p) {
31+
acceptProfiles.push(...p.getSupportedProfiles());
32+
}
33+
}
34+
return [...new Set(acceptProfiles)];
35+
}
36+
2437
/** {@inheritDoc IPackageManager.isProfileSupported} */
2538
isProfileSupported(mediaType: MediaType, profile: string): boolean {
2639
const p = this.packers.get(mediaType);

src/iden3comm/types/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './protocol/contract-request';
77
export * from './protocol/proposal-request';
88
export * from './protocol/payment';
99
export * from './protocol/accept-profile';
10+
export * from './protocol/discovery-protocol';
1011

1112
export * from './packer';
1213
export * from './models';

src/iden3comm/types/packageManager.ts

+7
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ export interface IPackageManager {
8282
*/
8383
getSupportedMediaTypes(): MediaType[];
8484

85+
/**
86+
* gets supported accept profiles by packer manager
87+
*
88+
* @returns string[]
89+
*/
90+
getSupportedProfiles(): string[];
91+
8592
/**
8693
* returns true if media type and algorithms supported by packer manager
8794
*

0 commit comments

Comments
 (0)