diff --git a/.eslintrc.js b/.eslintrc.js index 77fc6a3d..d536b3b9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,7 @@ module.exports = { ...spellcheckerRule, cspell: { ...cspellConfig, - ignoreWords: ['unmarshal', 'JUvpllMEYUZ2joO59UNui_XYDqxVqiFLLAJ8klWuPBw', 'gdwj', 'fwor', 'multichain', "ETHWEI", "ETHGWEI"] + ignoreWords: ['unmarshal', 'JUvpllMEYUZ2joO59UNui_XYDqxVqiFLLAJ8klWuPBw', 'gdwj', 'fwor', 'multichain', "ETHWEI", "ETHGWEI", "didcomm"] } } ] diff --git a/package-lock.json b/package-lock.json index 108bc1e3..0828a485 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.25.1", + "version": "1.26.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", - "version": "1.25.1", + "version": "1.26.0", "license": "MIT or Apache-2.0", "dependencies": { "@iden3/onchain-non-merklized-issuer-base-abi": "^0.0.3", diff --git a/package.json b/package.json index b52cc8b0..1d910f0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.25.1", + "version": "1.26.0", "description": "SDK to work with Polygon ID", "main": "dist/node/cjs/index.js", "module": "dist/node/esm/index.js", diff --git a/src/iden3comm/constants.ts b/src/iden3comm/constants.ts index 398767f1..f061f6ab 100644 --- a/src/iden3comm/constants.ts +++ b/src/iden3comm/constants.ts @@ -1,6 +1,7 @@ import { AcceptProfile } from './types'; const IDEN3_PROTOCOL = 'https://iden3-communication.io/'; +const DIDCOMM_PROTOCOL = 'https://didcomm.org/'; /** * Constants for Iden3 protocol */ @@ -48,7 +49,13 @@ export const PROTOCOL_MESSAGE_TYPE = Object.freeze({ // PaymentRequestMessageType is type for payment-request message PAYMENT_REQUEST_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment-request` as const, // PaymentMessageType is type for payment message - PAYMENT_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment` as const + PAYMENT_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/0.1/payment` as const, + // DiscoveryProtocolQueriesMessageType is type for didcomm discovery protocol queries + DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE: + `${DIDCOMM_PROTOCOL}discover-features/2.0/queries` as const, + // DiscoveryProtocolDiscloseMessageType is type for didcomm discovery protocol disclose + DISCOVERY_PROTOCOL_DISCLOSE_MESSAGE_TYPE: + `${DIDCOMM_PROTOCOL}discover-features/2.0/disclose` as const }); /** diff --git a/src/iden3comm/handlers/discovery-protocol.ts b/src/iden3comm/handlers/discovery-protocol.ts new file mode 100644 index 00000000..a3714e0b --- /dev/null +++ b/src/iden3comm/handlers/discovery-protocol.ts @@ -0,0 +1,275 @@ +import { PROTOCOL_MESSAGE_TYPE } from '../constants'; + +import { BasicMessage, IPackageManager, ProtocolMessage } from '../types'; + +import * as uuid from 'uuid'; +import { + DiscoverFeatureDiscloseMessage, + DiscoverFeatureDisclosure, + DiscoverFeatureQueriesMessage, + DiscoverFeatureQuery, + DiscoverFeatureQueryType, + DiscoveryProtocolFeatureType +} from '../types/protocol/discovery-protocol'; +import { + AbstractMessageHandler, + BasicHandlerOptions, + IProtocolMessageHandler +} from './message-handler'; +import { getUnixTimestamp } from '@iden3/js-iden3-core'; +import { verifyExpiresTime } from './common'; + +/** + * @beta + * DiscoveryProtocolOptions contains options for DiscoveryProtocolHandler + * @public + * @interface DiscoveryProtocolOptions + */ +export interface DiscoveryProtocolOptions { + packageManager: IPackageManager; + protocols?: Array; + goalCodes?: Array; + headers?: Array; +} + +/** + * + * Options to pass to discovery-protocol handler + * + * @beta + * @public + * @type DiscoveryProtocolHandlerOptions + */ +export type DiscoveryProtocolHandlerOptions = BasicHandlerOptions & { + disclosureExpiresDate?: Date; +}; + +/** + * @beta + * createDiscoveryFeatureQueryMessage is a function to create didcomm protocol discovery-feature query message + * @param opts - discovery-feature query options + * @returns `DiscoverFeatureQueriesMessage` + */ +export function createDiscoveryFeatureQueryMessage( + queries: DiscoverFeatureQuery[], + opts?: { + from?: string; + to?: string; + expires_time?: number; + } +): DiscoverFeatureQueriesMessage { + const uuidv4 = uuid.v4(); + return { + id: uuidv4, + thid: uuidv4, + type: PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE, + body: { + queries + }, + from: opts?.from, + to: opts?.to, + created_time: getUnixTimestamp(new Date()), + expires_time: opts?.expires_time + }; +} + +/** + * @beta + * createDiscoveryFeatureDiscloseMessage is a function to create didcomm protocol discovery-feature disclose message + * @param {DiscoverFeatureDisclosure[]} disclosures - array of disclosures + * @param opts - basic message options + * @returns `DiscoverFeatureQueriesMessage` + */ +export function createDiscoveryFeatureDiscloseMessage( + disclosures: DiscoverFeatureDisclosure[], + opts?: { + from?: string; + to?: string; + expires_time?: number; + } +): DiscoverFeatureDiscloseMessage { + const uuidv4 = uuid.v4(); + return { + id: uuidv4, + thid: uuidv4, + type: PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_DISCLOSE_MESSAGE_TYPE, + body: { + disclosures + }, + from: opts?.from, + to: opts?.to, + created_time: getUnixTimestamp(new Date()), + expires_time: opts?.expires_time + }; +} + +/** + * Interface to work with discovery protocol handler + * + * @beta + * @public + * @interface IDiscoveryProtocolHandler + */ +export interface IDiscoveryProtocolHandler { + /** + * handle discovery query message + * + * @param {DiscoverFeatureQueriesMessage} message - discover feature queries message + * @param {{ expires_time?: number}} opts - discover feature handle options + * @returns {Promise} - discover feature disclose message + */ + handleDiscoveryQuery( + message: DiscoverFeatureQueriesMessage, + opts?: DiscoveryProtocolHandlerOptions + ): Promise; +} + +/** + * + * Handler for discovery protocol + * + * @public + * @beta + * @class DiscoveryProtocolHandler + * @implements implements DiscoveryProtocolHandler interface + */ +export class DiscoveryProtocolHandler + extends AbstractMessageHandler + implements IDiscoveryProtocolHandler, IProtocolMessageHandler +{ + /** + * Creates an instance of DiscoveryProtocolHandler. + * @param {DiscoveryProtocolOptions} _options - discovery protocol options + */ + constructor(private readonly _options: DiscoveryProtocolOptions) { + super(); + const headers = [ + 'id', + 'typ', + 'type', + 'thid', + 'body', + 'from', + 'to', + 'created_time', + 'expires_time' + ]; + if (!_options.headers) { + _options.headers = headers; + } + } + + /** + * @inheritdoc IProtocolMessageHandler#handle + */ + public async handle( + message: BasicMessage, + context: { [key: string]: unknown } + ): Promise { + switch (message.type) { + case PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE: + return await this.handleDiscoveryQuery(message as DiscoverFeatureQueriesMessage, context); + default: + return super.handle(message, context as { [key: string]: unknown }); + } + } + + /** + * @inheritdoc IDiscoveryProtocolHandler#handleDiscoveryQuery + */ + async handleDiscoveryQuery( + message: DiscoverFeatureQueriesMessage, + opts?: DiscoveryProtocolHandlerOptions + ): Promise { + if (!opts?.allowExpiredMessages) { + verifyExpiresTime(message); + } + + const disclosures: DiscoverFeatureDisclosure[] = []; + for (const query of message.body.queries) { + disclosures.push(...this.handleQuery(query)); + } + + return Promise.resolve( + createDiscoveryFeatureDiscloseMessage(disclosures, { + to: message.from, + from: message.to, + expires_time: opts?.disclosureExpiresDate + ? getUnixTimestamp(opts.disclosureExpiresDate) + : undefined + }) + ); + } + + private handleQuery(query: DiscoverFeatureQuery): DiscoverFeatureDisclosure[] { + let result: DiscoverFeatureDisclosure[] = []; + switch (query[DiscoverFeatureQueryType.FeatureType]) { + case DiscoveryProtocolFeatureType.Accept: + result = this.handleAcceptQuery(); + break; + case DiscoveryProtocolFeatureType.Protocol: + result = this.handleProtocolQuery(); + break; + case DiscoveryProtocolFeatureType.GoalCode: + result = this.handleGoalCodeQuery(); + break; + case DiscoveryProtocolFeatureType.Header: + result = this.handleHeaderQuery(); + break; + } + + return this.handleMatch(result, query.match); + } + + private handleAcceptQuery(): DiscoverFeatureDisclosure[] { + const acceptProfiles = this._options.packageManager.getSupportedProfiles(); + return acceptProfiles.map((profile) => ({ + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Accept, + id: profile + })); + } + + private handleProtocolQuery(): DiscoverFeatureDisclosure[] { + return ( + this._options.protocols?.map((protocol) => ({ + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol, + id: protocol + })) ?? [] + ); + } + + private handleGoalCodeQuery(): DiscoverFeatureDisclosure[] { + return ( + this._options.goalCodes?.map((goalCode) => ({ + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.GoalCode, + id: goalCode + })) ?? [] + ); + } + + private handleHeaderQuery(): DiscoverFeatureDisclosure[] { + return ( + this._options.headers?.map((header) => ({ + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Header, + id: header + })) ?? [] + ); + } + + private handleMatch( + disclosures: DiscoverFeatureDisclosure[], + match?: string + ): DiscoverFeatureDisclosure[] { + if (!match || match === '*') { + return disclosures; + } + const regExp = this.wildcardToRegExp(match); + return disclosures.filter((disclosure) => regExp.test(disclosure.id)); + } + + private wildcardToRegExp(match: string): RegExp { + // Escape special regex characters, then replace `*` with `.*` + const regexPattern = match.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*'); + return new RegExp(`^${regexPattern}$`); + } +} diff --git a/src/iden3comm/handlers/index.ts b/src/iden3comm/handlers/index.ts index 5a718b16..fbeb9918 100644 --- a/src/iden3comm/handlers/index.ts +++ b/src/iden3comm/handlers/index.ts @@ -7,3 +7,4 @@ export * from './common'; export * from './credential-proposal'; export * from './message-handler'; export * from './payment'; +export * from './discovery-protocol'; diff --git a/src/iden3comm/packageManager.ts b/src/iden3comm/packageManager.ts index 2a73e1ca..40d7be48 100644 --- a/src/iden3comm/packageManager.ts +++ b/src/iden3comm/packageManager.ts @@ -21,6 +21,19 @@ export class PackageManager implements IPackageManager { this.packers = new Map(); } + /** {@inheritDoc IPackageManager.getSupportedProfiles} */ + getSupportedProfiles(): string[] { + const acceptProfiles: string[] = []; + const mediaTypes = this.getSupportedMediaTypes(); + for (const mediaType of mediaTypes) { + const p = this.packers.get(mediaType); + if (p) { + acceptProfiles.push(...p.getSupportedProfiles()); + } + } + return [...new Set(acceptProfiles)]; + } + /** {@inheritDoc IPackageManager.isProfileSupported} */ isProfileSupported(mediaType: MediaType, profile: string): boolean { const p = this.packers.get(mediaType); diff --git a/src/iden3comm/types/index.ts b/src/iden3comm/types/index.ts index da011009..b72df0ae 100644 --- a/src/iden3comm/types/index.ts +++ b/src/iden3comm/types/index.ts @@ -7,6 +7,7 @@ export * from './protocol/contract-request'; export * from './protocol/proposal-request'; export * from './protocol/payment'; export * from './protocol/accept-profile'; +export * from './protocol/discovery-protocol'; export * from './packer'; export * from './models'; diff --git a/src/iden3comm/types/packageManager.ts b/src/iden3comm/types/packageManager.ts index 554663bd..25f9d137 100644 --- a/src/iden3comm/types/packageManager.ts +++ b/src/iden3comm/types/packageManager.ts @@ -82,6 +82,13 @@ export interface IPackageManager { */ getSupportedMediaTypes(): MediaType[]; + /** + * gets supported accept profiles by packer manager + * + * @returns string[] + */ + getSupportedProfiles(): string[]; + /** * returns true if media type and algorithms supported by packer manager * diff --git a/src/iden3comm/types/protocol/discovery-protocol.ts b/src/iden3comm/types/protocol/discovery-protocol.ts new file mode 100644 index 00000000..911fad15 --- /dev/null +++ b/src/iden3comm/types/protocol/discovery-protocol.ts @@ -0,0 +1,49 @@ +import { PROTOCOL_MESSAGE_TYPE } from '../../constants'; +import { BasicMessage } from '../packer'; + +/** @beta DiscoverFeatureQueryType is enum for query type fields */ +export enum DiscoverFeatureQueryType { + FeatureType = 'feature-type' +} + +/** @beta DiscoveryProtocolFeatureType is enum for supported feature-types */ +export enum DiscoveryProtocolFeatureType { + Accept = 'accept', + Protocol = 'protocol', + GoalCode = 'goal-code', + Header = 'header' +} + +/** @beta DiscoverFeatureQueriesMessage is struct the represents discover feature queries message */ +export type DiscoverFeatureQueriesMessage = BasicMessage & { + body: DiscoverFeatureQueriesBody; + type: typeof PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_QUERIES_MESSAGE_TYPE; +}; + +/** @beta DiscoverFeatureQueriesBody is struct the represents discover feature queries body */ +export type DiscoverFeatureQueriesBody = { + queries: DiscoverFeatureQuery[]; +}; + +/** @beta DiscoverFeatureQuery is struct the represents discover feature query */ +export type DiscoverFeatureQuery = { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType; + match?: string; +}; + +/** @beta DiscoverFeatureDiscloseMessage is struct the represents discover feature disclose message */ +export type DiscoverFeatureDiscloseMessage = BasicMessage & { + body: DiscoverFeatureDiscloseBody; + type: typeof PROTOCOL_MESSAGE_TYPE.DISCOVERY_PROTOCOL_DISCLOSE_MESSAGE_TYPE; +}; + +/** @beta DiscoverFeatureDiscloseBody is struct the represents discover feature disclose body */ +export type DiscoverFeatureDiscloseBody = { + disclosures: DiscoverFeatureDisclosure[]; +}; + +/** @beta DiscoverFeatureDisclosure is struct the represents discover feature disclosure */ +export type DiscoverFeatureDisclosure = { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType; + id: string; +}; diff --git a/tests/handlers/discover-protocol.test.ts b/tests/handlers/discover-protocol.test.ts new file mode 100644 index 00000000..e5c117f0 --- /dev/null +++ b/tests/handlers/discover-protocol.test.ts @@ -0,0 +1,331 @@ +import { expect } from 'chai'; +import { + DiscoverFeatureQueriesMessage, + DiscoverFeatureQueryType, + DiscoveryProtocolFeatureType, + IPackageManager, + JWSPacker, + KMS, + PackageManager, + PlainPacker, + ZKPPacker +} from '../../src'; +import { + DiscoveryProtocolHandler, + createDiscoveryFeatureQueryMessage +} from '../../src/iden3comm/handlers/discovery-protocol'; +import { DIDResolutionResult } from 'did-resolver'; +import { PROTOCOL_MESSAGE_TYPE } from '../../src/iden3comm/constants'; + +describe('discovery-protocol', () => { + let acceptQueryMessage: DiscoverFeatureQueriesMessage; + let jwsPacker: JWSPacker; + let zkpPacker: ZKPPacker; + let plainPacker: PlainPacker; + + beforeEach(async () => { + jwsPacker = new JWSPacker(new KMS(), { + resolve: () => Promise.resolve({ didDocument: {} } as DIDResolutionResult) + }); + + zkpPacker = new ZKPPacker(new Map(), new Map()); + plainPacker = new PlainPacker(); + acceptQueryMessage = createDiscoveryFeatureQueryMessage([ + { [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Accept } + ]); + }); + + it('plain message accept disclosures', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([plainPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(acceptQueryMessage); + expect(disclosures.length).to.be.eq(1); + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[0].id).to.be.eq('env=application/iden3comm-plain-json'); + }); + + it('jws and plain message accept disclosures', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([new PlainPacker(), plainPacker, jwsPacker]); + + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(acceptQueryMessage); + expect(disclosures.length).to.be.eq(2); + + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include('env=application/iden3comm-plain-json'); + expect(disclosureIds).to.include('env=application/iden3comm-signed-json&alg=ES256K,ES256K-R'); + }); + + it('zkp and plain message accept disclosures', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([new PlainPacker(), plainPacker, zkpPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(acceptQueryMessage); + expect(disclosures.length).to.be.eq(2); + + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include('env=application/iden3comm-plain-json'); + expect(disclosureIds).to.include( + 'env=application/iden3-zkp-json&alg=groth16&circuitIds=authV2' + ); + }); + + it('zkp, jws and plain message accept disclosures', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([new PlainPacker(), plainPacker, zkpPacker, jwsPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(acceptQueryMessage); + expect(disclosures.length).to.be.eq(3); + + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[2][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include('env=application/iden3comm-plain-json'); + expect(disclosureIds).to.include( + 'env=application/iden3-zkp-json&alg=groth16&circuitIds=authV2' + ); + expect(disclosureIds).to.include('env=application/iden3comm-signed-json&alg=ES256K,ES256K-R'); + }); + + it('zkp, jws and plain message accept disclosures with exact match', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([new PlainPacker(), plainPacker, zkpPacker, jwsPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const acceptQueryMessageWithMatch = createDiscoveryFeatureQueryMessage([ + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Accept, + match: 'env=application/iden3-zkp-json&alg=groth16&circuitIds=authV2' + } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(acceptQueryMessageWithMatch); + expect(disclosures.length).to.be.eq(1); + + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[0].id).to.include( + 'env=application/iden3-zkp-json&alg=groth16&circuitIds=authV2' + ); + }); + + it('feature-type: protocol with protocol version match', async () => { + const packageManager: IPackageManager = new PackageManager(); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager, + protocols: [ + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE + ] + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol, + match: 'https://iden3-communication.io/authorization/1.*' + } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(2); + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + }); + + it('feature-type: protocol and goal-code with protocol version match', async () => { + const packageManager: IPackageManager = new PackageManager(); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager, + protocols: [ + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE + ], + goalCodes: ['unit.testing.some.goal-code'] + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol, + match: 'https://iden3-communication.io/authorization/1.*' + }, + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.GoalCode, + match: 'unit.testing.*' + } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(3); + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + expect(disclosures[2][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.GoalCode + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + expect(disclosureIds).to.include('unit.testing.some.goal-code'); + }); + + it('feature-type: header', async () => { + const packageManager: IPackageManager = new PackageManager(); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Header, + match: 'expires_time' + } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(1); + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Header + ); + expect(disclosures[0].id).to.be.eq('expires_time'); + }); + + it('feature-type: protocol with protocol version mismatch', async () => { + const packageManager: IPackageManager = new PackageManager(); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager, + protocols: [ + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE + ] + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { + [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol, + match: 'https://iden3-communication.io/authorization/44.*' + } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(0); + }); + + it('feature-type: protocol and accept', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([plainPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager, + protocols: [ + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, + PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE + ] + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Accept }, + { [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Protocol } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(3); + expect(disclosures[0][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Accept + ); + expect(disclosures[1][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + expect(disclosures[2][DiscoverFeatureQueryType.FeatureType]).to.be.eq( + DiscoveryProtocolFeatureType.Protocol + ); + const disclosureIds = disclosures.map((d) => d.id); + expect(disclosureIds).to.include('env=application/iden3comm-plain-json'); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + expect(disclosureIds).to.include(PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE); + }); + + it('feature-type: empty headers', async () => { + const packageManager: IPackageManager = new PackageManager(); + packageManager.registerPackers([plainPacker]); + const discoveryProtocolHandler = new DiscoveryProtocolHandler({ + packageManager, + headers: [] + }); + + const protocolQueryMessage = createDiscoveryFeatureQueryMessage([ + { [DiscoverFeatureQueryType.FeatureType]: DiscoveryProtocolFeatureType.Header } + ]); + + const { + body: { disclosures } + } = await discoveryProtocolHandler.handleDiscoveryQuery(protocolQueryMessage); + expect(disclosures.length).to.be.eq(0); + }); +});