diff --git a/packages/did-utils/package.json b/packages/did-utils/package.json index 46533bdff..59019a6fe 100644 --- a/packages/did-utils/package.json +++ b/packages/did-utils/package.json @@ -10,13 +10,12 @@ }, "dependencies": { "@sphereon/did-uni-client": "^0.6.0", - "@sphereon/jsencrypt": "3.3.2-unstable.0", "@sphereon/ssi-sdk-core": "^0.9.0", "@sphereon/ssi-types": "^0.9.0", - "@trust/keyto": "^2.0.0-alpha1", "@veramo/core": "4.2.0", "@veramo/utils": "4.2.0", - "did-resolver": "^4.0.1", + "did-resolver": "^4.1.0", + "did-jwt": "^6.11.6", "elliptic": "^6.5.4", "uint8arrays": "^3.1.1" }, diff --git a/packages/did-utils/src/didFunctions.ts b/packages/did-utils/src/didFunctions.ts index 70401f1d3..3c7a03c9e 100644 --- a/packages/did-utils/src/didFunctions.ts +++ b/packages/did-utils/src/didFunctions.ts @@ -1,5 +1,14 @@ import { UniResolver } from '@sphereon/did-uni-client' -import { DIDDocument, DIDDocumentSection, DIDResolutionResult, IAgentContext, IDIDManager, IIdentifier, IResolver } from '@veramo/core' +import { + DIDDocument, + DIDDocumentSection, + DIDResolutionResult, + IAgentContext, + IDIDManager, + IIdentifier, + IKey, + IResolver, +} from '@veramo/core' import { _ExtendedIKey, _ExtendedVerificationMethod, @@ -14,6 +23,7 @@ import { DIDResolutionOptions, Resolvable, VerificationMethod } from 'did-resolv import elliptic from 'elliptic' import * as u8a from 'uint8arrays' import { hexKeyFromPEMBasedJwk } from './x509-utils' +import { IDIDOptions, IIdentifierOpts } from './types' export const getFirstKeyWithRelation = async ( identifier: IIdentifier, @@ -144,10 +154,10 @@ export async function mapIdentifierKeysToDocWithJwkSupport( const localKeys = identifier.keys.filter(isDefined) // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` const extendedKeys: _ExtendedIKey[] = documentKeys - .map((verificationMethod) => { - if (verificationMethod.type !== 'JsonWebKey2020') { + . map((verificationMethod) => { + /*if (verificationMethod.type !== 'JsonWebKey2020') { return null - } + }*/ const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex) if (localKey) { const { meta, ...localProps } = localKey @@ -165,6 +175,68 @@ export async function getAgentDIDMethods(context: IAgentContext) { return (await context.agent.didManagerGetProviders()).map((provider) => provider.toLowerCase().replace('did:', '')) } +export async function getIdentifier(identifierOpts: IIdentifierOpts, context: IAgentContext): Promise { + if (typeof identifierOpts.identifier === 'string') { + return context.agent.didManagerGet({ did: identifierOpts.identifier }) + } else if (typeof identifierOpts.identifier === 'object') { + return identifierOpts.identifier + } + throw Error(`Cannot get agent identifier value from options`) +} + +export function getDID(identifierOpts: IIdentifierOpts): string { + if (typeof identifierOpts.identifier === 'string') { + return identifierOpts.identifier + } else if (typeof identifierOpts.identifier === 'object') { + return identifierOpts.identifier.did + } + throw Error(`Cannot get DID from identifier value`) +} + +export function toDID(identifier: string | IIdentifier | Partial): string { + if (typeof identifier === 'string') { + return identifier + } + if (identifier.did) { + return identifier.did + } + throw Error(`No DID value present in identifier`) +} + +export function toDIDs(identifiers?: (string | IIdentifier | Partial)[]): string[] { + if (!identifiers) { + return [] + } + return identifiers.map(toDID) +} + +export async function getKey( + identifier: IIdentifier, + verificationMethodSection: DIDDocumentSection = 'authentication', + context: IAgentContext, + keyId?: string +): Promise { + const keys = await mapIdentifierKeysToDocWithJwkSupport(identifier, verificationMethodSection, context) + if (!keys || keys.length === 0) { + throw new Error(`No keys found for verificationMethodSection: ${verificationMethodSection} and did ${identifier.did}`) + } + + const identifierKey = keyId ? keys.find((key: _ExtendedIKey) => key.kid === keyId || key.meta.verificationMethod.id === keyId) : keys[0] + if (!identifierKey) { + throw new Error(`No matching verificationMethodSection key found for keyId: ${keyId}`) + } + + return identifierKey +} + +export function determineKid(key: IKey, idOpts: IIdentifierOpts): string { + return key.meta?.verificationMethod.id ?? idOpts.kid ?? key.kid +} + +export async function getSupportedDIDMethods(didOpts: IDIDOptions, context: IAgentContext) { + return didOpts.supportedDIDMethods ?? (await getAgentDIDMethods(context)) +} + export class AgentDIDResolver implements Resolvable { private readonly context: IAgentContext private readonly uniresolverFallback: boolean diff --git a/packages/did-utils/src/types.ts b/packages/did-utils/src/types.ts index 961736585..691b33c2e 100644 --- a/packages/did-utils/src/types.ts +++ b/packages/did-utils/src/types.ts @@ -1,3 +1,7 @@ +import { JWTVerifyOptions } from 'did-jwt' +import { Resolvable } from 'did-resolver' +import { DIDDocumentSection, IIdentifier } from '@veramo/core' + export interface JWK extends JsonWebKey { x5c?: string x5u?: string @@ -13,4 +17,24 @@ export interface X509Opts { certificateChainPEM?: string // Base64 (not url!) encoded DER certificate chain. Please provide even if certificateChainURL is used! } +export interface ResolveOpts { + jwtVerifyOpts?: JWTVerifyOptions + resolver?: Resolvable + resolveUrl?: string + noUniversalResolverFallback?: boolean + subjectSyntaxTypesSupported?: string[] +} + +export interface IDIDOptions { + resolveOpts?: ResolveOpts + identifierOpts: IIdentifierOpts + supportedDIDMethods?: string[] +} + +export interface IIdentifierOpts { + identifier: IIdentifier | string + verificationMethodSection?: DIDDocumentSection + kid?: string +} + export const DID_PREFIX = 'did:' diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.d.ts index 7cd36932e..e03e398e9 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.d.ts @@ -1,11 +1,11 @@ /** - * Provides a {@link @veramo/did-manager#DIDManager | plugin} for the - * {@link @veramo/core#Agent} that implements {@link @veramo/core-types#IDIDManager} interface. + * Provides a {@link @veramo/kv-store#KeyValueStore} for the + * {@link @veramo/core#Agent} plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * * @packageDocumentation */ -export { KeyValueStore } from './key-value-store.js'; -export * from './store-adapters/tiered/index.js'; -export * from './store-adapters/typeorm/index.js'; -export * from './key-value-types.js'; +export { KeyValueStore } from './key-value-store'; +export * from './store-adapters/tiered/index'; +export * from './store-adapters/typeorm/index'; +export * from './key-value-types'; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.js index 5c18404a2..691e71bbe 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/index.js @@ -1,11 +1,29 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KeyValueStore = void 0; /** - * Provides a {@link @veramo/did-manager#DIDManager | plugin} for the - * {@link @veramo/core#Agent} that implements {@link @veramo/core-types#IDIDManager} interface. + * Provides a {@link @veramo/kv-store#KeyValueStore} for the + * {@link @veramo/core#Agent} plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * * @packageDocumentation */ -export { KeyValueStore } from './key-value-store.js'; -export * from './store-adapters/tiered/index.js'; -export * from './store-adapters/typeorm/index.js'; -export * from './key-value-types.js'; +var key_value_store_1 = require("./key-value-store"); +Object.defineProperty(exports, "KeyValueStore", { enumerable: true, get: function () { return key_value_store_1.KeyValueStore; } }); +__exportStar(require("./store-adapters/tiered/index"), exports); +__exportStar(require("./store-adapters/typeorm/index"), exports); +__exportStar(require("./key-value-types"), exports); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.d.ts index 683a0a64a..d1c6c42ac 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.d.ts @@ -1,6 +1,6 @@ -import { IKeyValueStore, IKeyValueStoreOnArgs, IKeyValueStoreOptions, IValueData } from './key-value-types.js'; +import { IKeyValueStore, IKeyValueStoreOnArgs, IKeyValueStoreOptions, IValueData } from './key-value-types'; /** - * Agent plugin that implements {@link @veramo/core-types#IKeyValueStore} interface + * Agent plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * @public */ export declare class KeyValueStore implements IKeyValueStore { diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.js index ed9edb99c..766e0ed43 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-store.js @@ -1,83 +1,107 @@ -import { Keyv } from './keyv/keyv.js'; +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KeyValueStore = void 0; +const keyv_1 = require("./keyv/keyv"); /** - * Agent plugin that implements {@link @veramo/core-types#IKeyValueStore} interface + * Agent plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * @public */ -export class KeyValueStore { - /** - * The main keyv typescript port which delegates to the storage adapters and takes care of some common functionality - * - * @private - */ - keyv; +class KeyValueStore { constructor(options) { - this.keyv = new Keyv(options.uri, options); + this.keyv = new keyv_1.Keyv(options.uri, options); } - async get(key) { - const result = await this.keyv.get(key, { raw: false }); - if (result === null || result === undefined) { - return undefined; - } - return result; + get(key) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.keyv.get(key, { raw: false }); + if (result === null || result === undefined) { + return undefined; + } + return result; + }); } - async getAsValueData(key) { - const result = await this.keyv.get(key, { raw: true }); - if (result === null || result === undefined) { - // We always return a ValueData object for this method - return { value: undefined, expires: undefined }; - } - return this.toDeserializedValueData(result); + getAsValueData(key) { + return __awaiter(this, void 0, void 0, function* () { + const result = yield this.keyv.get(key, { raw: true }); + if (result === null || result === undefined) { + // We always return a ValueData object for this method + return { value: undefined, expires: undefined }; + } + return this.toDeserializedValueData(result); + }); } - async getMany(keys) { - if (!keys || keys.length === 0) { - return []; - } - let result = await this.keyv.getMany(keys, { raw: false }); - // Making sure we return the same array length as the amount of key(s) passed in - if (result === null || result === undefined || result.length === 0) { - result = new Array(); - for (const key of keys) { - result.push(undefined); + getMany(keys) { + return __awaiter(this, void 0, void 0, function* () { + if (!keys || keys.length === 0) { + return []; } - } - return result.map((v) => (!!v ? v : undefined)); + let result = yield this.keyv.getMany(keys, { raw: false }); + // Making sure we return the same array length as the amount of key(s) passed in + if (result === null || result === undefined || result.length === 0) { + result = new Array(); + keys.forEach(() => result.push(undefined)); + } + return result.map((v) => (!!v ? v : undefined)); + }); } - async getManyAsValueData(keys) { - if (!keys || keys.length === 0) { - return []; - } - let result = await this.keyv.getMany(keys, { raw: true }); - // Making sure we return the same array length as the amount of key(s) passed in - if (result === null || result === undefined || result.length === 0) { - result = new Array(); - for (const key of keys) { - result.push({ value: undefined, expires: undefined }); + getManyAsValueData(keys) { + return __awaiter(this, void 0, void 0, function* () { + if (!keys || keys.length === 0) { + return []; } - } - return result.map((v) => !!v ? this.toDeserializedValueData(v) : { value: undefined, expires: undefined }); + let result = yield this.keyv.getMany(keys, { raw: true }); + // Making sure we return the same array length as the amount of key(s) passed in + if (result === null || result === undefined || result.length === 0) { + result = new Array(); + keys.forEach(() => result.push({ value: undefined, expires: undefined })); + } + return result.map((v) => !!v ? this.toDeserializedValueData(v) : { value: undefined, expires: undefined }); + }); } - async set(key, value, ttl) { - return this.keyv.set(key, value, ttl).then(() => this.getAsValueData(key)); + set(key, value, ttl) { + return __awaiter(this, void 0, void 0, function* () { + return this.keyv.set(key, value, ttl).then(() => this.getAsValueData(key)); + }); } - async has(key) { - return this.keyv.has(key); + has(key) { + return __awaiter(this, void 0, void 0, function* () { + return this.keyv.has(key); + }); } - async delete(key) { - return this.keyv.delete(key); + delete(key) { + return __awaiter(this, void 0, void 0, function* () { + return this.keyv.delete(key); + }); } - async deleteMany(keys) { - return Promise.all(keys.map(key => this.keyv.delete(key))); + deleteMany(keys) { + return __awaiter(this, void 0, void 0, function* () { + return Promise.all(keys.map((key) => this.keyv.delete(key))); + }); } - async clear() { - return this.keyv.clear().then(() => this); + clear() { + return __awaiter(this, void 0, void 0, function* () { + return this.keyv.clear().then(() => this); + }); } - async disconnect() { - return this.keyv.disconnect(); + disconnect() { + return __awaiter(this, void 0, void 0, function* () { + return this.keyv.disconnect(); + }); } // Public so parties using the kv store directly can add listeners if they want - async kvStoreOn(args) { - this.keyv.on(args.eventName, args.listener); - return this; + kvStoreOn(args) { + return __awaiter(this, void 0, void 0, function* () { + this.keyv.on(args.eventName, args.listener); + return this; + }); } toDeserializedValueData(result) { if (result === null || result === undefined) { @@ -95,4 +119,5 @@ export class KeyValueStore { return result; } } +exports.KeyValueStore = KeyValueStore; //# sourceMappingURL=key-value-store.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-types.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-types.js index 810a426b7..08441176b 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-types.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/key-value-types.js @@ -1,2 +1,3 @@ -export {}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=key-value-types.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv-types.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv-types.js index f75985afb..ebc0559f9 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv-types.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv-types.js @@ -1,2 +1,3 @@ -export {}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=keyv-types.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts index c2281c2f6..1cc228b79 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts @@ -1,6 +1,6 @@ /// -import EventEmitter from 'events'; -import { KeyvDeserializedData, KeyvOptions, KeyvStore, KeyvStoredData } from './keyv-types.js'; +import { EventEmitter } from 'events'; +import { KeyvDeserializedData, KeyvOptions, KeyvStore, KeyvStoredData } from './keyv-types'; /** * Please note that this is code adapted from @link https://github.com/jaredwray/keyv to support Typescript and ESM in Veramo * diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.js index 49657e3eb..8edb92f6a 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/keyv/keyv.js @@ -1,5 +1,39 @@ -import EventEmitter from 'events'; -import JSONB from 'json-buffer'; +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Keyv = void 0; +const events_1 = require("events"); +const json_buffer_1 = __importDefault(require("json-buffer")); /** * Please note that this is code adapted from @link https://github.com/jaredwray/keyv to support Typescript and ESM in Veramo * @@ -18,24 +52,15 @@ import JSONB from 'json-buffer'; * * We welcome any new storage modules */ -export class Keyv extends EventEmitter { - opts; - namespace; - iterator; +class Keyv extends events_1.EventEmitter { constructor(uri, options) { super(); - const emitErrors = options?.emitErrors === undefined ? true : options.emitErrors; - uri = uri ?? options?.uri; + const emitErrors = (options === null || options === void 0 ? void 0 : options.emitErrors) === undefined ? true : options.emitErrors; + uri = uri !== null && uri !== void 0 ? uri : options === null || options === void 0 ? void 0 : options.uri; /*if (!uri) { throw Error('No URI provided') }*/ - this.opts = { - namespace: 'keyv', - serialize: JSONB.stringify, - deserialize: JSONB.parse, - ...(typeof uri === 'string' ? { uri } : uri), - ...options, - }; + this.opts = Object.assign(Object.assign({ namespace: 'keyv', serialize: json_buffer_1.default.stringify, deserialize: json_buffer_1.default.parse }, (typeof uri === 'string' ? { uri } : uri)), options); if (!this.opts.store) { if (typeof uri !== 'string') { this.opts.store = uri; @@ -57,20 +82,40 @@ export class Keyv extends EventEmitter { } this.opts.store.namespace = this.opts.namespace || 'keyv'; this.namespace = this.opts.store.namespace; - const generateIterator = (iterator, keyv) => async function* () { - for await (const [key, raw] of typeof iterator === 'function' - ? iterator(keyv.store.namespace) - : iterator) { - const data = await keyv.deserialize(raw); - if (keyv.store.namespace && !key.includes(keyv.store.namespace)) { - continue; + const generateIterator = (iterator, keyv) => function () { + return __asyncGenerator(this, arguments, function* () { + var _a, e_1, _b, _c; + try { + for (var _d = true, _e = __asyncValues(typeof iterator === 'function' + ? iterator(keyv.store.namespace) + : iterator), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) { + _c = _f.value; + _d = false; + try { + const [key, raw] = _c; + const data = yield __await(keyv.deserialize(raw)); + if (keyv.store.namespace && !key.includes(keyv.store.namespace)) { + continue; + } + if (data && typeof data.expires === 'number' && Date.now() > data.expires) { + keyv.delete(key); + continue; + } + yield yield __await([keyv._getKeyUnprefix(key), data === null || data === void 0 ? void 0 : data.value]); + } + finally { + _d = true; + } + } } - if (data && typeof data.expires === 'number' && Date.now() > data.expires) { - keyv.delete(key); - continue; + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e)); + } + finally { if (e_1) throw e_1.error; } } - yield [keyv._getKeyUnprefix(key), data?.value]; - } + }); }; // Attach iterators // @ts-ignore @@ -104,86 +149,92 @@ export class Keyv extends EventEmitter { _getKeyUnprefix(key) { return key.split(':').splice(1).join(':'); } - async getMany(keys, options) { - const keyPrefixed = this._getKeyPrefixArray(keys); - let promise; - if (this.store.getMany !== undefined) { - promise = this.store.getMany(keyPrefixed, options); //.then(value => !!value ? value.values() : undefined) - // todo: Probably wise to check expired ValueData here, if the getMany does not implement this feature itself! - } - else { - promise = Promise.all(keyPrefixed.map(k => this.store.get(k, options))); - } - const allValues = Promise.resolve(promise); - const results = []; - return Promise.resolve(allValues).then(all => { - keys.forEach((key, index) => { - const data = all[index]; - let result = typeof data === 'string' - ? this.deserialize(data) - : !!data && this.opts.compression + getMany(keys, options) { + return __awaiter(this, void 0, void 0, function* () { + const keyPrefixed = this._getKeyPrefixArray(keys); + let promise; + if (this.store.getMany !== undefined) { + promise = this.store.getMany(keyPrefixed, options); //.then(value => !!value ? value.values() : undefined) + // todo: Probably wise to check expired ValueData here, if the getMany does not implement this feature itself! + } + else { + promise = Promise.all(keyPrefixed.map((k) => this.store.get(k, options))); + } + const allValues = Promise.resolve(promise); + const results = []; + return Promise.resolve(allValues) + .then((all) => { + keys.forEach((key, index) => { + const data = all[index]; + let result = typeof data === 'string' ? this.deserialize(data) - : data; - if (result && - typeof result === 'object' && - 'expires' in result && - typeof result.expires === 'number' && - Date.now() > result.expires) { - this.delete(key); - result = undefined; - } - const final = (options && options.raw - ? result - : result && typeof result === 'object' && 'value' in result - ? result.value - : result); - results.push(final); - }); - }).then(() => Promise.all(results)); + : !!data && this.opts.compression + ? this.deserialize(data) + : data; + if (result && + typeof result === 'object' && + 'expires' in result && + typeof result.expires === 'number' && + Date.now() > result.expires) { + this.delete(key); + result = undefined; + } + const final = (options && options.raw + ? result + : result && typeof result === 'object' && 'value' in result + ? result.value + : result); + results.push(final); + }); + }) + .then(() => Promise.all(results)); + }); } - async get(key, options) { - const isArray = Array.isArray(key); - return Promise.resolve() - .then(() => isArray - ? this.getMany(this._getKeyPrefixArray(key), options) - : this.store.get(this._getKeyPrefix(key))) - .then((data) => typeof data === 'string' - ? this.deserialize(data) - : this.opts.compression + get(key, options) { + return __awaiter(this, void 0, void 0, function* () { + const isArray = Array.isArray(key); + return Promise.resolve() + .then(() => isArray + ? this.getMany(this._getKeyPrefixArray(key), options) + : this.store.get(this._getKeyPrefix(key))) + .then((data) => typeof data === 'string' ? this.deserialize(data) - : data) - .then((data) => { - if (data === undefined || data === null) { - return undefined; - } - const rows = Array.isArray(data) ? data : [data]; - if (isArray) { - const result = []; - for (let row of rows) { - if (row === undefined || row === null) { - result.push(undefined); - continue; - } - if (this.isExpired(row)) { - this.delete(key).then(() => undefined); - result.push(undefined); - } - else { - result.push(options && options.raw ? row : toValue(row)); + : this.opts.compression + ? this.deserialize(data) + : data) + .then((data) => { + if (data === undefined || data === null) { + return undefined; + } + const rows = Array.isArray(data) ? data : [data]; + if (isArray) { + const result = []; + for (let row of rows) { + if (row === undefined || row === null) { + result.push(undefined); + continue; + } + if (this.isExpired(row)) { + this.delete(key).then(() => undefined); + result.push(undefined); + } + else { + result.push(options && options.raw ? row : toValue(row)); + } } + return result; } - return result; - } - else if (!Array.isArray(data)) { - if (this.isExpired(data)) { - return this.delete(key).then(() => undefined); + else if (!Array.isArray(data)) { + if (this.isExpired(data)) { + return this.delete(key).then(() => undefined); + } } - } - return options && options.raw - ? data - : Array.isArray(data) - ? data.map((d) => toValue(d)) - : toValue(data); + return options && options.raw + ? data + : Array.isArray(data) + ? data.map((d) => toValue(d)) + : toValue(data); + }); }); } isExpired(data) { @@ -230,18 +281,20 @@ export class Keyv extends EventEmitter { } return Promise.allSettled(promises).then((values) => values.every((x) => x.valueOf() === true)); } - async clear() { - return Promise.resolve().then(() => this.store.clear()); + clear() { + return __awaiter(this, void 0, void 0, function* () { + return Promise.resolve().then(() => this.store.clear()); + }); } has(key) { const keyPrefixed = this._getKeyPrefix(key); - return Promise.resolve().then(async () => { + return Promise.resolve().then(() => __awaiter(this, void 0, void 0, function* () { if (typeof this.store.has === 'function') { return this.store.has(keyPrefixed); } - const value = await this.store.get(keyPrefixed); + const value = yield this.store.get(keyPrefixed); return value !== undefined; - }); + })); } disconnect() { if (typeof this.store.disconnect === 'function') { @@ -249,6 +302,7 @@ export class Keyv extends EventEmitter { } } } +exports.Keyv = Keyv; const iterableAdapters = ['sqlite', 'postgres', 'mysql', 'mongo', 'redis', 'tiered']; function toValue(input) { return input !== null && typeof input === 'object' && 'value' in input ? input.value : input; diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts index 27a72e3b9..dac261f0c 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts @@ -1,3 +1,3 @@ -export * from './tiered/index.js'; -export * from './typeorm/index.js'; +export * from './tiered/index'; +export * from './typeorm/index'; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.js index 6bf368909..3d50061a3 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/index.js @@ -1,3 +1,19 @@ -export * from './tiered/index.js'; -export * from './typeorm/index.js'; +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./tiered/index"), exports); +__exportStar(require("./typeorm/index"), exports); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts index a4f64c32f..47410d168 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts @@ -1,8 +1,8 @@ /// -import EventEmitter from 'events'; -import type { Options, Options_ } from './types.js'; -import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types.js'; -import { IKeyValueStoreAdapter } from '../../key-value-types.js'; +import { EventEmitter } from 'events'; +import type { Options, Options_ } from './types'; +import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types'; +import { IKeyValueStoreAdapter } from '../../key-value-types'; export declare class KeyValueTieredStoreAdapter extends EventEmitter implements KeyvStore, IKeyValueStoreAdapter { opts: Options_; remote: KeyvStore; diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js index 671449f75..94b71ef27 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js @@ -1,124 +1,193 @@ -import EventEmitter from 'events'; -import { Keyv } from '../../keyv/keyv.js'; -export class KeyValueTieredStoreAdapter extends EventEmitter { - opts; - remote; - local; - iterationLimit; - namespace; - constructor({ remote, local, ...options }) { +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KeyValueTieredStoreAdapter = void 0; +const events_1 = require("events"); +const keyv_1 = require("../../keyv/keyv"); +class KeyValueTieredStoreAdapter extends events_1.EventEmitter { + constructor(_a) { + var { remote, local } = _a, options = __rest(_a, ["remote", "local"]); super(); - this.opts = { - validator: () => true, - dialect: 'tiered', - ...options, - }; + this.opts = Object.assign({ validator: () => true, dialect: 'tiered' }, options); // todo: since we are instantiating a new Keyv object in case we encounter a map, the key prefix applied twice, given it will be part of a an outer keyv object as well. // Probably wise to simply create a Map Store class. As it is an in memory construct, and will work in terms of resolution it does not have highest priority - this.local = (isMap(local) ? new Keyv(local) : local); - this.remote = (isMap(remote) ? new Keyv(remote) : remote); + this.local = (isMap(local) ? new keyv_1.Keyv(local) : local); + this.remote = (isMap(remote) ? new keyv_1.Keyv(remote) : remote); this.namespace = this.local.namespace; } - async get(key, options) { - if (Array.isArray(key)) { - return await this.getMany(key, options); - } - const localResult = await this.local.get(key, options); - if (localResult === undefined || !this.opts.validator(localResult, key)) { - const remoteResult = (await this.remote.get(key, options)); - if (remoteResult !== localResult) { - const value = (!!remoteResult - ? typeof remoteResult === 'object' && 'value' in remoteResult - ? remoteResult.value - : remoteResult - : undefined); - const ttl = !!remoteResult && - typeof remoteResult === 'object' && - 'expires' in remoteResult && - remoteResult.expires - ? remoteResult.expires - Date.now() - : undefined; - if (!ttl || ttl > 0) { - await this.local.set(key, value, ttl); - } - else { - this.local.delete(key); + get(key, options) { + return __awaiter(this, void 0, void 0, function* () { + if (Array.isArray(key)) { + return yield this.getMany(key, options); + } + const localResult = (yield this.local.get(key, options)); + if (localResult === undefined || !this.opts.validator(localResult, key)) { + const remoteResult = yield this.remote.get(key, options); + if (remoteResult !== localResult) { + const value = (!!remoteResult + ? typeof remoteResult === 'object' && 'value' in remoteResult + ? remoteResult.value + : remoteResult + : undefined); + const ttl = !!remoteResult && + typeof remoteResult === 'object' && + 'expires' in remoteResult && + remoteResult.expires + ? remoteResult.expires - Date.now() + : undefined; + if (!ttl || ttl > 0) { + yield this.local.set(key, value, ttl); + } + else { + this.local.delete(key); + } } + return remoteResult; } - return remoteResult; - } - return localResult; + return localResult; + }); } - async getMany(keys, options) { - const promises = []; - for (const key of keys) { - promises.push(this.get(key, options)); - } - const values = await Promise.all(promises); - const data = []; - for (const value of values) { - data.push(value); - } - return data; + getMany(keys, options) { + return __awaiter(this, void 0, void 0, function* () { + const promises = []; + for (const key of keys) { + promises.push(this.get(key, options)); + } + const values = yield Promise.all(promises); + const data = []; + for (const value of values) { + data.push(value); + } + return data; + }); } - async set(key, value, ttl) { - const toSet = ['local', 'remote']; - return Promise.all(toSet.map(async (store) => this[store].set(key, value, ttl))); + set(key, value, ttl) { + return __awaiter(this, void 0, void 0, function* () { + const toSet = ['local', 'remote']; + return Promise.all(toSet.map((store) => __awaiter(this, void 0, void 0, function* () { return this[store].set(key, value, ttl); }))); + }); } - async clear() { - const toClear = ['local']; - if (!this.opts.localOnly) { - toClear.push('remote'); - } - await Promise.all(toClear.map(async (store) => this[store].clear())); - return undefined; + clear() { + return __awaiter(this, void 0, void 0, function* () { + const toClear = ['local']; + if (!this.opts.localOnly) { + toClear.push('remote'); + } + yield Promise.all(toClear.map((store) => __awaiter(this, void 0, void 0, function* () { return this[store].clear(); }))); + return undefined; + }); } - async delete(key) { - const toDelete = ['local']; - if (!this.opts.localOnly) { - toDelete.push('remote'); - } - const deleted = await Promise.all(toDelete.map(async (store) => this[store].delete(key))); - return deleted.every(Boolean); + delete(key) { + return __awaiter(this, void 0, void 0, function* () { + const toDelete = ['local']; + if (!this.opts.localOnly) { + toDelete.push('remote'); + } + const deleted = yield Promise.all(toDelete.map((store) => __awaiter(this, void 0, void 0, function* () { return this[store].delete(key); }))); + return deleted.every(Boolean); + }); } - async deleteMany(keys) { - const promises = []; - for (const key of keys) { - promises.push(this.delete(key)); - } - const values = await Promise.all(promises); - return values.every(Boolean); + deleteMany(keys) { + return __awaiter(this, void 0, void 0, function* () { + const promises = []; + for (const key of keys) { + promises.push(this.delete(key)); + } + const values = yield Promise.all(promises); + return values.every(Boolean); + }); } - async has(key) { - let response; - if (typeof this.local.has === 'function') { - response = this.local.has(key); - } - else { - const value = await this.local.get(key); - response = value !== undefined; - } - if (!response || !this.opts.validator(response, key)) { - if (typeof this.remote.has === 'function') { - response = this.remote.has(key); + has(key) { + return __awaiter(this, void 0, void 0, function* () { + let response; + if (typeof this.local.has === 'function') { + response = this.local.has(key); } else { - const value = await this.remote.get(key); + const value = yield this.local.get(key); response = value !== undefined; } - } - return response; + if (!response || !this.opts.validator(response, key)) { + if (typeof this.remote.has === 'function') { + response = this.remote.has(key); + } + else { + const value = yield this.remote.get(key); + response = value !== undefined; + } + } + return response; + }); } - async *iterator(namespace) { - const limit = Number.parseInt(this.iterationLimit, 10) || 10; - this.remote.opts.iterationLimit = limit; - if (this.remote && typeof this.remote.iterator === 'function') { - for await (const entries of this.remote.iterator(namespace)) { - yield entries; + iterator(namespace) { + return __asyncGenerator(this, arguments, function* iterator_1() { + var _a, e_1, _b, _c; + const limit = Number.parseInt(this.iterationLimit, 10) || 10; + this.remote.opts.iterationLimit = limit; + if (this.remote && typeof this.remote.iterator === 'function') { + try { + for (var _d = true, _e = __asyncValues(this.remote.iterator(namespace)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) { + _c = _f.value; + _d = false; + try { + const entries = _c; + yield yield __await(entries); + } + finally { + _d = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e)); + } + finally { if (e_1) throw e_1.error; } + } } - } + }); } } +exports.KeyValueTieredStoreAdapter = KeyValueTieredStoreAdapter; function isMap(map) { if (map instanceof Map) { return true; diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts index bff526f7b..f590a38ea 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts @@ -1,4 +1,4 @@ -import { IKeyValueStoreAdapter } from '../../key-value-types.js'; +import { IKeyValueStoreAdapter } from '../../key-value-types'; export type Options = { local: IKeyValueStoreAdapter | Map; remote: IKeyValueStoreAdapter | Map; diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js index 718fd38ae..11e638d1e 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js @@ -1,2 +1,3 @@ -export {}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js index 07e610788..512360fc6 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js @@ -1,3 +1,4 @@ +"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); @@ -7,26 +8,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KeyValueStoreEntity = void 0; +const typeorm_1 = require("typeorm"); /** * * @beta This API may change without a BREAKING CHANGE notice. */ -let KeyValueStoreEntity = class KeyValueStoreEntity extends BaseEntity { - // @ts-ignore - key; - // @ts-ignore - data; - expires; +let KeyValueStoreEntity = class KeyValueStoreEntity extends typeorm_1.BaseEntity { }; __decorate([ - PrimaryColumn() + (0, typeorm_1.PrimaryColumn)() // @ts-ignore , __metadata("design:type", String) ], KeyValueStoreEntity.prototype, "key", void 0); __decorate([ - Column({ + (0, typeorm_1.Column)({ type: 'text', }) // @ts-ignore @@ -34,7 +32,7 @@ __decorate([ __metadata("design:type", String) ], KeyValueStoreEntity.prototype, "data", void 0); KeyValueStoreEntity = __decorate([ - Entity('keyvaluestore') + (0, typeorm_1.Entity)('keyvaluestore') ], KeyValueStoreEntity); -export { KeyValueStoreEntity }; +exports.KeyValueStoreEntity = KeyValueStoreEntity; //# sourceMappingURL=keyValueStoreEntity.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts index 6c230cf46..6b8f1bc9d 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts @@ -1,11 +1,13 @@ /// -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import { OrPromise } from '@veramo/utils'; import { DataSource } from 'typeorm'; -import { KeyValueTypeORMOptions, Options_ } from './types.js'; -import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types.js'; -import { IKeyValueStoreAdapter } from '../../key-value-types.js'; -export { KeyValueTypeORMOptions } from './types.js'; +import { KeyValueTypeORMOptions, Options_ } from './types'; +import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types'; +import { IKeyValueStoreAdapter } from '../../key-value-types'; +export { KeyValueTypeORMOptions } from './types'; +export { KeyValueStoreEntity } from './entities/keyValueStoreEntity'; +export { kvStoreMigrations } from './migrations'; export declare class KeyValueTypeORMStoreAdapter extends EventEmitter implements KeyvStore, IKeyValueStoreAdapter { private readonly dbConnection; readonly namespace: string; diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js index 3cbeb321e..b7d730863 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js @@ -1,107 +1,136 @@ -import EventEmitter from 'events'; -import { In, Like } from 'typeorm'; -import { KeyValueStoreEntity } from './entities/keyValueStoreEntity.js'; -import JSONB from 'json-buffer'; -export class KeyValueTypeORMStoreAdapter extends EventEmitter { - dbConnection; - namespace; - opts; +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getConnectedDb = exports.KeyValueTypeORMStoreAdapter = exports.kvStoreMigrations = exports.KeyValueStoreEntity = void 0; +const events_1 = require("events"); +const typeorm_1 = require("typeorm"); +const keyValueStoreEntity_1 = require("./entities/keyValueStoreEntity"); +const json_buffer_1 = __importDefault(require("json-buffer")); +var keyValueStoreEntity_2 = require("./entities/keyValueStoreEntity"); +Object.defineProperty(exports, "KeyValueStoreEntity", { enumerable: true, get: function () { return keyValueStoreEntity_2.KeyValueStoreEntity; } }); +var migrations_1 = require("./migrations"); +Object.defineProperty(exports, "kvStoreMigrations", { enumerable: true, get: function () { return migrations_1.kvStoreMigrations; } }); +class KeyValueTypeORMStoreAdapter extends events_1.EventEmitter { constructor(options) { super(); this.dbConnection = options.dbConnection; this.namespace = options.namespace || 'keyv'; - this.opts = { - validator: () => true, - dialect: 'typeorm', - serialize: JSONB.stringify, - deserialize: JSONB.parse, - ...options, - }; + this.opts = Object.assign({ validator: () => true, dialect: 'typeorm', serialize: json_buffer_1.default.stringify, deserialize: json_buffer_1.default.parse }, options); } - async get(key, options) { - if (Array.isArray(key)) { - return this.getMany(key, options); - } - const connection = await getConnectedDb(this.dbConnection); - const result = await connection.getRepository(KeyValueStoreEntity).findOneBy({ - key, + get(key, options) { + return __awaiter(this, void 0, void 0, function* () { + if (Array.isArray(key)) { + return this.getMany(key, options); + } + const connection = yield getConnectedDb(this.dbConnection); + const result = yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).findOneBy({ + key, + }); + return (options === null || options === void 0 ? void 0 : options.raw) !== true || !result ? result === null || result === void 0 ? void 0 : result.data : { value: result === null || result === void 0 ? void 0 : result.data, expires: result === null || result === void 0 ? void 0 : result.expires }; }); - return options?.raw !== true || !result - ? result?.data - : { value: result?.data, expires: result?.expires }; } - async getMany(keys, options) { - const connection = await getConnectedDb(this.dbConnection); - const results = await connection.getRepository(KeyValueStoreEntity).findBy({ - key: In(keys), - }); - const values = keys.map(async (key) => { - const result = results.find(result => result.key === key); - return options?.raw !== true || !result - ? result?.data - : { - value: result?.data ? (await this.opts.deserialize(result.data))?.value : undefined, - expires: result?.expires, - }; + getMany(keys, options) { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + const results = yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).findBy({ + key: (0, typeorm_1.In)(keys), + }); + const values = keys.map((key) => __awaiter(this, void 0, void 0, function* () { + var _a; + const result = results.find((result) => result.key === key); + return (options === null || options === void 0 ? void 0 : options.raw) !== true || !result + ? result === null || result === void 0 ? void 0 : result.data + : { + value: (result === null || result === void 0 ? void 0 : result.data) ? (_a = (yield this.opts.deserialize(result.data))) === null || _a === void 0 ? void 0 : _a.value : undefined, + expires: result === null || result === void 0 ? void 0 : result.expires, + }; + })); + return Promise.all(values); }); - return Promise.all(values); } - async set(key, value, ttl) { - const connection = await getConnectedDb(this.dbConnection); - const entity = new KeyValueStoreEntity(); - entity.key = key; - entity.data = value; - entity.expires = ttl; - await connection.getRepository(KeyValueStoreEntity).save(entity); - return { value: value, expires: ttl }; + set(key, value, ttl) { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + const entity = new keyValueStoreEntity_1.KeyValueStoreEntity(); + entity.key = key; + entity.data = value; + entity.expires = ttl; + yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).save(entity); + return { value: value, expires: ttl }; + }); } - async delete(key) { - if (Array.isArray(key)) { - return this.deleteMany(key); - } - const connection = await getConnectedDb(this.dbConnection); - const result = await connection.getRepository(KeyValueStoreEntity).delete({ key }); - return result.affected === 1; + delete(key) { + return __awaiter(this, void 0, void 0, function* () { + if (Array.isArray(key)) { + return this.deleteMany(key); + } + const connection = yield getConnectedDb(this.dbConnection); + const result = yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).delete({ key }); + return result.affected === 1; + }); } - async deleteMany(keys) { - const connection = await getConnectedDb(this.dbConnection); - const results = await connection.getRepository(KeyValueStoreEntity).delete({ - key: In(keys), + deleteMany(keys) { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + const results = yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).delete({ + key: (0, typeorm_1.In)(keys), + }); + return !!results.affected && results.affected >= 1; }); - return !!results.affected && results.affected >= 1; } - async clear() { - const connection = await getConnectedDb(this.dbConnection); - await connection.getRepository(KeyValueStoreEntity).delete({ - key: Like(`${this.namespace}:%`), + clear() { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).delete({ + key: (0, typeorm_1.Like)(`${this.namespace}:%`), + }); }); } - async has(key) { - const connection = await getConnectedDb(this.dbConnection); - const result = await connection.getRepository(KeyValueStoreEntity).countBy({ - key, + has(key) { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + const result = yield connection.getRepository(keyValueStoreEntity_1.KeyValueStoreEntity).countBy({ + key, + }); + return result === 1; }); - return result === 1; } - async disconnect() { - const connection = await getConnectedDb(this.dbConnection); - connection.destroy(); + disconnect() { + return __awaiter(this, void 0, void 0, function* () { + const connection = yield getConnectedDb(this.dbConnection); + connection.destroy(); + }); } } +exports.KeyValueTypeORMStoreAdapter = KeyValueTypeORMStoreAdapter; /** * Ensures that the provided DataSource is connected. * * @param dbConnection - a TypeORM DataSource or a Promise that resolves to a DataSource */ -export async function getConnectedDb(dbConnection) { - if (dbConnection instanceof Promise) { - return await dbConnection; - } - else if (!dbConnection.isInitialized) { - return await dbConnection.initialize(); - } - else { - return dbConnection; - } +function getConnectedDb(dbConnection) { + return __awaiter(this, void 0, void 0, function* () { + if (dbConnection instanceof Promise) { + return yield dbConnection; + } + else if (!dbConnection.isInitialized) { + return yield dbConnection.initialize(); + } + else { + return dbConnection; + } + }); } +exports.getConnectedDb = getConnectedDb; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js index 57decf0c6..e06e0aad0 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js @@ -1,41 +1,60 @@ -import { Table } from 'typeorm'; -import Debug from 'debug'; -const debug = Debug('veramo:data-store:initial-migration'); +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateKVDatabaseMigration = void 0; +const typeorm_1 = require("typeorm"); +const debug_1 = __importDefault(require("debug")); +const debug = (0, debug_1.default)('veramo:kv-store:initial-migration'); /** * Create the database layout for Veramo 3.0 * * @public */ -export class CreateKVDatabaseMigration { - _tableName; - name; +class CreateKVDatabaseMigration { constructor(tableName) { this._tableName = tableName || 'keyvaluestore'; this.name = `CreateKVDatabase${tableName}1680297189001`; } - async up(queryRunner) { - function getTableName(givenName) { - return (queryRunner.connection.entityMetadatas.find((meta) => meta.givenTableName === givenName)?.tableName || - givenName); - } - debug(`creating ${this._tableName} table`); - // CREATE TABLE "keyvaluestore" ("key" varchar PRIMARY KEY NOT NULL, "data" text NOT NULL) - await queryRunner.createTable(new Table({ - name: getTableName(this._tableName), - columns: [ - { name: 'key', type: 'varchar', isPrimary: true }, - { name: 'data', type: 'text', isNullable: false }, - ], - indices: [ - { - columnNames: ['key'], - isUnique: true, - }, - ], - }), true); + up(queryRunner) { + return __awaiter(this, void 0, void 0, function* () { + function getTableName(givenName) { + var _a; + return (((_a = queryRunner.connection.entityMetadatas.find((meta) => meta.givenTableName === givenName)) === null || _a === void 0 ? void 0 : _a.tableName) || + givenName); + } + debug(`creating ${this._tableName} table`); + // CREATE TABLE "keyvaluestore" ("key" varchar PRIMARY KEY NOT NULL, "data" text NOT NULL) + yield queryRunner.createTable(new typeorm_1.Table({ + name: getTableName(this._tableName), + columns: [ + { name: 'key', type: 'varchar', isPrimary: true }, + { name: 'data', type: 'text', isNullable: false }, + ], + indices: [ + { + columnNames: ['key'], + isUnique: true, + }, + ], + }), true); + }); } - async down(queryRunner) { - throw new Error('illegal_operation: cannot roll back initial migration'); + down(queryRunner) { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('illegal_operation: cannot roll back initial migration'); + }); } } +exports.CreateKVDatabaseMigration = CreateKVDatabaseMigration; //# sourceMappingURL=1.createKVDatabase.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js index 5da7b509f..20f7086b5 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js @@ -1,4 +1,7 @@ -import { CreateKVDatabaseMigration } from './1.createKVDatabase'; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.kvStoreMigrations = void 0; +const _1_createKVDatabase_1 = require("./1.createKVDatabase"); /** * The migrations array that SHOULD be used when initializing a TypeORM database connection. * @@ -6,7 +9,5 @@ import { CreateKVDatabaseMigration } from './1.createKVDatabase'; * * @public */ -export const kvStoreMigrations = [ - CreateKVDatabaseMigration -]; +exports.kvStoreMigrations = [_1_createKVDatabase_1.CreateKVDatabaseMigration]; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js index 718fd38ae..11e638d1e 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js @@ -1,2 +1,3 @@ -export {}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/tsdoc-metadata.json b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/tsdoc-metadata.json index b76bafebf..52f4aa531 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/build/tsdoc-metadata.json +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/build/tsdoc-metadata.json @@ -5,7 +5,7 @@ "toolPackages": [ { "packageName": "@microsoft/api-extractor", - "packageVersion": "7.33.8" + "packageVersion": "7.33.7" } ] } diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/package.json b/packages/presentation-exchange/.yalc/@veramo/kv-store/package.json index 3f1c19658..c682bc18a 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/package.json +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/package.json @@ -1,9 +1,8 @@ { "name": "@veramo/kv-store", "description": "Veramo Key Value Store plugin", - "version": "5.1.2", + "version": "4.3.0", "main": "build/index.js", - "exports": "./build/index.js", "types": "build/index.d.ts", "scripts": { "build": "tsc", @@ -12,8 +11,7 @@ "extract-api": "node ../cli/bin/veramo.js dev extract-api" }, "dependencies": { - "@veramo/core-types": "5.1.2", - "@veramo/utils": "5.1.2", + "@veramo/utils": "4.3.0", "debug": "^4.3.4", "events": "^3.3.0", "json-buffer": "^3.0.1", @@ -37,10 +35,5 @@ "Key Value Store", "keyv" ], - "type": "module", - "moduleDirectories": [ - "node_modules", - "src" - ], - "yalcSig": "09469f925723eeecd041caefbbb3346e" + "yalcSig": "bfd8bfc89f0050ba31664b2d7c3efc7a" } diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts index 42d1c2f55..61ba910d3 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts @@ -1,14 +1,13 @@ import KeyvSqlite from '@keyv/sqlite' import timekeeper from 'timekeeper' -import { Keyv } from '../keyv/keyv.js' +import { Keyv } from '../keyv/keyv' import { DataSource } from 'typeorm' import { KeyValueStoreEntity } from '../store-adapters/typeorm/entities/keyValueStoreEntity' import { KeyValueTieredStoreAdapter, KeyValueTypeORMStoreAdapter } from '../store-adapters' import { KeyvOptions } from '../keyv/keyv-types' import { kvStoreMigrations } from '../store-adapters/typeorm/migrations' - let dbConnection: DataSource beforeEach(async () => { dbConnection = await new DataSource({ @@ -29,7 +28,6 @@ afterEach(async () => { } catch (error) { // the disconnect test will close the DB anyway } - }) describe('keyv MAP store', () => { it('should respect ttl', async () => { @@ -45,12 +43,9 @@ describe('keyv MAP store', () => { const store = new Map() await testAllKeyvMethods(store) }) - - }) describe('keyv sqlite store', () => { - it('should respect ttl', async () => { const keyv = new Keyv(new KeyvSqlite()) await keyv.set('key', 'value', 100) @@ -142,18 +137,13 @@ describe('keyv TypeORM store', () => { }) describe('keyv tiered store', () => { - - - - - it('should respect ttl', async () => { const local: Map = new Map() const remote = new KeyValueTypeORMStoreAdapter({ dbConnection }) const options: KeyvOptions = { namespace: 'example', - store: new KeyValueTieredStoreAdapter({ local, remote }) + store: new KeyValueTieredStoreAdapter({ local, remote }), } const keyv = new Keyv(undefined, options) await keyv.set('key', 'value', 100) @@ -169,18 +159,15 @@ describe('keyv tiered store', () => { const options: KeyvOptions = { namespace: 'example', - store: new KeyValueTieredStoreAdapter({ local, remote }) + store: new KeyValueTieredStoreAdapter({ local, remote }), } const store = new Keyv(undefined, options) // No raw match test, as remote and local have different raw values await testAllKeyvMethods(store, false) }) - - }) - async function testAllKeyvMethods(store: any, rawMatchTest = true) { const keyv = new Keyv(store) @@ -189,17 +176,20 @@ async function testAllKeyvMethods(store: any, rawMatchTest = true) { await expect(keyv.set('key2', 'value2')).resolves.toEqual(true) // get value by key - await expect(keyv.get('key1', {raw: false})).resolves.toEqual('value1') - await expect(keyv.get('key1', {raw: true})).resolves.toMatchObject({value: 'value1'}) + await expect(keyv.get('key1', { raw: false })).resolves.toEqual('value1') + await expect(keyv.get('key1', { raw: true })).resolves.toMatchObject({ value: 'value1' }) // get value by non-existing key - await expect(keyv.get('key3', {raw: false})).resolves.toBeUndefined() - await expect(keyv.get('key3', {raw: true})).resolves.toBeUndefined() + await expect(keyv.get('key3', { raw: false })).resolves.toBeUndefined() + await expect(keyv.get('key3', { raw: true })).resolves.toBeUndefined() // Get many as non-raw and raw - await expect(keyv.getMany(['key1', 'key2'], {raw: false})).resolves.toMatchObject(['value1', 'value2']) + await expect(keyv.getMany(['key1', 'key2'], { raw: false })).resolves.toMatchObject(['value1', 'value2']) if (rawMatchTest) { - await expect(keyv.getMany(['key1', 'key2'], { raw: true })).resolves.toMatchObject([{ value: 'value1' }, { value: 'value2' }]) + await expect(keyv.getMany(['key1', 'key2'], { raw: true })).resolves.toMatchObject([ + { value: 'value1' }, + { value: 'value2' }, + ]) } // Check existence @@ -214,6 +204,4 @@ async function testAllKeyvMethods(store: any, rawMatchTest = true) { // clear await keyv.clear() await expect(keyv.has('key2')).resolves.toEqual(false) - } - diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts index f27c57134..66b2a26bc 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts @@ -1,7 +1,5 @@ import { KeyValueStore } from '../key-value-store' import { IKeyValueStore } from '../key-value-types' -import { beforeEach, expect } from '@jest/globals' - interface TestValue { stringProp: string @@ -35,13 +33,14 @@ describe('kvStore with MAP adapter', () => { try { await kvStore.clear() await kvStore.disconnect() - } catch (error) { - } - + } catch (error) {} }) it('should get non-existing keys as undefined', async () => { await expect(kvStore.get('nope')).resolves.toBeUndefined() - await expect(kvStore.getAsValueData('nope')).resolves.toMatchObject({ value: undefined, expires: undefined }) + await expect(kvStore.getAsValueData('nope')).resolves.toMatchObject({ + value: undefined, + expires: undefined, + }) }) it('should get single results', async () => { @@ -91,5 +90,4 @@ describe('kvStore with MAP adapter', () => { await kvStore.clear() await expect(kvStore.has(`key1`)).resolves.toEqual(false) }) - }) diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/index.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/index.ts index 415492c9d..c72fc52ff 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/index.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/index.ts @@ -1,10 +1,10 @@ /** - * Provides a {@link @veramo/did-manager#DIDManager | plugin} for the - * {@link @veramo/core#Agent} that implements {@link @veramo/core-types#IDIDManager} interface. + * Provides a {@link @veramo/kv-store#KeyValueStore} for the + * {@link @veramo/core#Agent} plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * * @packageDocumentation */ -export { KeyValueStore } from './key-value-store.js' -export * from './store-adapters/tiered/index.js' -export * from './store-adapters/typeorm/index.js' -export * from './key-value-types.js' +export { KeyValueStore } from './key-value-store' +export * from './store-adapters/tiered/index' +export * from './store-adapters/typeorm/index' +export * from './key-value-types' diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/key-value-store.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/key-value-store.ts index ff419713a..653b58eae 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/key-value-store.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/key-value-store.ts @@ -1,18 +1,12 @@ -import { - IKeyValueStore, - IKeyValueStoreOnArgs, - IKeyValueStoreOptions, - IValueData, -} from './key-value-types.js' -import { Keyv } from './keyv/keyv.js' -import { KeyvDeserializedData, KeyvOptions, KeyvStoredData } from './keyv/keyv-types.js' +import { IKeyValueStore, IKeyValueStoreOnArgs, IKeyValueStoreOptions, IValueData } from './key-value-types' +import { Keyv } from './keyv/keyv' +import { KeyvDeserializedData, KeyvOptions, KeyvStoredData } from './keyv/keyv-types' /** - * Agent plugin that implements {@link @veramo/core-types#IKeyValueStore} interface + * Agent plugin that implements {@link @veramo/kv-store#IKeyValueStore} interface * @public */ export class KeyValueStore implements IKeyValueStore { - /** * The main keyv typescript port which delegates to the storage adapters and takes care of some common functionality * @@ -32,9 +26,7 @@ export class KeyValueStore implements IKeyValueStore { return result as ValueType } - async getAsValueData( - key: string, - ): Promise> { + async getAsValueData(key: string): Promise> { const result = await this.keyv.get(key, { raw: true }) if (result === null || result === undefined) { // We always return a ValueData object for this method @@ -52,16 +44,12 @@ export class KeyValueStore implements IKeyValueStore { // Making sure we return the same array length as the amount of key(s) passed in if (result === null || result === undefined || result.length === 0) { result = new Array() - for (const key of keys) { - result.push(undefined) - } + keys.forEach(() => result.push(undefined)) } return result.map((v) => (!!v ? (v as ValueType) : undefined)) } - async getManyAsValueData( - keys: string[], - ): Promise>> { + async getManyAsValueData(keys: string[]): Promise>> { if (!keys || keys.length === 0) { return [] } @@ -70,21 +58,15 @@ export class KeyValueStore implements IKeyValueStore { // Making sure we return the same array length as the amount of key(s) passed in if (result === null || result === undefined || result.length === 0) { result = new Array>() - for (const key of keys) { - result.push({ value: undefined, expires: undefined } as KeyvDeserializedData) - } + keys.forEach(() => result.push({ value: undefined, expires: undefined } as KeyvDeserializedData)) } return result.map((v) => !!v ? this.toDeserializedValueData(v) : { value: undefined, expires: undefined }, ) } - async set( - key: string, value: ValueType, ttl?: number, - ): Promise> { - return this.keyv.set(key, value, ttl).then(() => - this.getAsValueData(key), - ) + async set(key: string, value: ValueType, ttl?: number): Promise> { + return this.keyv.set(key, value, ttl).then(() => this.getAsValueData(key)) } async has(key: string): Promise { @@ -96,7 +78,7 @@ export class KeyValueStore implements IKeyValueStore { } async deleteMany(keys: string[]): Promise { - return Promise.all(keys.map(key => this.keyv.delete(key))) + return Promise.all(keys.map((key) => this.keyv.delete(key))) } async clear(): Promise> { diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts index a2bc71d93..e23714e39 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts @@ -66,7 +66,7 @@ export interface KeyvStore { options?: { raw?: boolean }, ): OrPromise | Array>> - getMany?(keys: string[], options?: { raw?: boolean }): OrPromise>> | undefined + getMany?(keys: string[], options?: { raw?: boolean }): OrPromise>> | undefined iterator?(namespace?: string | undefined): AsyncGenerator diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv.ts index 47a75e017..27debb7ad 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/keyv/keyv.ts @@ -1,6 +1,6 @@ -import EventEmitter from 'events' +import { EventEmitter } from 'events' import JSONB from 'json-buffer' -import { KeyvDeserializedData, KeyvOptions, KeyvStore, KeyvStoredData } from './keyv-types.js' +import { KeyvDeserializedData, KeyvOptions, KeyvStore, KeyvStoredData } from './keyv-types' /** * Please note that this is code adapted from @link https://github.com/jaredwray/keyv to support Typescript and ESM in Veramo @@ -132,45 +132,51 @@ export class Keyv extends EventEmitter implements KeyvStore const keyPrefixed = this._getKeyPrefixArray(keys) let promise: Promise>> if (this.store.getMany !== undefined) { - promise = (this.store.getMany(keyPrefixed, options) as Promise[]>)//.then(value => !!value ? value.values() : undefined) + promise = this.store.getMany(keyPrefixed, options) as Promise[]> //.then(value => !!value ? value.values() : undefined) // todo: Probably wise to check expired ValueData here, if the getMany does not implement this feature itself! } else { - promise = Promise.all(keyPrefixed.map(k => this.store.get(k, options) as Promise>)) + promise = Promise.all( + keyPrefixed.map((k) => this.store.get(k, options) as Promise>), + ) } const allValues = Promise.resolve(promise) const results: Promise>[] = [] + return Promise.resolve(allValues) + .then((all) => { + keys.forEach((key, index) => { + const data = all[index] + + let result = + typeof data === 'string' + ? this.deserialize(data) + : !!data && this.opts.compression + ? this.deserialize(data) + : data + + if ( + result && + typeof result === 'object' && + 'expires' in result && + typeof result.expires === 'number' && + Date.now() > result.expires + ) { + this.delete(key) + result = undefined + } - return Promise.resolve(allValues).then(all => { - keys.forEach((key, index) => { - const data = all[index] - - let result = typeof data === 'string' - ? this.deserialize(data) - : !!data && this.opts.compression - ? this.deserialize(data) - : data - - if ( - result && - typeof result === 'object' && - 'expires' in result && - typeof result.expires === 'number' && - Date.now() > result.expires - ) { - this.delete(key) - result = undefined - } - - const final = (options && options.raw - ? result - : result && typeof result === 'object' && 'value' in result - ? result.value - : result) as Promise> + const final = ( + options && options.raw + ? result + : result && typeof result === 'object' && 'value' in result + ? result.value + : result + ) as Promise> - results.push(final) + results.push(final) + }) }) - }).then(() => Promise.all(results)) + .then(() => Promise.all(results)) } async get( @@ -188,8 +194,8 @@ export class Keyv extends EventEmitter implements KeyvStore typeof data === 'string' ? this.deserialize(data) : this.opts.compression - ? this.deserialize(data) - : data, + ? this.deserialize(data) + : data, ) .then((data) => { if (data === undefined || data === null) { @@ -224,8 +230,8 @@ export class Keyv extends EventEmitter implements KeyvStore return options && options.raw ? data : Array.isArray(data) - ? data.map((d) => toValue(d)) - : toValue(data) + ? data.map((d) => toValue(d)) + : toValue(data) }) } diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/index.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/index.ts index 4b88a3c38..1fe352d0b 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/index.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/index.ts @@ -1,2 +1,2 @@ -export * from './tiered/index.js' -export * from './typeorm/index.js' +export * from './tiered/index' +export * from './typeorm/index' diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts index d78422672..c18bc02be 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts @@ -1,8 +1,8 @@ -import EventEmitter from 'events' -import type { Options, Options_ } from './types.js' -import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types.js' -import { Keyv } from '../../keyv/keyv.js' -import { IKeyValueStoreAdapter } from '../../key-value-types.js' +import { EventEmitter } from 'events' +import type { Options, Options_ } from './types' +import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types' +import { Keyv } from '../../keyv/keyv' +import { IKeyValueStoreAdapter } from '../../key-value-types' type KeyvTieredIndex = 'local' | 'remote' @@ -39,10 +39,10 @@ export class KeyValueTieredStoreAdapter if (Array.isArray(key)) { return await this.getMany(key, options) } - const localResult = await this.local.get(key, options) as KeyvStoredData + const localResult = (await this.local.get(key, options)) as KeyvStoredData if (localResult === undefined || !this.opts.validator(localResult, key)) { - const remoteResult = (await this.remote.get(key, options)) + const remoteResult = await this.remote.get(key, options) if (remoteResult !== localResult) { const value = ( @@ -71,10 +71,7 @@ export class KeyValueTieredStoreAdapter return localResult } - async getMany( - keys: string[], - options?: { raw?: boolean }, - ): Promise>> { + async getMany(keys: string[], options?: { raw?: boolean }): Promise>> { const promises: Array>> = [] for (const key of keys) { promises.push(this.get(key, options) as Promise>) diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts index 18c84e881..d808e3735 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts @@ -1,4 +1,4 @@ -import { IKeyValueStoreAdapter } from '../../key-value-types.js' +import { IKeyValueStoreAdapter } from '../../key-value-types' export type Options = { local: IKeyValueStoreAdapter | Map diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts index 0cf8fa371..fdb2db4f9 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts @@ -1,17 +1,21 @@ -import EventEmitter from 'events' +import { EventEmitter } from 'events' import { OrPromise } from '@veramo/utils' import { DataSource, In, Like } from 'typeorm' -import { KeyValueStoreEntity } from './entities/keyValueStoreEntity.js' -import { KeyValueTypeORMOptions, Options_ } from './types.js' -import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types.js' -import { IKeyValueStoreAdapter } from '../../key-value-types.js' +import { KeyValueStoreEntity } from './entities/keyValueStoreEntity' +import { KeyValueTypeORMOptions, Options_ } from './types' +import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types' +import { IKeyValueStoreAdapter } from '../../key-value-types' import JSONB from 'json-buffer' -export { KeyValueTypeORMOptions } from './types.js' +export { KeyValueTypeORMOptions } from './types' +export { KeyValueStoreEntity } from './entities/keyValueStoreEntity' +export { kvStoreMigrations } from './migrations' + export class KeyValueTypeORMStoreAdapter extends EventEmitter - implements KeyvStore, IKeyValueStoreAdapter { + implements KeyvStore, IKeyValueStoreAdapter +{ private readonly dbConnection: OrPromise readonly namespace: string opts: Options_ @@ -40,9 +44,7 @@ export class KeyValueTypeORMStoreAdapter const result = await connection.getRepository(KeyValueStoreEntity).findOneBy({ key, }) - return options?.raw !== true || !result - ? result?.data - : { value: result?.data, expires: result?.expires } + return options?.raw !== true || !result ? result?.data : { value: result?.data, expires: result?.expires } } async getMany(keys: string[], options?: { raw?: boolean }): Promise>> { @@ -50,14 +52,14 @@ export class KeyValueTypeORMStoreAdapter const results = await connection.getRepository(KeyValueStoreEntity).findBy({ key: In(keys), }) - const values = keys.map(async key => { - const result = results.find(result => result.key === key) + const values = keys.map(async (key) => { + const result = results.find((result) => result.key === key) return options?.raw !== true || !result - ? result?.data as KeyvStoredData - : { - value: result?.data ? (await this.opts.deserialize(result.data))?.value : undefined, - expires: result?.expires, - } as KeyvStoredData + ? (result?.data as KeyvStoredData) + : ({ + value: result?.data ? (await this.opts.deserialize(result.data))?.value : undefined, + expires: result?.expires, + } as KeyvStoredData) }) return Promise.all(values) diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts index 594542a75..03ef426fb 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts @@ -1,6 +1,6 @@ import { MigrationInterface, QueryRunner, Table } from 'typeorm' import Debug from 'debug' -const debug = Debug('veramo:data-store:initial-migration') +const debug = Debug('veramo:kv-store:initial-migration') /** * Create the database layout for Veramo 3.0 @@ -11,8 +11,6 @@ export class CreateKVDatabaseMigration implements MigrationInterface { private readonly _tableName: string readonly name: string - - constructor(tableName?: string) { this._tableName = tableName || 'keyvaluestore' this.name = `CreateKVDatabase${tableName}1680297189001` diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts index b8ce4e933..6d6075eb5 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts @@ -7,6 +7,4 @@ import { CreateKVDatabaseMigration } from './1.createKVDatabase' * * @public */ -export const kvStoreMigrations = [ - CreateKVDatabaseMigration -] +export const kvStoreMigrations = [CreateKVDatabaseMigration] diff --git a/packages/presentation-exchange/.yalc/@veramo/kv-store/yalc.sig b/packages/presentation-exchange/.yalc/@veramo/kv-store/yalc.sig index 15eec14d7..44752dbc7 100644 --- a/packages/presentation-exchange/.yalc/@veramo/kv-store/yalc.sig +++ b/packages/presentation-exchange/.yalc/@veramo/kv-store/yalc.sig @@ -1 +1 @@ -09469f925723eeecd041caefbbb3346e \ No newline at end of file +bfd8bfc89f0050ba31664b2d7c3efc7a \ No newline at end of file diff --git a/packages/presentation-exchange/README.md b/packages/presentation-exchange/README.md index e12dbf5f2..ad5a016e3 100644 --- a/packages/presentation-exchange/README.md +++ b/packages/presentation-exchange/README.md @@ -12,14 +12,12 @@ --- - ## Requirements For this plugin a DID resolver is also required. A DID resolver can be added to the agent as plugin as seen in the example below. ## Available functions - ## Usage ### Adding the plugin to an agent: diff --git a/packages/presentation-exchange/__tests__/fixtures/pd_single.json b/packages/presentation-exchange/__tests__/fixtures/pd_single.json new file mode 100644 index 000000000..826299da7 --- /dev/null +++ b/packages/presentation-exchange/__tests__/fixtures/pd_single.json @@ -0,0 +1,24 @@ +{ + "id": "Credentials", + "input_descriptors": [ + { + "id": "ID Card Credential", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" + } + ], + "constraints": { + "fields": [ + { + "path": ["$.issuer.id"], + "filter": { + "type": "string", + "pattern": "did:example:issuer" + } + } + ] + } + } + ] +} diff --git a/packages/presentation-exchange/__tests__/restAgent.test.ts b/packages/presentation-exchange/__tests__/restAgent.test.ts index 90c88038a..d52882adb 100644 --- a/packages/presentation-exchange/__tests__/restAgent.test.ts +++ b/packages/presentation-exchange/__tests__/restAgent.test.ts @@ -1,55 +1,33 @@ -import * as fs from 'fs' import 'cross-fetch/polyfill' // @ts-ignore import express from 'express' -import { IAgent, createAgent, IAgentOptions, IDataStore } from '@veramo/core' +import { createAgent, IAgent, IAgentOptions, IDataStore } from '@veramo/core' import { AgentRestClient } from '@veramo/remote-client' import { Server } from 'http' import { AgentRouter, RequestWithAgentRouter } from '@veramo/remote-server' import { getConfig } from '@veramo/cli/build/setup' import { createObjects } from '@veramo/cli/build/lib/objectCreator' -import { PresentationExchange, IPresentationExchange } from '../src' +import { IPresentationExchange, PresentationExchange } from '../src' import { Resolver } from 'did-resolver' import { getDidKeyResolver } from '@veramo/did-provider-key' import { DIDResolverPlugin } from '@veramo/did-resolver' -import { getUniResolver } from '@sphereon/did-uni-client' -import didAuthSiopOpAuthenticatorAgentLogic from './shared/presentationExchangeAgentLogic' -import { PresentationSignCallback } from '@sphereon/did-auth-siop' +import presentationExchangeAgentLogic from './shared/presentationExchangeAgentLogic' jest.setTimeout(30000) -function getFile(path: string) { - return fs.readFileSync(path, 'utf-8') -} - -function getFileAsJson(path: string) { - return JSON.parse(getFile(path)) -} - const port = 3002 const basePath = '/agent' let serverAgent: IAgent let restServer: Server -const presentationSignCallback: PresentationSignCallback = async (args) => { - const presentationSignProof = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/psc/psc.json') - - return { - ...args.presentation, - ...presentationSignProof, - } -} - const getAgent = (options?: IAgentOptions) => createAgent({ ...options, plugins: [ - new PresentationExchange(presentationSignCallback), + new PresentationExchange(), new DIDResolverPlugin({ resolver: new Resolver({ ...getDidKeyResolver(), - ...getUniResolver('lto', { resolveUrl: 'https://uniresolver.test.sphereon.io/1.0/identifiers' }), - ...getUniResolver('factom', { resolveUrl: 'https://uniresolver.test.sphereon.io/1.0/identifiers' }), }), }), new AgentRestClient({ @@ -61,11 +39,11 @@ const getAgent = (options?: IAgentOptions) => }) const setup = async (): Promise => { - const config = getConfig('packages/siopv2-openid4vp-op-auth/agent.yml') - config.agent.$args[0].plugins[1].$args[0] = presentationSignCallback + const config = getConfig('packages/presentation-exchange/agent.yml') + // config.agent.$args[0].plugins[1].$args[0] = presentationSignCallback const { agent } = createObjects(config, { agent: '/agent' }) - agent.registerCustomApprovalForSiop({ key: 'success', customApproval: () => Promise.resolve() }) - agent.registerCustomApprovalForSiop({ key: 'failure', customApproval: () => Promise.reject(new Error('denied')) }) + // agent.registerCustomApprovalForSiop({ key: 'success', customApproval: () => Promise.resolve() }) + // agent.registerCustomApprovalForSiop({ key: 'failure', customApproval: () => Promise.reject(new Error('denied')) }) serverAgent = agent const agentRouter = AgentRouter({ @@ -97,6 +75,6 @@ const testContext = { isRestTest: true, } -xdescribe('REST integration tests', () => { - didAuthSiopOpAuthenticatorAgentLogic(testContext) +describe('REST integration tests', () => { + presentationExchangeAgentLogic(testContext) }) diff --git a/packages/presentation-exchange/__tests__/shared/presentationExchangeAgentLogic.ts b/packages/presentation-exchange/__tests__/shared/presentationExchangeAgentLogic.ts index a51e4f9ed..ac1a4fddd 100644 --- a/packages/presentation-exchange/__tests__/shared/presentationExchangeAgentLogic.ts +++ b/packages/presentation-exchange/__tests__/shared/presentationExchangeAgentLogic.ts @@ -4,7 +4,6 @@ import { IPresentationExchange } from '../../src' import { mapIdentifierKeysToDoc } from '@veramo/utils' import { mapIdentifierKeysToDocWithJwkSupport } from '@sphereon/ssi-sdk-did-utils' import { IPresentationDefinition } from '@sphereon/pex' -import { expect } from '@jest/globals' function getFile(path: string) { return fs.readFileSync(path, 'utf-8') @@ -29,7 +28,7 @@ type ConfiguredAgent = TAgent // const didMethod = 'ethr' const did = 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a' -const identifier = { +/*const identifier = { did, provider: '', controllerKeyId: `${did}#controller`, @@ -43,7 +42,7 @@ const identifier = { }, ], services: [], -} +}*/ const authKeys = [ { kid: `${did}#controller`, @@ -62,9 +61,6 @@ const authKeys = [ }, }, ] - -console.log(identifier) - export default (testContext: { getAgent: () => ConfiguredAgent setup: () => Promise @@ -73,30 +69,78 @@ export default (testContext: { }) => { describe('Presentation Exchange Agent Plugin', () => { let agent: ConfiguredAgent - const singleDefinition: IPresentationDefinition = getFileAsJson('./packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_single.json') + const singleDefinition: IPresentationDefinition = getFileAsJson('./packages/presentation-exchange/__tests__/fixtures/pd_single.json') // const multiDefinition: IPresentationDefinition = getFileAsJson('./packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_multiple.json') beforeAll(async () => { await testContext.setup() agent = testContext.getAgent() - const mockedMapIdentifierKeysToDocMethod = mapIdentifierKeysToDoc as jest.Mock mockedMapIdentifierKeysToDocMethod.mockReturnValue(Promise.resolve(authKeys)) const mockedMapIdentifierKeysToDocMethodWithJwkSupport = mapIdentifierKeysToDocWithJwkSupport as jest.Mock mockedMapIdentifierKeysToDocMethodWithJwkSupport.mockReturnValue(Promise.resolve(authKeys)) - }) + /* afterEach(async () => { + await agent.pexStoreClearDefinitions() + })*/ + afterAll(testContext.tearDown) - it('should store definition', async () => { + it('should store valid definition', async () => { const storeValue = await agent.pexStorePersistDefinition({ definition: singleDefinition }) expect(storeValue).toBeDefined() expect(storeValue.value).toBeDefined() + await expect(agent.pexStoreHasDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(true) + }) + + it('should not store invalid definition by default', async () => { + await expect(agent.pexStorePersistDefinition({ definition: { invalid: 'definition' } as unknown as IPresentationDefinition })).rejects.toThrow( + 'Invalid definition. This is not a valid PresentationDefinition' + ) + }) + + it('should not store invalid definition if validation is enabled', async () => { + await expect( + agent.pexStorePersistDefinition({ validation: true, definition: { invalid: 'definition' } as unknown as IPresentationDefinition }) + ).rejects.toThrow('Invalid definition. This is not a valid PresentationDefinition') + }) + + it('should store invalid definition if validation is disabled', async () => { + const storeValue = await agent.pexStorePersistDefinition({ + validation: false, + definition: { invalid: 'definition' } as unknown as IPresentationDefinition, + }) + expect(storeValue).toBeDefined() + expect(storeValue.value).toBeDefined() + await expect(agent.pexStoreHasDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(true) }) + it('should get definition', async () => { + await agent.pexStorePersistDefinition({ definition: singleDefinition }) + await expect(agent.pexStoreGetDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(singleDefinition) + }) + + it('should remove definition', async () => { + await agent.pexStorePersistDefinition({ definition: singleDefinition }) + await expect(agent.pexStoreHasDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(true) + await expect(agent.pexStoreRemoveDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(true) + await expect(agent.pexStoreHasDefinition({ definitionId: singleDefinition.id })).resolves.toEqual(false) + }) + it('should clear definitions', async () => { + await agent.pexStorePersistDefinition({ definitionId: 'c1', definition: singleDefinition }) + await agent.pexStorePersistDefinition({ definitionId: 'c2', definition: singleDefinition }) + await agent.pexStorePersistDefinition({ definitionId: 'c3', definition: singleDefinition }) + await expect(agent.pexStoreHasDefinition({ definitionId: 'c1' })).resolves.toEqual(true) + await expect(agent.pexStoreHasDefinition({ definitionId: 'c2' })).resolves.toEqual(true) + await expect(agent.pexStoreHasDefinition({ definitionId: 'c3' })).resolves.toEqual(true) + await agent.pexStoreClearDefinitions() + await expect(agent.pexStoreHasDefinition({ definitionId: 'c1' })).resolves.toEqual(false) + await expect(agent.pexStoreHasDefinition({ definitionId: 'c2' })).resolves.toEqual(false) + await expect(agent.pexStoreHasDefinition({ definitionId: 'c3' })).resolves.toEqual(false) + }) }) } diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_multiple.json b/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_multiple.json deleted file mode 100644 index 8c86517e8..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_multiple.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "definition": { - "id": "Credentials", - "input_descriptors": [ - { - "id": "ID Card Credential and Driver's License", - "schema": [ - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" - }, - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/DriversLicense" - } - ], - "constraints": { - "fields": [ - { - "path": ["$.issuer.id"], - "filter": { - "type": "string", - "pattern": "did:example:[issuer|ebfeb1f712ebc6f1c276e12ec21]" - } - } - ] - } - } - ] - }, - "location": "presentation_definition" -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_single.json b/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_single.json deleted file mode 100644 index 81a68f376..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/pd/pd_single.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "definition": { - "id": "Credentials", - "input_descriptors": [ - { - "id": "ID Card Credential", - "schema": [ - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" - } - ], - "constraints": { - "fields": [ - { - "path": ["$.issuer.id"], - "filter": { - "type": "string", - "pattern": "did:example:issuer" - } - } - ] - } - } - ] - }, - "location": "presentation_definition" -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/psc/psc.json b/packages/presentation-exchange/__tests__/vc_vp_examples/psc/psc.json deleted file mode 100644 index 9755bd33f..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/psc/psc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_driverLicense.json b/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_driverLicense.json deleted file mode 100644 index daa0f0f2b..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_driverLicense.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": "https://example.com/credentials/1873", - "type": ["VerifiableCredential", "DriversLicense"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], - "issuer": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "John", - "family_name": "Doe", - "birthdate": "1975-01-05" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_idCardCredential.json b/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_idCardCredential.json deleted file mode 100644 index 01abcea0e..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/vc/vc_idCardCredential.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": "https://example.com/credentials/1872", - "type": ["VerifiableCredential", "IDCardCredential"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "issuer": { - "id": "did:example:issuer" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "Fredrik", - "family_name": "Stremberg", - "birthdate": "1949-01-22" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_multiple.json b/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_multiple.json deleted file mode 100644 index fdb562154..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_multiple.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "format": "ldp_vp", - "location": "authorization_response", - "presentation": { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], - "presentation_submission": { - "definition_id": "Credentials", - "descriptor_map": [ - { - "format": "ldp_vc", - "id": "ID Card Credential and Driver's License", - "path": "$.verifiableCredential[0]" - }, - { - "format": "ldp_vc", - "id": "ID Card Credential and Driver's License", - "path": "$.verifiableCredential[1]" - } - ], - "id": "8oBenRGlNXd0Sp770bCb3" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - }, - "type": ["VerifiablePresentation", "PresentationSubmission"], - "verifiableCredential": [ - { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "credentialSubject": { - "birthdate": "1949-01-22", - "family_name": "Stremberg", - "given_name": "Fredrik" - }, - "id": "https://example.com/credentials/1872", - "issuanceDate": "2010-01-01T19:23:24Z", - "issuer": { - "id": "did:example:issuer" - }, - "proof": { - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "created": "2018-09-14T21:19:10Z", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", - "proofPurpose": "authentication", - "type": "RsaSignature2018", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" - }, - "type": ["VerifiableCredential", "IDCardCredential"] - }, - { - "id": "https://example.com/credentials/1873", - "type": ["VerifiableCredential", "DriversLicense"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], - "issuer": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "John", - "family_name": "Doe", - "birthdate": "1975-01-05" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } - } - ] - } -} diff --git a/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_single.json b/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_single.json deleted file mode 100644 index 3ca91b205..000000000 --- a/packages/presentation-exchange/__tests__/vc_vp_examples/vp/vp_single.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "format": "ldp_vp", - "location": "authorization_response", - "presentation": { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], - "presentation_submission": { - "definition_id": "Credentials", - "descriptor_map": [ - { - "format": "ldp_vc", - "id": "ID Card Credential", - "path": "$.verifiableCredential[0]" - } - ], - "id": "8oBenRGlNXd0Sp770bCb3" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - }, - "type": ["VerifiablePresentation", "PresentationSubmission"], - "verifiableCredential": [ - { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "credentialSubject": { - "birthdate": "1949-01-22", - "family_name": "Stremberg", - "given_name": "Fredrik" - }, - "id": "https://example.com/credentials/1872", - "issuanceDate": "2010-01-01T19:23:24Z", - "issuer": { - "id": "did:example:issuer" - }, - "proof": { - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "created": "2018-09-14T21:19:10Z", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", - "proofPurpose": "authentication", - "type": "RsaSignature2018", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" - }, - "type": ["VerifiableCredential", "IDCardCredential"] - } - ] - } -} diff --git a/packages/presentation-exchange/package.json b/packages/presentation-exchange/package.json index f81b024ca..aeb5f7307 100644 --- a/packages/presentation-exchange/package.json +++ b/packages/presentation-exchange/package.json @@ -11,8 +11,7 @@ }, "scripts": { "build": "tsc --build", - "build:clean": "tsc --build --clean && tsc --build", - "generate-plugin-schema": "yarn veramo dev generate-plugin-schema" + "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { "@sphereon/pex": "2.0.0-unstable.13", @@ -23,6 +22,7 @@ "@veramo/kv-store": "file:.yalc/@veramo/kv-store" }, "devDependencies": { + "@types/json-buffer": "^3.0.0", "@sphereon/did-uni-client": "^0.6.0", "@veramo/cli": "4.2.0", "@veramo/did-provider-key": "4.2.0", @@ -31,6 +31,9 @@ "did-resolver": "^4.1.0", "nock": "^13.2.1" }, + "resolutions": { + "@veramo/utils": "4.2.0" + }, "files": [ "dist/**/*", "src/**/*", diff --git a/packages/presentation-exchange/src/agent/PresentationExchange.ts b/packages/presentation-exchange/src/agent/PresentationExchange.ts index 7d0da54a3..a3861a1f7 100644 --- a/packages/presentation-exchange/src/agent/PresentationExchange.ts +++ b/packages/presentation-exchange/src/agent/PresentationExchange.ts @@ -16,10 +16,10 @@ import { FindCredentialsArgs, IAgentPlugin } from '@veramo/core' import { IPresentationExchange } from '../types/IPresentationExchange' import { IKeyValueStore, IValueData, KeyValueStore } from '@veramo/kv-store' -import { IPresentationDefinition, PEX } from '@sphereon/pex' +import { Checked, IPresentationDefinition, PEX } from '@sphereon/pex' import { CredentialMapper, JWT_PROOF_TYPE_2020, W3CVerifiableCredential } from '@sphereon/ssi-types' -import { toDIDs } from '../functions' import { InputDescriptorV1, InputDescriptorV2 } from '@sphereon/pex-models' +import { toDIDs } from '@sphereon/ssi-sdk-did-utils' export class PresentationExchange implements IAgentPlugin { private static PEX = new PEX() @@ -39,63 +39,76 @@ export class PresentationExchange implements IAgentPlugin { pexDefinitionFilterCredentialsPerInputDescriptor: this.pexDefinitionFilterCredentialsPerInputDescriptor.bind(this), } - constructor( - opts: PEXOpts, - ) { - this.defaultStore = opts.defaultStore ?? '_default' - this.defaultNamespace = opts.defaultNamespace ?? 'pex' - if (opts.stores && opts.stores instanceof Map) { + constructor(opts?: PEXOpts) { + this.defaultStore = opts?.defaultStore ?? '_default' + this.defaultNamespace = opts?.defaultNamespace ?? 'pex' + if (opts?.stores && opts.stores instanceof Map) { this._stores = opts.stores - } else if (opts.stores) { + } else if (opts?.stores) { this._stores = new Map().set(this.defaultStore, opts.stores) } else { - this._stores = new Map().set(this.defaultStore, new KeyValueStore({ - namespace: this.defaultNamespace, - store: new Map(), - })) + this._stores = new Map().set( + this.defaultStore, + new KeyValueStore({ + namespace: this.defaultNamespace, + store: new Map(), + }) + ) } - if (Array.isArray(opts.importDefinitions)) { + if (opts && Array.isArray(opts?.importDefinitions)) { opts.importDefinitions.forEach(this.pexStorePersistDefinition) } } - - private async pexStoreGetDefinition({ - definitionId, - storeId, - namespace, - }: IDefinitionGetArgs): Promise { + private async pexStoreGetDefinition({ definitionId, storeId, namespace }: IDefinitionGetArgs): Promise { return this.store({ storeId }).get(this.prefix({ namespace, definitionId })) } - private async pexStoreHasDefinition({ definitionId, storeId, namespace }: IDefinitionExistsArgs): Promise { return this.store({ storeId }).has(this.prefix({ namespace, definitionId })) } private async pexStorePersistDefinition(args: IDefinitionPersistArgs): Promise> { const { definition, ttl, storeId, namespace } = args - PresentationExchange.PEX.validateDefinition(definition) // throws an error in case the def is not valid + if (args?.validation !== false) { + let invalids: Checked[] = [] + + try { + const result = PresentationExchange.PEX.validateDefinition(definition) // throws an error in case the def is not valid + const validations = Array.isArray(result) ? result : [result] + invalids = validations.filter((v) => v.status === 'error') + } catch (error) { + invalids.push({ + status: 'error', + message: + typeof error === 'string' + ? error + : typeof error === 'object' && 'message' in (error as object) + ? (error as Error).message + : 'unknown error', + tag: 'validation', + }) + } + if (invalids.length > 0) { + throw Error(`Invalid definition. ${invalids.map((v) => v.message).toString()}`) + } + } const definitionId = args.definitionId ?? definition.id const existing = await this.store({ storeId }).getAsValueData(this.prefix({ namespace, definitionId })) if (!existing.value || (existing.value && args.overwriteExisting !== false)) { - return await this.store({ storeId }) - .set(this.prefix({ namespace, definitionId }), definition, ttl) + return await this.store({ storeId }).set(this.prefix({ namespace, definitionId }), definition, ttl) } return existing } - - private async pexStoreRemoveDefinition({ - storeId, - definitionId, - namespace, - }: IDefinitionRemoveArgs): Promise { + private async pexStoreRemoveDefinition({ storeId, definitionId, namespace }: IDefinitionRemoveArgs): Promise { return this.store({ storeId }).delete(this.prefix({ namespace, definitionId })) } - private async pexStoreClearDefinitions({ storeId }: IDefinitionsClearArgs): Promise { - await this.store({ storeId }).clear() + private async pexStoreClearDefinitions({ storeId }: IDefinitionsClearArgs): Promise { + return await this.store({ storeId }) + .clear() + .then(() => true) } async pexDefinitionVersion(presentationDefinition: IPresentationDefinition): Promise { @@ -105,15 +118,23 @@ export class PresentationExchange implements IAgentPlugin { async pexDefinitionFilterCredentials(args: IDefinitionCredentialFilterArgs, context: IRequiredContext): Promise { const credentials = await this.pexFilterCredentials(args.credentialFilterOpts ?? {}, context) const holderDIDs = args.holderDIDs ? toDIDs(args.holderDIDs) : toDIDs(await context.agent.dataStoreORMGetIdentifiers()) - const selectResults = new PEX().selectFrom(args.presentationDefinition, credentials ?? [], holderDIDs, args.limitDisclosureSignatureSuites ?? ['BbsBlsSignature2020']) + const selectResults = new PEX().selectFrom( + args.presentationDefinition, + credentials ?? [], + holderDIDs, + args.limitDisclosureSignatureSuites ?? ['BbsBlsSignature2020'] + ) return { id: args.presentationDefinition.id, selectResults, - filteredCredentials: selectResults.verifiableCredential?.map(vc => CredentialMapper.storedCredentialToOriginalFormat(vc)) ?? [], + filteredCredentials: selectResults.verifiableCredential?.map((vc) => CredentialMapper.storedCredentialToOriginalFormat(vc)) ?? [], } } - async pexDefinitionFilterCredentialsPerInputDescriptor(args: IDefinitionCredentialFilterArgs, context: IRequiredContext): Promise { + async pexDefinitionFilterCredentialsPerInputDescriptor( + args: IDefinitionCredentialFilterArgs, + context: IRequiredContext + ): Promise { const origDefinition = args.presentationDefinition const credentials = await this.pexFilterCredentials(args.credentialFilterOpts ?? {}, context) const holderDIDs = args.holderDIDs ? toDIDs(args.holderDIDs) : toDIDs(await context.agent.dataStoreORMGetIdentifiers()) @@ -125,12 +146,18 @@ export class PresentationExchange implements IAgentPlugin { id: inputDescriptor.id, input_descriptors: [inputDescriptor], } - promises.set(inputDescriptor, this.pexDefinitionFilterCredentials({ - credentialFilterOpts: { verifiableCredentials: credentials }, - presentationDefinition, - holderDIDs, - limitDisclosureSignatureSuites, - }, context)) + promises.set( + inputDescriptor, + this.pexDefinitionFilterCredentials( + { + credentialFilterOpts: { verifiableCredentials: credentials }, + presentationDefinition, + holderDIDs, + limitDisclosureSignatureSuites, + }, + context + ) + ) }) await Promise.all(promises.values()) const result: IPEXFilterResultWithInputDescriptor[] = [] @@ -140,11 +167,13 @@ export class PresentationExchange implements IAgentPlugin { return result } - - private async pexFilterCredentials(filterOpts: { - verifiableCredentials?: W3CVerifiableCredential[]; - filter?: FindCredentialsArgs - }, context: IRequiredContext): Promise { + private async pexFilterCredentials( + filterOpts: { + verifiableCredentials?: W3CVerifiableCredential[] + filter?: FindCredentialsArgs + }, + context: IRequiredContext + ): Promise { if (filterOpts?.verifiableCredentials && filterOpts.verifiableCredentials.length > 0) { return filterOpts.verifiableCredentials as W3CVerifiableCredential[] } @@ -159,7 +188,6 @@ export class PresentationExchange implements IAgentPlugin { } }*/ - private store({ storeId }: { storeId?: string }): IKeyValueStore { const store = this._stores.get(storeId ?? this.defaultStore) if (!store) { @@ -172,9 +200,7 @@ export class PresentationExchange implements IAgentPlugin { return namespace ?? this.defaultStore } - private prefix({ namespace, definitionId }: { namespace?: string, definitionId: string }) { + private prefix({ namespace, definitionId }: { namespace?: string; definitionId: string }) { return `${this.namespace({ namespace })}:${definitionId}` } - - } diff --git a/packages/presentation-exchange/src/functions.ts b/packages/presentation-exchange/src/functions.ts index 70b523dc9..d48dd3472 100644 --- a/packages/presentation-exchange/src/functions.ts +++ b/packages/presentation-exchange/src/functions.ts @@ -1,15 +1,8 @@ -import { - IIdentifierOpts, - IPEXOptions, - IPEXPresentationSignCallback, - IRequiredContext, -} from './types/IPresentationExchange' -import { getAgentDIDMethods } from '@sphereon/ssi-sdk-did-utils' +import { IPEXOptions, IPEXPresentationSignCallback, IRequiredContext } from './types/IPresentationExchange' import { IPresentationDefinition } from '@sphereon/pex' -import { IIdentifier, PresentationPayload } from '@veramo/core' +import { PresentationPayload } from '@veramo/core' import { W3CVerifiablePresentation } from '@sphereon/ssi-types' - /* export async function getPresentationDefinitionStore(pexOptions?: IPEXOptions): Promise | undefined> { if (pexOptions && pexOptions.definitionId) { @@ -32,60 +25,29 @@ export async function getPresentationDefinition(pexOptions?: IPEXOptions): Promi return store && pexOptions?.definitionId ? store.get(pexOptions?.definitionId) : undefined*/ } -export function toDIDs(identifiers?: (string | IIdentifier | Partial)[]): string[] { - if (!identifiers) { - return [] - } - return identifiers.map(toDID) -} - -export function toDID(identifier: string | IIdentifier | Partial): string { - if (typeof identifier === 'string') { - return identifier - } - if (identifier.did) { - return identifier.did - } - throw Error(`No DID value present in identifier`) -} - -export function getDID(identifierOpts: IIdentifierOpts): string { - if (typeof identifierOpts.identifier === 'string' || typeof identifierOpts.identifier === 'object') { - return toDID(identifierOpts.identifier) - } - throw Error(`Cannot get DID from identifier value`) -} - - -export async function getIdentifier(identifierOpts: IIdentifierOpts, context: IRequiredContext): Promise { - if (typeof identifierOpts.identifier === 'string') { - return context.agent.didManagerGet({ did: identifierOpts.identifier }) - } else if (typeof identifierOpts.identifier === 'object') { - return identifierOpts.identifier - } - throw Error(`Cannot get agent identifier value from options`) -} - - -export async function getSupportedDIDMethods(opts: { supportedDIDMethods?: string[] }, context: IRequiredContext) { - return opts.supportedDIDMethods ?? (await getAgentDIDMethods(context)) -} - - -export async function createPEXPresentationSignCallback({ - kid, - fetchRemoteContexts, - domain, - challenge - }: { - kid: string - fetchRemoteContexts?: boolean - domain?: string, - challenge?: string, - -}, context: IRequiredContext): Promise { - return async ({ presentation, domain, presentationDefinition, challenge }: { - presentation: PresentationPayload, presentationDefinition: IPresentationDefinition, domain?: string +export async function createPEXPresentationSignCallback( + { + kid, + fetchRemoteContexts, + domain, + challenge, + }: { + kid: string + fetchRemoteContexts?: boolean + domain?: string + challenge?: string + }, + context: IRequiredContext +): Promise { + return async ({ + presentation, + domain, + presentationDefinition, + challenge, + }: { + presentation: PresentationPayload + presentationDefinition: IPresentationDefinition + domain?: string challenge?: string }): Promise => { const format = presentationDefinition.format @@ -101,4 +63,3 @@ export async function createPEXPresentationSignCallback({ return vp as W3CVerifiablePresentation } } - diff --git a/packages/presentation-exchange/src/types/IPresentationExchange.ts b/packages/presentation-exchange/src/types/IPresentationExchange.ts index 448cb515d..dff488699 100644 --- a/packages/presentation-exchange/src/types/IPresentationExchange.ts +++ b/packages/presentation-exchange/src/types/IPresentationExchange.ts @@ -2,9 +2,11 @@ import { DIDDocumentSection, FindCredentialsArgs, IAgentContext, - IDataStoreORM, IDIDManager, + IDataStoreORM, + IDIDManager, IIdentifier, - IPluginMethodMap, PresentationPayload, + IPluginMethodMap, + PresentationPayload, } from '@veramo/core' import { W3CVerifiableCredential, W3CVerifiablePresentation } from '@sphereon/ssi-types' import { IKeyValueStore, IValueData } from '@veramo/kv-store' @@ -12,7 +14,6 @@ import { IPresentationDefinition, SelectResults } from '@sphereon/pex' import { PEVersion } from '@sphereon/pex/dist/main/lib/types' import { InputDescriptorV1, InputDescriptorV2 } from '@sphereon/pex-models' - export interface IPresentationExchange extends IPluginMethodMap { pexStoreGetDefinition(args: IDefinitionGetArgs): Promise @@ -22,18 +23,21 @@ export interface IPresentationExchange extends IPluginMethodMap { pexStoreRemoveDefinition(args: IDefinitionRemoveArgs): Promise - pexStoreClearDefinitions(args: IDefinitionsClearArgs): Promise + pexStoreClearDefinitions(args: IDefinitionsClearArgs): Promise pexDefinitionVersion(presentationDefinition: IPresentationDefinition): Promise pexDefinitionFilterCredentials(args: IDefinitionCredentialFilterArgs, context: IRequiredContext): Promise - pexDefinitionFilterCredentialsPerInputDescriptor(args: IDefinitionCredentialFilterArgs, context: IRequiredContext): Promise + pexDefinitionFilterCredentialsPerInputDescriptor( + args: IDefinitionCredentialFilterArgs, + context: IRequiredContext + ): Promise } export interface IDefinitionGetArgs { - definitionId: string, - storeId?: string, + definitionId: string + storeId?: string namespace?: string } @@ -41,29 +45,28 @@ export type IDefinitionExistsArgs = IDefinitionGetArgs export type IDefinitionClearArgs = IDefinitionGetArgs export type IDefinitionRemoveArgs = IDefinitionGetArgs - export type IDefinitionImportArgs = IDefinitionPersistArgs export interface IDefinitionPersistArgs { - overwriteExisting?: boolean - definitionId?: string - definition: IPresentationDefinition - ttl?: number - storeId?: string, - namespace?: string + definition: IPresentationDefinition // The actual Presentation definition to be stored/ + definitionId?: string // Allows to define a custom key for storage. By default, the id of the definition will be used + overwriteExisting?: boolean // Whether to overwrite any existing definition by id. Defaults to true + validation?: boolean // Whether to check the definition. Defaults to true + ttl?: number // How long should the definition be stored in seconds. By default, it will be indefinite + storeId?: string // The store id to use. Allows you to use multiple different stores next to each-other + namespace?: string // The namespace (prefix) to use whilst storing the definition. Allows you to partition definitions } export interface IDefinitionsClearArgs { - storeId?: string, + storeId?: string // namespace?: string } export interface IDefinitionCredentialFilterArgs { - presentationDefinition: IPresentationDefinition, - credentialFilterOpts?: { verifiableCredentials?: W3CVerifiableCredential[]; filter?: FindCredentialsArgs }, - holderDIDs?: (string | IIdentifier)[], + presentationDefinition: IPresentationDefinition + credentialFilterOpts?: { verifiableCredentials?: W3CVerifiableCredential[]; filter?: FindCredentialsArgs } + holderDIDs?: (string | IIdentifier)[] limitDisclosureSignatureSuites?: string[] - } export interface PEXOpts { @@ -73,7 +76,6 @@ export interface PEXOpts { importDefinitions?: IDefinitionImportArgs[] } - export interface IPEXOptions { // presentationVerifyCallback?: PresentationVerificationCallback definition?: IPresentationDefinition @@ -101,12 +103,10 @@ export interface VersionDiscoveryResult { error?: string } - - -export type IPEXPresentationSignCallback = (args: IPEXPresentationSignCallBackParams) => Promise; +export type IPEXPresentationSignCallback = (args: IPEXPresentationSignCallBackParams) => Promise export interface IPEXPresentationSignCallBackParams { - presentation: PresentationPayload, + presentation: PresentationPayload presentationDefinition: IPresentationDefinition } diff --git a/packages/presentation-exchange/yalc.lock b/packages/presentation-exchange/yalc.lock index b581efc47..0d032f29f 100644 --- a/packages/presentation-exchange/yalc.lock +++ b/packages/presentation-exchange/yalc.lock @@ -2,7 +2,8 @@ "version": "v1", "packages": { "@veramo/kv-store": { - "signature": "09469f925723eeecd041caefbbb3346e", + "version": "4.3.0", + "signature": "bfd8bfc89f0050ba31664b2d7c3efc7a", "file": true } } diff --git a/packages/siopv2-oid4vp-common/src/auth-model.ts b/packages/siopv2-oid4vp-common/src/auth-model.ts index 5b7007c51..fe88ee793 100644 --- a/packages/siopv2-oid4vp-common/src/auth-model.ts +++ b/packages/siopv2-oid4vp-common/src/auth-model.ts @@ -1,37 +1,36 @@ // noinspection JSUnusedGlobalSymbols -import {AuthorizationResponsePayload} from "@sphereon/did-auth-siop"; +import { AuthorizationResponsePayload } from '@sphereon/did-auth-siop' export interface ClaimPayloadCommonOpts { - [x: string]: any; + [x: string]: any } export declare enum AuthorizationRequestStateStatus { - CREATED = "created", - SENT = "sent", - RECEIVED = "received", - VERIFIED = "verified", - ERROR = "error" + CREATED = 'created', + SENT = 'sent', + RECEIVED = 'received', + VERIFIED = 'verified', + ERROR = 'error', } export declare enum AuthorizationResponseStateStatus { - CREATED = "created", - SENT = "sent", - RECEIVED = "received", - VERIFIED = "verified", - ERROR = "error" + CREATED = 'created', + SENT = 'sent', + RECEIVED = 'received', + VERIFIED = 'verified', + ERROR = 'error', } export interface GenerateAuthRequestURIResponse { - correlationId: string; - definitionId: string; - authRequestURI: string; - authStatusURI: string; + correlationId: string + definitionId: string + authRequestURI: string + authStatusURI: string } - export interface AuthStatusResponse { - status: AuthorizationRequestStateStatus | AuthorizationResponseStateStatus; - correlationId: string; - error?: string - definitionId: string; - lastUpdated: number; - payload?: AuthorizationResponsePayload; // Only put in here once the status reaches Verified on the RP side + status: AuthorizationRequestStateStatus | AuthorizationResponseStateStatus + correlationId: string + error?: string + definitionId: string + lastUpdated: number + payload?: AuthorizationResponsePayload // Only put in here once the status reaches Verified on the RP side } diff --git a/packages/siopv2-oid4vp-common/src/index.ts b/packages/siopv2-oid4vp-common/src/index.ts index d08da2951..0b23f51b5 100644 --- a/packages/siopv2-oid4vp-common/src/index.ts +++ b/packages/siopv2-oid4vp-common/src/index.ts @@ -1,3 +1,3 @@ -export * from './auth-model'; -export * from './utils'; - +export * from './auth-model' +export * from './utils' +export { decodeUriAsJson } from '@sphereon/did-auth-siop' diff --git a/packages/siopv2-oid4vp-common/src/utils.ts b/packages/siopv2-oid4vp-common/src/utils.ts index 97128977f..08263cdce 100644 --- a/packages/siopv2-oid4vp-common/src/utils.ts +++ b/packages/siopv2-oid4vp-common/src/utils.ts @@ -1,16 +1,17 @@ import * as u8a from 'uint8arrays' +import * as process from 'process' export function base64ToBytes(s: string): Uint8Array { - const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') - return u8a.fromString(inputBase64Url, 'base64url') + const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') + return u8a.fromString(inputBase64Url, 'base64url') } export function decodeBase64url(s: string): string { - return u8a.toString(base64ToBytes(s)) + return u8a.toString(base64ToBytes(s)) } -// noinspection JSUnusedLocalSymbols - -export function uriWithBase(path: string) { - return `${process.env.BACKEND_BASE_URL}${path.startsWith('/') ? path : '/' + path}`; +export function uriWithBase(path: string, opts?: { baseURI?: string; envVarName?: string }) { + let baseUri = `${opts?.baseURI ?? opts?.envVarName ? process.env[opts.envVarName!] : process.env.BACKEND_BASE_URL}` + baseUri = baseUri.endsWith('/') ? baseUri.substring(0, baseUri.length - 1) : baseUri + return `${baseUri}${path.startsWith('/') ? path : '/' + path}` } diff --git a/packages/siopv2-oid4vp-common/tsconfig.json b/packages/siopv2-oid4vp-common/tsconfig.json index f6211d63d..54ed0b2d5 100644 --- a/packages/siopv2-oid4vp-common/tsconfig.json +++ b/packages/siopv2-oid4vp-common/tsconfig.json @@ -8,10 +8,6 @@ "noUnusedLocals": false }, "references": [{ "path": "../ssi-types" }], - "include": [ - "src/**/*", - ], - "exclude": [ - "node_modules/**/*" - ] + "include": ["src/**/*"], + "exclude": ["node_modules/**/*"] } diff --git a/packages/siopv2-oid4vp-op-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts b/packages/siopv2-oid4vp-op-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts index 440346c01..9beaa1090 100644 --- a/packages/siopv2-oid4vp-op-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts +++ b/packages/siopv2-oid4vp-op-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts @@ -332,9 +332,7 @@ export default (testContext: { const pd_single: PresentationDefinitionWithLocation = getFileAsJson( './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' ) - const vp_single: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) + const vp_single: IPresentationWithDefinition = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json') const presentation = CredentialMapper.toWrappedVerifiablePresentation(vp_single.presentation) presentation.presentation.presentation_submission!.id = expect.any(String) @@ -363,9 +361,7 @@ export default (testContext: { const pdSingle: PresentationDefinitionWithLocation = getFileAsJson( './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' ) - const vpSingle: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) + const vpSingle: IPresentationWithDefinition = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json') const presentation = CredentialMapper.toWrappedVerifiablePresentation(vpSingle.presentation) presentation.presentation.presentation_submission!.id = expect.any(String) diff --git a/packages/siopv2-oid4vp-op-auth/src/session/OID4VP.ts b/packages/siopv2-oid4vp-op-auth/src/session/OID4VP.ts index e4fc70021..6ec404e9c 100644 --- a/packages/siopv2-oid4vp-op-auth/src/session/OID4VP.ts +++ b/packages/siopv2-oid4vp-op-auth/src/session/OID4VP.ts @@ -9,8 +9,9 @@ import { CredentialMapper, W3CVerifiableCredential } from '@sphereon/ssi-types' import { PresentationDefinitionWithLocation, PresentationExchange } from '@sphereon/did-auth-siop' import { SelectResults, Status, SubmissionRequirementMatch } from '@sphereon/pex' import { ProofOptions } from '@sphereon/ssi-sdk-core' -import { createOID4VPPresentationSignCallback, determineKid, getKey } from './functions' +import { createOID4VPPresentationSignCallback } from './functions' import { FindCredentialsArgs, IIdentifier } from '@veramo/core' +import { determineKid, getKey } from '@sphereon/ssi-sdk-did-utils' export class OID4VP { private readonly session: OpSession diff --git a/packages/siopv2-oid4vp-op-auth/src/session/OpSession.ts b/packages/siopv2-oid4vp-op-auth/src/session/OpSession.ts index c62d488b0..74b7affa5 100644 --- a/packages/siopv2-oid4vp-op-auth/src/session/OpSession.ts +++ b/packages/siopv2-oid4vp-op-auth/src/session/OpSession.ts @@ -103,7 +103,7 @@ export class OpSession { return (await this.getMergedRequestPayload()).redirect_uri } - public async isOID4VP(): Promise { + public async hasPresentationDefinitions(): Promise { const defs = (await this.getAuthorizationRequest()).presentationDefinitions return defs !== undefined && defs.length > 0 } @@ -154,7 +154,7 @@ export class OpSession { const request = this.verifiedAuthorizationRequest! if ( - (await this.isOID4VP()) && + (await this.hasPresentationDefinitions()) && request.presentationDefinitions && (!args.verifiablePresentations || args.verifiablePresentations.length !== request.presentationDefinitions.length) ) { diff --git a/packages/siopv2-oid4vp-op-auth/src/session/functions.ts b/packages/siopv2-oid4vp-op-auth/src/session/functions.ts index 0851b021d..e3b409324 100644 --- a/packages/siopv2-oid4vp-op-auth/src/session/functions.ts +++ b/packages/siopv2-oid4vp-op-auth/src/session/functions.ts @@ -1,6 +1,6 @@ import { IIdentifierOpts, IOPOptions, IRequiredContext } from '../types/IDidAuthSiopOpAuthenticator' import { EventEmitter } from 'events' -import { AgentDIDResolver, getAgentDIDMethods, mapIdentifierKeysToDocWithJwkSupport } from '@sphereon/ssi-sdk-did-utils' +import { AgentDIDResolver, determineKid, getAgentDIDMethods, getKey } from '@sphereon/ssi-sdk-did-utils' import { KeyAlgo, SuppliedSigner } from '@sphereon/ssi-sdk-core' import { Builder, @@ -12,8 +12,7 @@ import { SigningAlgo, SupportedVersion, } from '@sphereon/did-auth-siop' -import { DIDDocumentSection, IIdentifier, IKey, TKeyType } from '@veramo/core' -import { _ExtendedIKey } from '@veramo/utils' +import { TKeyType } from '@veramo/core' import { IVerifyCallbackArgs, IVerifyCredentialResult } from '@sphereon/wellknown-dids-client' import { createPEXPresentationSignCallback } from '@sphereon/ssi-sdk-presentation-exchange' @@ -29,14 +28,14 @@ export async function createOID4VPPresentationSignCallback({ kid: string domain?: string challenge?: string - fetchRemoteContexts?: boolean, + fetchRemoteContexts?: boolean context: IRequiredContext }): Promise { // fixme: Remove once IPresentation in proper form is available in PEX // @ts-ignore return presentationSignCallback ? presentationSignCallback - : createPEXPresentationSignCallback({kid, fetchRemoteContexts, domain, challenge}, context) + : createPEXPresentationSignCallback({ kid, fetchRemoteContexts, domain, challenge }, context) /*async (args: PresentationSignCallBackParams): Promise => { const presentation: PresentationPayload = args.presentation as PresentationPayload @@ -123,6 +122,7 @@ export async function createOP({ return (await createOPBuilder({ opOptions, idOpts, context })).build() } +/* export async function getKey( identifier: IIdentifier, verificationMethodSection: DIDDocumentSection = 'authentication', @@ -145,6 +145,7 @@ export async function getKey( export function determineKid(key: IKey, idOpts: IIdentifierOpts): string { return key.meta?.verificationMethod.id ?? idOpts.kid ?? key.kid } +*/ export function getSigningAlgo(type: TKeyType): SigningAlgo { switch (type) { diff --git a/packages/siopv2-oid4vp-op-auth/tsconfig.json b/packages/siopv2-oid4vp-op-auth/tsconfig.json index c2bb62f39..723ec7a2d 100644 --- a/packages/siopv2-oid4vp-op-auth/tsconfig.json +++ b/packages/siopv2-oid4vp-op-auth/tsconfig.json @@ -5,5 +5,5 @@ "outDir": "dist", "declarationDir": "dist" }, - "references": [{ "path": "../ssi-types" }, {"path": "../presentation-exchange"}, { "path": "../ssi-sdk-core" }, { "path": "../did-utils" }] + "references": [{ "path": "../ssi-types" }, { "path": "../presentation-exchange" }, { "path": "../ssi-sdk-core" }, { "path": "../did-utils" }] } diff --git a/packages/siopv2-oid4vp-rp-auth/README.md b/packages/siopv2-oid4vp-rp-auth/README.md index 595235c4a..182c13d07 100644 --- a/packages/siopv2-oid4vp-rp-auth/README.md +++ b/packages/siopv2-oid4vp-rp-auth/README.md @@ -27,7 +27,6 @@ For this plugin a DID resolver is also required. A DID resolver can be added to ## Available functions - ## Usage ### Adding the plugin to an agent: diff --git a/packages/siopv2-oid4vp-rp-auth/__tests__/restAgent.test.ts b/packages/siopv2-oid4vp-rp-auth/__tests__/restAgent.test.ts index 24f9678d3..d884e5b5f 100644 --- a/packages/siopv2-oid4vp-rp-auth/__tests__/restAgent.test.ts +++ b/packages/siopv2-oid4vp-rp-auth/__tests__/restAgent.test.ts @@ -8,7 +8,7 @@ import { Server } from 'http' import { AgentRouter, RequestWithAgentRouter } from '@veramo/remote-server' import { getConfig } from '@veramo/cli/build/setup' import { createObjects } from '@veramo/cli/build/lib/objectCreator' -import { Siopv2RelyingParty, ISiopv2RelyingParty } from '../src' +import { SIOPv2RP, ISIOPv2RP } from '../src' import { Resolver } from 'did-resolver' import { getDidKeyResolver } from '@veramo/did-provider-key' import { DIDResolverPlugin } from '@veramo/did-resolver' @@ -41,10 +41,10 @@ const presentationSignCallback: PresentationSignCallback = async (args) => { } const getAgent = (options?: IAgentOptions) => - createAgent({ + createAgent({ ...options, plugins: [ - new Siopv2RelyingParty(presentationSignCallback), + new SIOPv2RP(presentationSignCallback), new DIDResolverPlugin({ resolver: new Resolver({ ...getDidKeyResolver(), diff --git a/packages/siopv2-oid4vp-rp-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts b/packages/siopv2-oid4vp-rp-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts index 809bfbc8e..fa2ca68c4 100644 --- a/packages/siopv2-oid4vp-rp-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts +++ b/packages/siopv2-oid4vp-rp-auth/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts @@ -1,6 +1,6 @@ import * as fs from 'fs' import { IDataStore, TAgent, VerifiableCredential } from '@veramo/core' -import { IAuthRequestDetails, ISiopv2RelyingParty, IPresentationWithDefinition } from '../../src' +import { IAuthRequestDetails, ISIOPv2RP, IPresentationWithDefinition } from '../../src' import { AuthorizationRequest, OP, @@ -37,7 +37,7 @@ jest.mock('@sphereon/ssi-sdk-did-utils', () => ({ mapIdentifierKeysToDocWithJwkSupport: jest.fn(), })) -type ConfiguredAgent = TAgent +type ConfiguredAgent = TAgent const didMethod = 'ethr' const did = 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a' @@ -332,9 +332,7 @@ export default (testContext: { const pd_single: PresentationDefinitionWithLocation = getFileAsJson( './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' ) - const vp_single: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) + const vp_single: IPresentationWithDefinition = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json') const presentation = CredentialMapper.toWrappedVerifiablePresentation(vp_single.presentation) presentation.presentation.presentation_submission!.id = expect.any(String) @@ -363,9 +361,7 @@ export default (testContext: { const pdSingle: PresentationDefinitionWithLocation = getFileAsJson( './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' ) - const vpSingle: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) + const vpSingle: IPresentationWithDefinition = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json') const presentation = CredentialMapper.toWrappedVerifiablePresentation(vpSingle.presentation) presentation.presentation.presentation_submission!.id = expect.any(String) diff --git a/packages/siopv2-oid4vp-rp-auth/package.json b/packages/siopv2-oid4vp-rp-auth/package.json index 3ee0837b2..69db8591a 100644 --- a/packages/siopv2-oid4vp-rp-auth/package.json +++ b/packages/siopv2-oid4vp-rp-auth/package.json @@ -6,7 +6,7 @@ "types": "dist/index.d.ts", "veramo": { "pluginInterfaces": { - "ISiopv2RelyingParty": "./src/types/ISiopv2RelyingParty.ts" + "ISIOPv2RP": "./src/types/ISIOPv2RP.ts" } }, "scripts": { @@ -58,7 +58,7 @@ "DID", "SIOP", "SIOPv2", - "OIDC4VP", + "OID4VP", "Presentation Exchange", "OpenID Connect", "Authenticator" diff --git a/packages/siopv2-oid4vp-rp-auth/src/RPInstance.ts b/packages/siopv2-oid4vp-rp-auth/src/RPInstance.ts index c52e7f646..aef42700d 100644 --- a/packages/siopv2-oid4vp-rp-auth/src/RPInstance.ts +++ b/packages/siopv2-oid4vp-rp-auth/src/RPInstance.ts @@ -1,49 +1,91 @@ -import { RP } from '@sphereon/did-auth-siop' -import { IPEXOptions, IRequiredContext, IRPOptions } from './types/ISiopv2RelyingParty' +import { AuthorizationRequest, RP, URI } from '@sphereon/did-auth-siop' +import { ICreateAuthRequestArgs, IPEXOptions, IRequiredContext, IRPOptions } from './types/ISIOPv2RP' import { IPresentationDefinition } from '@sphereon/pex' -import { createRPBuilder } from './functions' +import { createRPBuilder, getRequestVersion } from './functions' +import { v4 as uuidv4 } from 'uuid' export class RPInstance { - private readonly context: IRequiredContext private _rp: RP | undefined private readonly _pexOptions: IPEXOptions | undefined private readonly _rpOptions: IRPOptions - public constructor({ rpOpts, pexOpts }: { - rpOpts: IRPOptions, - pexOpts?: IPEXOptions - }, context: IRequiredContext) { + public constructor({ rpOpts, pexOpts }: { rpOpts: IRPOptions; pexOpts?: IPEXOptions }) { this._rpOptions = rpOpts this._pexOptions = pexOpts - this.context = context } - - public async get(): Promise { + public async get(context: IRequiredContext): Promise { if (!this._rp) { - const builder = await createRPBuilder({ rpOpts: this._rpOptions, pexOpts: this._pexOptions, context: this.context }) + const builder = await createRPBuilder({ + rpOpts: this._rpOptions, + pexOpts: this._pexOptions, + context, + }) this._rp = builder.build() } return this._rp! } - get rpOptions() { return this._rpOptions } - get pexOptions() { return this._pexOptions } + public hasDefinition(): boolean { + return this.definitionId !== undefined + } + get definitionId(): string | undefined { return this.pexOptions?.definitionId } - public getPresentationDefinition(): IPresentationDefinition | undefined { - return this.pexOptions?.definition + public async getPresentationDefinition(context: IRequiredContext): Promise { + return this.definitionId + ? await context.agent.pexStoreGetDefinition({ + definitionId: this.definitionId, + storeId: this.pexOptions?.storeId, + namespace: this.pexOptions?.storeNamespace, + }) + : undefined } -} + public async createAuthorizationRequestURI(createArgs: Omit, context: IRequiredContext): Promise { + const { correlationId, claims, requestByReferenceURI, redirectURI } = createArgs + const nonce = createArgs.nonce ?? uuidv4() + const state = createArgs.state ?? correlationId + return await this.get(context).then((rp) => + rp.createAuthorizationRequestURI({ + version: getRequestVersion(this.rpOptions), + correlationId, + nonce, + state, + claims, + requestByReferenceURI, + redirectURI, + }) + ) + } + public async createAuthorizationRequest( + createArgs: Omit, + context: IRequiredContext + ): Promise { + const { correlationId, claims, requestByReferenceURI, redirectURI } = createArgs + const nonce = createArgs.nonce ?? uuidv4() + const state = createArgs.state ?? correlationId + return await this.get(context).then((rp) => + rp.createAuthorizationRequest({ + version: getRequestVersion(this.rpOptions), + correlationId, + nonce, + state, + claims, + requestByReferenceURI, + redirectURI, + }) + ) + } +} diff --git a/packages/siopv2-oid4vp-rp-auth/src/agent/SIOPv2RP.ts b/packages/siopv2-oid4vp-rp-auth/src/agent/SIOPv2RP.ts new file mode 100644 index 000000000..890be402c --- /dev/null +++ b/packages/siopv2-oid4vp-rp-auth/src/agent/SIOPv2RP.ts @@ -0,0 +1,148 @@ +import { + IAuthorizationRequestPayloads, + ICreateAuthRequestArgs, + IGetAuthRequestStateArgs, + IGetAuthResponseStateArgs, + IPEXInstanceOptions, + IRequiredContext, + IRPOptions, + ISiopRPInstanceArgs, + ISiopv2RPOpts, IUpdateRequestStateArgs, IVerifyAuthResponseStateArgs, + schema, +} from '../index' +import { IAgentPlugin } from '@veramo/core' + +import { ISIOPv2RP } from '../types/ISIOPv2RP' +import { RPInstance } from '../RPInstance' +import { + AuthorizationRequestState, + AuthorizationResponsePayload, + VerifiedAuthorizationResponse, +} from '@sphereon/did-auth-siop/dist/main/types' +import { AuthorizationResponseState, decodeUriAsJson } from '@sphereon/did-auth-siop' +import { AuthorizationRequestStateStatus } from '@sphereon/ssi-sdk-siopv2-oid4vp-common' + +export class SIOPv2RP implements IAgentPlugin { + private readonly opts: ISiopv2RPOpts + private static readonly _DEFAULT_OPTS_KEY = '_default' + private readonly instances: Map = new Map() + readonly schema = schema.IDidAuthSiopOpAuthenticator + + readonly methods: ISIOPv2RP = { + siopCreateAuthRequestURI: this.createAuthorizationRequestURI.bind(this), + siopCreateAuthRequestPayloads: this.createAuthorizationRequestPayloads.bind(this), + siopGetAuthRequestState: this.siopGetRequestState.bind(this), + siopGetAuthResponseState: this.siopGetResponseState.bind(this), + siopUpdateAuthRequestState: this.siopUpdateRequestState.bind(this), + siopDeleteAuthState: this.siopDeleteState.bind(this), + siopVerifyAuthResponse: this.siopVerifyAuthResponse.bind(this), + } + + constructor(opts: ISiopv2RPOpts) { + this.opts = opts + } + + private async createAuthorizationRequestURI(createArgs: ICreateAuthRequestArgs, context: IRequiredContext): Promise { + return await this.getRPInstance({ definitionId: createArgs.definitionId }, context) + .then((rp) => rp.createAuthorizationRequestURI(createArgs, context)) + .then((URI) => URI.encodedUri) + } + + private async createAuthorizationRequestPayloads( + createArgs: ICreateAuthRequestArgs, + context: IRequiredContext, + ): Promise { + return await this.getRPInstance({ definitionId: createArgs.definitionId }, context) + .then((rp) => rp.createAuthorizationRequest(createArgs, context)) + .then(async (request) => { + const authRequest: IAuthorizationRequestPayloads = { + authorizationRequest: request.payload, + requestObject: await request.requestObjectJwt(), + requestObjectDecoded: await request.requestObject?.getPayload(), + } + return authRequest + }) + } + + private async siopGetRequestState(args: IGetAuthRequestStateArgs, context: IRequiredContext): Promise { + return await this.getRPInstance({ definitionId: args.definitionId }, context) + .then((rp) => rp.get(context).then(rp => rp.sessionManager.getRequestStateByCorrelationId(args.correlationId, args.errorOnNotFound))) + } + + private async siopGetResponseState(args: IGetAuthResponseStateArgs, context: IRequiredContext): Promise { + return await this.getRPInstance({ definitionId: args.definitionId }, context) + .then((rp) => rp.get(context).then(rp => rp.sessionManager.getResponseStateByCorrelationId(args.correlationId, args.errorOnNotFound))) + } + + private async siopUpdateRequestState(args: IUpdateRequestStateArgs, context: IRequiredContext): Promise { + if (args.state !== AuthorizationRequestStateStatus.SENT) { + throw Error(`Only 'sent' status is support for this method at this point`) + } + return await this.getRPInstance({ definitionId: args.definitionId }, context) + // todo: In the SIOP library we need to update the signal method to be more like this method + .then((rp) => rp.get(context).then(async rp => { + await rp.signalAuthRequestRetrieved({ + correlationId: args.correlationId, + error: args.error ? new Error(args.error) : undefined, + }) + return await rp.sessionManager.getRequestStateByCorrelationId(args.correlationId, true) as AuthorizationRequestState + })) + + } + + + private async siopDeleteState(args: IGetAuthResponseStateArgs, context: IRequiredContext): Promise { + return await this.getRPInstance({ definitionId: args.definitionId }, context) + .then((rp) => rp.get(context).then(rp => rp.sessionManager.deleteStateForCorrelationId(args.correlationId))).then(() => true) + } + + + private async siopVerifyAuthResponse(args: IVerifyAuthResponseStateArgs, context: IRequiredContext): Promise { + const authResponse = decodeUriAsJson(args.authorizationResponse) as AuthorizationResponsePayload + return await this.getRPInstance({ definitionId: args.definitionId }, context) + .then((rp) => rp.get(context).then(rp => rp.verifyAuthorizationResponse(authResponse, { + correlationId: args.correlationId, + presentationDefinitions: args.presentationDefinitions, + audience: args.audience, + }))) + } + + + async getRPInstance(args: ISiopRPInstanceArgs, context: IRequiredContext): Promise { + const definitionId = args.definitionId + const instanceId = definitionId ?? SIOPv2RP._DEFAULT_OPTS_KEY + if (!this.instances.has(instanceId)) { + const instanceOpts = this.getInstanceOpts(definitionId) + const rpOpts = await this.getRPOptions(definitionId) + + /*const definition = args.definition ?? (definitionId ? await context.agent.pexStoreGetDefinition({ + definitionId, + storeId, + namespace: storeNamespace, + }) : undefined)*/ + if (instanceOpts?.definition) { + await context.agent.pexStorePersistDefinition({ + definitionId, + definition: instanceOpts.definition, + storeId: instanceOpts.storeId, + namespace: instanceOpts.storeNamespace, + validation: true, + }) + } + this.instances.set(instanceId, new RPInstance({ rpOpts, pexOpts: instanceOpts })) + } + return this.instances.get(instanceId)! + } + + async getRPOptions(definitionId?: string): Promise { + const options = this.getInstanceOpts(definitionId)?.rpOpts ?? this.opts.defaultOpts + if (!options) { + throw Error(`Could not get specific nor default options for definition ${definitionId}`) + } + return options + } + + getInstanceOpts(definitionId?: string): IPEXInstanceOptions | undefined { + return definitionId ? this.opts.instanceOpts?.find((i) => i.definitionId === definitionId) : undefined + } +} diff --git a/packages/siopv2-oid4vp-rp-auth/src/agent/Siopv2RelyingParty.ts b/packages/siopv2-oid4vp-rp-auth/src/agent/Siopv2RelyingParty.ts deleted file mode 100644 index 2ae723ed4..000000000 --- a/packages/siopv2-oid4vp-rp-auth/src/agent/Siopv2RelyingParty.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - IPEXDefinitionPersistArgs, - IPEXInstanceOptions, - IRequiredContext, - IRPOptions, ISiopRPInstanceArgs, - ISiopv2RPOpts, - schema, VersionDiscoveryResult, -} from '../index' -import { IAgentPlugin } from '@veramo/core' - -import { ISiopv2RelyingParty } from '../types/ISiopv2RelyingParty' -import { IKeyValueStore, IValueData, KeyValueStore } from '@veramo/kv-store' -import { IPresentationDefinition, PEX } from '@sphereon/pex' -import { RPInstance } from '../RPInstance' - -export class Siopv2RelyingParty implements IAgentPlugin { - private static PEX = new PEX() - private readonly _definitionStore: IKeyValueStore - private readonly opts: ISiopv2RPOpts - private readonly _optionsStore: IKeyValueStore - private static readonly _DEFAULT_OPTS_KEY = '_default' - readonly schema = schema.IDidAuthSiopOpAuthenticator - - readonly methods: ISiopv2RelyingParty = { - pexDefinitionGet: this.pexDefinitionGet.bind(this), - pexDefinitionPersist: this.pexDefinitionPersist.bind(this), - pexDefinitionExists: this.pexDefinitionExists.bind(this), - pexDefinitionVersion: this.pexDefinitionVersion.bind(this), - siopRPInstance: this.siopRPInstance.bind(this), - } - - constructor( - opts: ISiopv2RPOpts, - ) { - this.opts = opts - this._optionsStore = opts.optionsStore ?? new KeyValueStore({ - namespace: 'siop-rp-opts', - store: new Map, - }) - this.opts.optionsStore = this._optionsStore - this._definitionStore = opts.definitionStore ?? new KeyValueStore({ - namespace: 'siop-rp-defs', - store: new Map(), - }) - this.opts.definitionStore = this._definitionStore - if (opts.defaultOpts) { - this.optionsStore.set(Siopv2RelyingParty._DEFAULT_OPTS_KEY, opts.defaultOpts) - } - if (Array.isArray(opts.instanceOpts)) { - for (const instance of opts.instanceOpts) { - const instanceOpts = JSON.parse(JSON.stringify(instance)) as IPEXInstanceOptions - const definition = instanceOpts.definition - if (definition) { - this.definitionStore.set(instanceOpts.definitionId, definition) - delete instanceOpts.definition - } - if (instanceOpts.rpOpts) { - this.optionsStore.set(instanceOpts.definitionId, instanceOpts.rpOpts) - } - } - } - } - - - - async siopRPOptionsGet(definitionId?: string): Promise { - const options = await this.optionsStore.get(definitionId ?? Siopv2RelyingParty._DEFAULT_OPTS_KEY) ?? await this.optionsStore.get(Siopv2RelyingParty._DEFAULT_OPTS_KEY) - if (!options) { - throw Error(`Could not get specific nor default options for definition ${definitionId}`) - } - return options - } - - async siopRPInstance(args: ISiopRPInstanceArgs, context: IRequiredContext): Promise { - const { definitionId } = args - const rpOpts = await this.siopRPOptionsGet(definitionId ?? Siopv2RelyingParty._DEFAULT_OPTS_KEY) - const definition = definitionId ? await this.pexDefinitionGet(definitionId) : undefined - return new RPInstance({ rpOpts, ...(definitionId ? { pexOpts: { definitionId, definition } } : {}) }, context) - } - - - private get optionsStore(): IKeyValueStore { - return this._optionsStore - } - - - public getDefinitionRP(definitionId: string) { - - } - -} diff --git a/packages/siopv2-oid4vp-rp-auth/src/functions.ts b/packages/siopv2-oid4vp-rp-auth/src/functions.ts index c1b14c727..474a9a5ce 100644 --- a/packages/siopv2-oid4vp-rp-auth/src/functions.ts +++ b/packages/siopv2-oid4vp-rp-auth/src/functions.ts @@ -1,6 +1,6 @@ -import { IDIDOptions, IIdentifierOpts, IPEXOptions, IRequiredContext, IRPOptions } from './types/ISiopv2RelyingParty' +import { IPEXOptions, IRequiredContext, IRPOptions, ISIOPDIDOptions } from './types/ISIOPv2RP' import { EventEmitter } from 'events' -import { AgentDIDResolver, getAgentDIDMethods, mapIdentifierKeysToDocWithJwkSupport } from '@sphereon/ssi-sdk-did-utils' +import { AgentDIDResolver, determineKid, getDID, getIdentifier, getKey, getSupportedDIDMethods, IDIDOptions } from '@sphereon/ssi-sdk-did-utils' import { KeyAlgo, SuppliedSigner } from '@sphereon/ssi-sdk-core' import { CheckLinkedDomain, @@ -16,12 +16,10 @@ import { SubjectType, SupportedVersion, } from '@sphereon/did-auth-siop' -import { IPresentationDefinition } from '@sphereon/pex' -import { DIDDocumentSection, IIdentifier, IKey, TKeyType } from '@veramo/core' -import { _ExtendedIKey } from '@veramo/utils' +import { TKeyType } from '@veramo/core' import { IVerifyCallbackArgs, IVerifyCredentialResult } from '@sphereon/wellknown-dids-client' import { PresentationVerificationResult } from '@sphereon/did-auth-siop/dist/main/authorization-response/types' - +import { IPresentationDefinition } from '@sphereon/pex' /* export async function getPresentationDefinitionStore(pexOptions?: IPEXOptions): Promise | undefined> { @@ -39,12 +37,13 @@ export async function getPresentationDefinitionStore(pexOptions?: IPEXOptions): } */ +/* export async function getPresentationDefinition(pexOptions?: IPEXOptions): Promise { return pexOptions?.definition - /*const store = await getPresentationDefinitionStore(pexOptions) - return store && pexOptions?.definitionId ? store.get(pexOptions?.definitionId) : undefined*/ + /!*const store = await getPresentationDefinitionStore(pexOptions) + return store && pexOptions?.definitionId ? store.get(pexOptions?.definitionId) : undefined*!/ } - +*/ export function getRequestVersion(rpOptions: IRPOptions): SupportedVersion { if (Array.isArray(rpOptions.supportedVersions) && rpOptions.supportedVersions.length > 0) { @@ -53,38 +52,13 @@ export function getRequestVersion(rpOptions: IRPOptions): SupportedVersion { return SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1 } - -export function getDID(didOptions: IDIDOptions): string { - if (typeof didOptions.identifierOpts.identifier === 'string') { - return didOptions.identifierOpts.identifier - } else if (typeof didOptions.identifierOpts.identifier === 'object') { - return didOptions.identifierOpts.identifier.did - } - throw Error(`Cannot get DID from identifier value`) -} - - -export async function getIdentifier(didOptions: IDIDOptions, context: IRequiredContext): Promise { - if (typeof didOptions.identifierOpts.identifier === 'string') { - return context.agent.didManagerGet({ did: didOptions.identifierOpts.identifier }) - } else if (typeof didOptions.identifierOpts.identifier === 'object') { - return didOptions.identifierOpts.identifier - } - throw Error(`Cannot get agent identifier value from options`) -} - - -export async function getSupportedDIDMethods(didOpts: IDIDOptions, context: IRequiredContext) { - return didOpts.supportedDIDMethods ?? (await getAgentDIDMethods(context)) -} - -function getWellKnownDIDVerifyCallback(didOpts: IDIDOptions, context: IRequiredContext) { +function getWellKnownDIDVerifyCallback(didOpts: ISIOPDIDOptions, context: IRequiredContext) { return didOpts.wellknownDIDVerifyCallback ? didOpts.wellknownDIDVerifyCallback : async (args: IVerifyCallbackArgs): Promise => { - const result = await context.agent.verifyCredential({ credential: args.credential, fetchRemoteContexts: true }) - return { verified: result.verified } - } + const result = await context.agent.verifyCredential({ credential: args.credential, fetchRemoteContexts: true }) + return { verified: result.verified } + } } export function getPresentationVerificationCallback(didOpts: IDIDOptions, context: IRequiredContext) { @@ -92,7 +66,7 @@ export function getPresentationVerificationCallback(didOpts: IDIDOptions, contex const result = await context.agent.verifyPresentation({ presentation: args, fetchRemoteContexts: true, - domain: getDID(didOpts), + domain: getDID(didOpts.identifierOpts), }) console.log(`VP verification result: ${JSON.stringify(result, null, 2)}`) return { verified: result.verified } @@ -101,20 +75,26 @@ export function getPresentationVerificationCallback(didOpts: IDIDOptions, contex return presentationVerificationCallback } -export async function createRPBuilder({ - rpOpts, - pexOpts, - context, - }: { +export async function createRPBuilder(args: { rpOpts: IRPOptions pexOpts?: IPEXOptions | undefined + definition?: IPresentationDefinition context: IRequiredContext }) { + const { rpOpts, pexOpts, context } = args const { didOpts } = rpOpts - const definition = !!pexOpts ? await getPresentationDefinition(pexOpts) : undefined - const did = getDID(didOpts) + const definition = + args.definition ?? + (!!pexOpts && pexOpts.definitionId + ? await context.agent.pexStoreGetDefinition({ + definitionId: pexOpts.definitionId, + storeId: pexOpts.storeId, + namespace: pexOpts.storeNamespace, + }) + : undefined) + const did = getDID(didOpts.identifierOpts) const didMethods = await getSupportedDIDMethods(didOpts, context) - const identifier = await getIdentifier(didOpts, context) + const identifier = await getIdentifier(didOpts.identifierOpts, context) const key = await getKey(identifier, didOpts.identifierOpts.verificationMethodSection, context, didOpts.identifierOpts.kid) const kid = determineKid(key, didOpts.identifierOpts) @@ -124,16 +104,19 @@ export async function createRPBuilder({ .withScope('openid', PropertyTarget.REQUEST_OBJECT) .withResponseMode(rpOpts.responseMode ?? ResponseMode.POST) .withResponseType(ResponseType.ID_TOKEN, PropertyTarget.REQUEST_OBJECT) - .withCustomResolver(rpOpts.didOpts.resolveOpts?.resolver ?? new AgentDIDResolver(context, rpOpts.didOpts.resolveOpts?.noUniversalResolverFallback !== false)) + .withCustomResolver( + rpOpts.didOpts.resolveOpts?.resolver ?? new AgentDIDResolver(context, rpOpts.didOpts.resolveOpts?.noUniversalResolverFallback !== false) + ) .withClientId(did, PropertyTarget.REQUEST_OBJECT) // todo: move to options fill/correct method - .withSupportedVersions(rpOpts.supportedVersions ?? [SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1, SupportedVersion.SIOPv2_ID1, SupportedVersion.SIOPv2_D11]) + .withSupportedVersions( + rpOpts.supportedVersions ?? [SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1, SupportedVersion.SIOPv2_ID1, SupportedVersion.SIOPv2_D11] + ) .withEventEmitter(eventEmitter) .withSessionManager(rpOpts.sessionManager ?? new InMemoryRPSessionManager(eventEmitter)) .withClientMetadata( { - //FIXME: All of the below should be configurable. Some should come from builder, some should be determined by the agent idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256, SigningAlgo.ES256K], // added newly requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256, SigningAlgo.ES256K], // added newly @@ -145,16 +128,16 @@ export async function createRPBuilder({ }, scopesSupported: [Scope.OPENID_DIDAUTHN], subjectTypesSupported: [SubjectType.PAIRWISE], - subject_syntax_types_supported: didMethods.map(method => `did:${method}`), + subject_syntax_types_supported: didMethods.map((method) => `did:${method}`), passBy: PassBy.VALUE, - }, PropertyTarget.REQUEST_OBJECT, + }, + PropertyTarget.REQUEST_OBJECT ) .withCheckLinkedDomain(didOpts.checkLinkedDomains ?? CheckLinkedDomain.IF_PRESENT) .withRevocationVerification(RevocationVerification.NEVER) .withPresentationVerification(getPresentationVerificationCallback(didOpts, context)) - didMethods.forEach((method) => builder.addDidMethod(method)) builder.withWellknownDIDVerifyCallback(getWellKnownDIDVerifyCallback(didOpts, context)) @@ -162,51 +145,15 @@ export async function createRPBuilder({ builder.withPresentationDefinition({ definition }, PropertyTarget.REQUEST_OBJECT) } - - builder.withSuppliedSignature( - SuppliedSigner(key, context, getSigningAlgo(key.type) as unknown as KeyAlgo), - did, - kid, - getSigningAlgo(key.type), - ) - + builder.withSuppliedSignature(SuppliedSigner(key, context, getSigningAlgo(key.type) as unknown as KeyAlgo), did, kid, getSigningAlgo(key.type)) return builder } -export async function createRP({ - rpOptions, - context, - }: { - rpOptions: IRPOptions - context: IRequiredContext -}): Promise { +export async function createRP({ rpOptions, context }: { rpOptions: IRPOptions; context: IRequiredContext }): Promise { return (await createRPBuilder({ rpOpts: rpOptions, context })).build() } -export async function getKey( - identifier: IIdentifier, - verificationMethodSection: DIDDocumentSection = 'authentication', - context: IRequiredContext, - keyId?: string, -): Promise { - const keys = await mapIdentifierKeysToDocWithJwkSupport(identifier, verificationMethodSection, context) - if (!keys || keys.length === 0) { - throw new Error(`No keys found for verificationMethodSection: ${verificationMethodSection} and did ${identifier.did}`) - } - - const identifierKey = keyId ? keys.find((key: _ExtendedIKey) => key.kid === keyId || key.meta.verificationMethod.id === keyId) : keys[0] - if (!identifierKey) { - throw new Error(`No matching verificationMethodSection key found for keyId: ${keyId}`) - } - - return identifierKey -} - -export function determineKid(key: IKey, idOpts: IIdentifierOpts): string { - return key.meta?.verificationMethod.id ?? idOpts.kid ?? key.kid -} - export function getSigningAlgo(type: TKeyType): SigningAlgo { switch (type) { case 'Ed25519': diff --git a/packages/siopv2-oid4vp-rp-auth/src/index.ts b/packages/siopv2-oid4vp-rp-auth/src/index.ts index a7212a7ce..c2081e544 100644 --- a/packages/siopv2-oid4vp-rp-auth/src/index.ts +++ b/packages/siopv2-oid4vp-rp-auth/src/index.ts @@ -3,5 +3,5 @@ */ const schema = require('../plugin.schema.json') export { schema } -export { Siopv2RelyingParty } from './agent/Siopv2RelyingParty' -export * from './types/ISiopv2RelyingParty' +export { SIOPv2RP } from './agent/SIOPv2RP' +export * from './types/ISIOPv2RP' diff --git a/packages/siopv2-oid4vp-rp-auth/src/types/ISIOPv2RP.ts b/packages/siopv2-oid4vp-rp-auth/src/types/ISIOPv2RP.ts new file mode 100644 index 000000000..c23bb59aa --- /dev/null +++ b/packages/siopv2-oid4vp-rp-auth/src/types/ISIOPv2RP.ts @@ -0,0 +1,160 @@ +import { + IAgentContext, + ICredentialIssuer, + ICredentialVerifier, + IDataStoreORM, + IDIDManager, + IKeyManager, + IPluginMethodMap, + IResolver, +} from '@veramo/core' +import { W3CVerifiablePresentation } from '@sphereon/ssi-types' +import { + AuthorizationRequestPayload, + AuthorizationResponseState, + CheckLinkedDomain, + IRPSessionManager, + PresentationDefinitionWithLocation, + PresentationVerificationCallback, + RequestObjectPayload, + ResponseMode, + SupportedVersion, + VerifiablePresentationTypeFormat, + VPTokenLocation, +} from '@sphereon/did-auth-siop' + +import { Resolvable } from 'did-resolver' +import { DIDDocument } from '@sphereon/did-uni-client' +import { EventEmitter } from 'events' +import { IPresentationDefinition } from '@sphereon/pex' +import { IDIDOptions } from '@sphereon/ssi-sdk-did-utils' +import { IPresentationExchange } from '@sphereon/ssi-sdk-presentation-exchange' +import { VerifyCallback } from '@sphereon/wellknown-dids-client' +import { ClaimPayloadCommonOpts } from '@sphereon/did-auth-siop/dist/main/authorization-request/types' +import { AuthorizationRequestState, VerifiedAuthorizationResponse } from '@sphereon/did-auth-siop/dist/main/types' +import { AuthorizationRequestStateStatus } from '@sphereon/ssi-sdk-siopv2-oid4vp-common' + +export interface ISIOPv2RP extends IPluginMethodMap { + siopCreateAuthRequestURI(createArgs: ICreateAuthRequestArgs, context: IRequiredContext): Promise + siopCreateAuthRequestPayloads(createArgs: ICreateAuthRequestArgs, context: IRequiredContext): Promise + siopGetAuthRequestState(args: IGetAuthRequestStateArgs, context: IRequiredContext): Promise + siopGetAuthResponseState(args: IGetAuthResponseStateArgs, context: IRequiredContext): Promise + siopUpdateAuthRequestState(args: IUpdateRequestStateArgs, context: IRequiredContext): Promise + siopDeleteAuthState(args: IDeleteAuthStateArgs, context: IRequiredContext): Promise + siopVerifyAuthResponse(args: IVerifyAuthResponseStateArgs, context: IRequiredContext): Promise; +} + +export interface ISiopv2RPOpts { + defaultOpts?: IRPDefaultOpts + instanceOpts?: IPEXInstanceOptions[] +} + +export interface IRPDefaultOpts extends IRPOptions {} + +export interface ICreateAuthRequestArgs { + definitionId: string + correlationId: string + redirectURI: string + requestByReferenceURI?: string + nonce?: string + state?: string + claims?: ClaimPayloadCommonOpts +} + +export interface IGetAuthRequestStateArgs { + correlationId: string + definitionId: string + errorOnNotFound?: boolean +} + +export interface IGetAuthResponseStateArgs { + correlationId: string + definitionId: string + errorOnNotFound?: boolean + progressRequestStateTo?: AuthorizationRequestStateStatus +} + +export interface IUpdateRequestStateArgs { + definitionId: string, + correlationId: string, + state: AuthorizationRequestStateStatus, + error?: string +} + +export interface IDeleteAuthStateArgs { + correlationId: string + definitionId: string +} + +export interface IVerifyAuthResponseStateArgs { + authorizationResponse: string; + definitionId?: string + correlationId: string; + audience?: string; + presentationDefinitions?: PresentationDefinitionWithLocation | PresentationDefinitionWithLocation[]; +} + +export interface IAuthorizationRequestPayloads { + authorizationRequest: AuthorizationRequestPayload + requestObject?: string + requestObjectDecoded?: RequestObjectPayload +} + +export interface IPEXDefinitionPersistArgs extends IPEXInstanceOptions { + definition: IPresentationDefinition + ttl?: number +} + +export interface ISiopRPInstanceArgs { + definitionId?: string +} + +export interface IPEXInstanceOptions extends IPEXOptions { + rpOpts?: IRPOptions + definition?: IPresentationDefinition +} + +export interface IRPOptions { + responseMode?: ResponseMode + supportedVersions?: SupportedVersion[] // The supported version by the RP. The first version will be the default version + sessionManager?: IRPSessionManager + expiresIn?: number + eventEmitter?: EventEmitter + didOpts: ISIOPDIDOptions +} + +export interface IPEXOptions { + presentationVerifyCallback?: PresentationVerificationCallback + // definition?: IPresentationDefinition + definitionId: string + storeId?: string + storeNamespace?: string +} + +export interface PerDidResolver { + didMethod: string + resolver: Resolvable +} + +export interface IAuthRequestDetails { + rpDIDDocument?: DIDDocument + id: string + verifiablePresentationMatches: IPresentationWithDefinition[] + alsoKnownAs?: string[] +} + +export interface IPresentationWithDefinition { + location: VPTokenLocation + definition: PresentationDefinitionWithLocation + format: VerifiablePresentationTypeFormat + presentation: W3CVerifiablePresentation +} + +export interface ISIOPDIDOptions extends IDIDOptions { + checkLinkedDomains?: CheckLinkedDomain + wellknownDIDVerifyCallback?: VerifyCallback +} + +export type IRequiredContext = IAgentContext< + IDataStoreORM & IResolver & IDIDManager & IKeyManager & ICredentialIssuer & ICredentialVerifier & IPresentationExchange +> diff --git a/packages/siopv2-oid4vp-rp-auth/src/types/ISiopv2RelyingParty.ts b/packages/siopv2-oid4vp-rp-auth/src/types/ISiopv2RelyingParty.ts deleted file mode 100644 index f57ecf530..000000000 --- a/packages/siopv2-oid4vp-rp-auth/src/types/ISiopv2RelyingParty.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - DIDDocumentSection, - IAgentContext, - ICredentialIssuer, - ICredentialVerifier, - IDataStoreORM, - IDIDManager, - IIdentifier, - IKeyManager, - IPluginMethodMap, - IResolver, -} from '@veramo/core' -import { W3CVerifiablePresentation } from '@sphereon/ssi-types' -import { - CheckLinkedDomain, - IRPSessionManager, - PresentationDefinitionWithLocation, - PresentationVerificationCallback, - ResolveOpts, - ResponseMode, - SupportedVersion, - VerifiablePresentationTypeFormat, - VPTokenLocation, -} from '@sphereon/did-auth-siop' -import { VerifyCallback } from '@sphereon/wellknown-dids-client' - -import { Resolvable } from 'did-resolver' -import { DIDDocument } from '@sphereon/did-uni-client' -import { EventEmitter } from 'events' -import { IKeyValueStore, IValueData } from '@veramo/kv-store' -import { IPresentationDefinition } from '@sphereon/pex' -import { RPInstance } from '../RPInstance' -import { PEVersion } from '@sphereon/pex/dist/main/lib/types' - - -export interface ISiopv2RelyingParty extends IPluginMethodMap { - pexDefinitionGet(definitionId: string): Promise - - pexDefinitionExists(definitionId: string): Promise - - pexDefinitionPersist(args: IPEXDefinitionPersistArgs): Promise> - - pexDefinitionVersion(presentationDefinition: IPresentationDefinition): Promise - - siopRPInstance(args: ISiopRPInstanceArgs, context: IRequiredContext): Promise -} - - -export interface ISiopv2RPOpts { - optionsStore?: IKeyValueStore - definitionStore?: IKeyValueStore - defaultOpts?: IRPDefaultOpts - instanceOpts?: IPEXInstanceOptions[] -} - -export interface IRPDefaultOpts extends IRPOptions { -} - - -export interface IPEXDefinitionPersistArgs extends IPEXInstanceOptions { - definition: IPresentationDefinition - ttl?: number -} - -export interface ISiopRPInstanceArgs { - definitionId?: string -} - -export interface IPEXInstanceOptions extends IPEXOptions { - rpOpts?: IRPOptions -} - -export interface IPEXOptions { - presentationVerifyCallback?: PresentationVerificationCallback - definition?: IPresentationDefinition - definitionId: string -} - -export interface PerDidResolver { - didMethod: string - resolver: Resolvable -} - - -export interface IAuthRequestDetails { - rpDIDDocument?: DIDDocument - id: string - verifiablePresentationMatches: IPresentationWithDefinition[] - alsoKnownAs?: string[] -} - - -export interface IPresentationWithDefinition { - location: VPTokenLocation - definition: PresentationDefinitionWithLocation - format: VerifiablePresentationTypeFormat - presentation: W3CVerifiablePresentation -} - - -export type IRequiredContext = IAgentContext - -export interface IRPOptions { - responseMode?: ResponseMode - supportedVersions?: SupportedVersion[] - sessionManager?: IRPSessionManager - expiresIn?: number - eventEmitter?: EventEmitter - didOpts: IDIDOptions -} - -export interface IDIDOptions { - resolveOpts?: ResolveOpts - identifierOpts: IIdentifierOpts - supportedDIDMethods?: string[] - checkLinkedDomains?: CheckLinkedDomain - wellknownDIDVerifyCallback?: VerifyCallback -} - - -export interface IIdentifierOpts { - identifier: IIdentifier | string - verificationMethodSection?: DIDDocumentSection - kid?: string -} - - -export interface VersionDiscoveryResult { - version?: PEVersion - error?: string -} diff --git a/packages/siopv2-oid4vp-rp-auth/tsconfig.json b/packages/siopv2-oid4vp-rp-auth/tsconfig.json index 6602f374e..4be833d8c 100644 --- a/packages/siopv2-oid4vp-rp-auth/tsconfig.json +++ b/packages/siopv2-oid4vp-rp-auth/tsconfig.json @@ -6,5 +6,11 @@ "declarationDir": "dist", "esModuleInterop": true }, - "references": [{ "path": "../siopv2-oid4vp-common" }, { "path": "../presentation-exchange" }, { "path": "../ssi-types" }, { "path": "../ssi-sdk-core" }, { "path": "../did-utils" }] + "references": [ + { "path": "../siopv2-oid4vp-common" }, + { "path": "../presentation-exchange" }, + { "path": "../ssi-types" }, + { "path": "../ssi-sdk-core" }, + { "path": "../did-utils" } + ] } diff --git a/packages/siopv2-oid4vp-rp-rest/README.md b/packages/siopv2-oid4vp-rp-rest/README.md index ba5ca2eaf..636f0c122 100644 --- a/packages/siopv2-oid4vp-rp-rest/README.md +++ b/packages/siopv2-oid4vp-rp-rest/README.md @@ -27,7 +27,6 @@ For this plugin a DID resolver is also required. A DID resolver can be added to ## Available functions - ## Usage ### Adding the plugin to an agent: diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/RestAPI.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/RestAPI.ts new file mode 100644 index 000000000..65a557a79 --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/RestAPI.ts @@ -0,0 +1,4 @@ +import agent from './agent' +import { SIOPv2RPRestAPI } from '../src' + +new SIOPv2RPRestAPI(agent) diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/agent.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/agent.ts new file mode 100644 index 000000000..e5a11639b --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/agent.ts @@ -0,0 +1,211 @@ +import { + createAgent, + ICredentialVerifier, + IDataStore, + IDataStoreORM, + IDIDManager, + IKeyManager, + IResolver, +} from '@veramo/core' +import { IonPublicKeyPurpose } from '@decentralized-identity/ion-sdk' +import { getUniResolver } from '@sphereon/did-uni-client' +import { + CredentialHandlerLDLocal, + ICredentialHandlerLDLocal, + LdDefaultContexts, + MethodNames, + SphereonBbsBlsSignature2020, + SphereonEd25519Signature2018, + SphereonEd25519Signature2020, + SphereonJsonWebSignature2020, +} from '@sphereon/ssi-sdk-vc-handler-ld-local' +import { CredentialPlugin } from '@veramo/credential-w3c' +import { DataStore, DataStoreORM, DIDStore, KeyStore, PrivateKeyStore } from '@veramo/data-store' +import { DIDManager } from '@veramo/did-manager' +import { EthrDIDProvider } from '@veramo/did-provider-ethr' +import { getDidIonResolver, IonDIDProvider } from '@veramo/did-provider-ion' +import { getDidKeyResolver, KeyDIDProvider } from '@veramo/did-provider-key' +import { DIDResolverPlugin } from '@veramo/did-resolver' +import { KeyManager } from '@veramo/key-manager' +import { KeyManagementSystem, SecretBox } from '@veramo/kms-local' +import { Resolver } from 'did-resolver' +import { DB_CONNECTION_NAME, DB_ENCRYPTION_KEY, getDbConnection } from './database' +import { ISIOPv2RP, SIOPv2RP } from '@sphereon/ssi-sdk-siopv2-oid4vp-rp-auth' +import { IPresentationExchange, PresentationExchange } from '@sphereon/ssi-sdk-presentation-exchange' +import { CheckLinkedDomain } from '@sphereon/did-auth-siop' +import { entraAndSphereonCompatibleDef, entraVerifiedIdPresentation } from './presentationDefinitions' + + +export const DIF_UNIRESOLVER_RESOLVE_URL = 'https://dev.uniresolver.io/1.0/identifiers' +export const APP_ID = 'sphereon:rp-demo' +export const DID_PREFIX = 'did' + +export enum KeyManagementSystemEnum { + LOCAL = 'local' +} + +export enum SupportedDidMethodEnum { + DID_ETHR = 'ethr', + DID_KEY = 'key', + // DID_LTO = 'lto', + DID_ION = 'ion', + // DID_FACTOM = 'factom', + DID_JWK = 'jwk' +} + +/*const COOKIE_SIGNING_KEY = '8E5er6YyAO6dIrDTm7BXYWsafBSLxzjb' +const BACKEND_BASE_URL = 'https://nk-gx-compliance.eu.ngrok.io' +const AUTH_REQUEST_EXPIRES_AFTER_SEC = 120*/ +const RP_PRIVATE_KEY_HEX = '851eb04ca3e2b2589d6f6a7287565816ee8e3126599bfeede8d3e93c53fb26e3' +// const RP_DID = 'did:ion:EiAG1fCl2kHSyZv7Z1Bb1eL7b_PVbiHaoxGki-5s8PjsFQ:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJhdXRoLWtleSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJmUUE3WUpNRk1qNXFET0RrS25qR1ZLNW0za1VSRFc1YnJ1TWhUa1NYSGQwIiwieSI6IlI3cVBNNEsxWHlqNkprM3M2a3I2aFNrQzlDa0ExSEFpMVFTejZqSU56dFkifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iLCJhc3NlcnRpb25NZXRob2QiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQmRpaVlrT3kyd3VOQ3Z5OWs4X1RoNzhSSlBvcy04MzZHZWpyRmJycTROZFEifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaUFTdTN1NGxsRk5KRkNEbTU5VFVBS1NSLTg3QUpsNFNzWEhlS05kbVRydXp3IiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlEZXBoWHJVQVdCcWswcnFBLTI3bE1ib08zMFZZVFdoV0Y0NHBlanJyXzNOQSJ9fQ' +// const RP_DID_SHORT = 'did:ion:EiAeobpQwEVpR-Ib9toYwbISQZZGIBck6zIUm0ZDmm9v0g' +const RP_DID = 'did:ion:EiAeobpQwEVpR-Ib9toYwbISQZZGIBck6zIUm0ZDmm9v0g:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJhdXRoLWtleSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJmUUE3WUpNRk1qNXFET0RrS25qR1ZLNW0za1VSRFc1YnJ1TWhUa1NYSGQwIiwieSI6IlI3cVBNNEsxWHlqNkprM3M2a3I2aFNrQzlDa0ExSEFpMVFTejZqSU56dFkifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iLCJhc3NlcnRpb25NZXRob2QiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQnpwN1loTjltaFVjWnNGZHhuZi1sd2tSVS1oVmJCdFpXc1ZvSkhWNmprd0EifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaUJvbWxvZ0JPOERROFdpVVFsa3diYmxuMXpsRFU2Q3Jvc01wNDRySjYzWHhBIiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlEQVFYU2k3SGNqSlZCWUFLZE8yenJNNEhmeWJtQkJDV3NsNlBRUEpfamtsQSJ9fQ' +const PRIVATE_RECOVERY_KEY_HEX = '7c90c0575643d09a370c35021c91e9d8af2c968c5f3a4bf73802693511a55b9f' +const PRIVATE_UPDATE_KEY_HEX = '7288a92f6219c873446abd1f8d26fcbbe1caa5274b47f6f086ef3e7e75dcad8b' +const RP_DID_KID = `${RP_DID}#auth-key` + + +export const resolver = new Resolver({ + /*// const SPHEREON_UNIRESOLVER_RESOLVE_URL = 'https://uniresolver.test.sphereon.io/1.0/identifiers' + ...getUniResolver('jwk', { + resolveUrl: DIF_UNIRESOLVER_RESOLVE_URL + }), + ...getUniResolver('ion', { + resolveUrl: DIF_UNIRESOLVER_RESOLVE_URL + }), + ..getUniResolver('lto', { + resolveUrl: SPHEREON_UNIRESOLVER_RESOLVE_URL + }),*/ + ...getUniResolver('ethr', { + resolveUrl: DIF_UNIRESOLVER_RESOLVE_URL, + }), + ...getDidKeyResolver(), + // ...getDidJwkResolver(), + ...getUniResolver('jwk', { + resolveUrl: DIF_UNIRESOLVER_RESOLVE_URL, + }), + ...getDidIonResolver(), +}) + +export const didProviders = { + [`${DID_PREFIX}:${SupportedDidMethodEnum.DID_ETHR}`]: new EthrDIDProvider({ + defaultKms: KeyManagementSystemEnum.LOCAL, + network: 'ropsten', + }), + [`${DID_PREFIX}:${SupportedDidMethodEnum.DID_KEY}`]: new KeyDIDProvider({ + defaultKms: KeyManagementSystemEnum.LOCAL, + }), + [`${DID_PREFIX}:${SupportedDidMethodEnum.DID_ION}`]: new IonDIDProvider({ + defaultKms: KeyManagementSystemEnum.LOCAL, + }), + /*[`${DID_PREFIX}:${SupportedDidMethodEnum.DID_JWK}`]: new JwkDIDProvider({ + defaultKms: KeyManagementSystemEnum.LOCAL + })*/ +} + +const dbConnection = getDbConnection(DB_CONNECTION_NAME) +const privateKeyStore: PrivateKeyStore = new PrivateKeyStore(dbConnection, new SecretBox(DB_ENCRYPTION_KEY)) + +const agent = createAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IPresentationExchange & + ISIOPv2RP & + ICredentialVerifier & + ICredentialHandlerLDLocal +>({ + plugins: [ + new DataStore(dbConnection), + new DataStoreORM(dbConnection), + new KeyManager({ + store: new KeyStore(dbConnection), + kms: { + local: new KeyManagementSystem(privateKeyStore), + }, + }), + new DIDManager({ + store: new DIDStore(dbConnection), + defaultProvider: `${DID_PREFIX}:${SupportedDidMethodEnum.DID_JWK}`, + providers: didProviders, + }), + new DIDResolverPlugin({ + resolver, + }), + new PresentationExchange(), + new SIOPv2RP({ + defaultOpts: { + didOpts: { + checkLinkedDomains: CheckLinkedDomain.IF_PRESENT, + identifierOpts: { + identifier: RP_DID, + kid: RP_DID_KID, + }, + }, + }, + instanceOpts: [ + { + definitionId: entraAndSphereonCompatibleDef.id, + definition: entraAndSphereonCompatibleDef, + }, + { + definitionId: entraVerifiedIdPresentation.id, + definition: entraVerifiedIdPresentation, + }, + ], + }), + new CredentialPlugin(), + new CredentialHandlerLDLocal({ + contextMaps: [LdDefaultContexts], + suites: [ + new SphereonEd25519Signature2018(), + new SphereonEd25519Signature2020(), + new SphereonBbsBlsSignature2020(), + new SphereonJsonWebSignature2020(), + ], + bindingOverrides: new Map([ + ['createVerifiableCredentialLD', MethodNames.createVerifiableCredentialLDLocal], + ['createVerifiablePresentationLD', MethodNames.createVerifiablePresentationLDLocal], + ]), + keyStore: privateKeyStore, + }), + ], +}) + +// agent.didManagerImport({did: RP_DID, keys: }) +agent.didManagerCreate({ + provider: 'did:ion', + alias: RP_DID, + options: { + kid: 'auth-key', + anchor: false, + recoveryKey: { + kid: 'recovery-test2', + key: { + privateKeyHex: PRIVATE_RECOVERY_KEY_HEX, + }, + }, + updateKey: { + kid: 'update-test2', + key: { + privateKeyHex: PRIVATE_UPDATE_KEY_HEX, + }, + }, + verificationMethods: [ + { + key: { + kid: 'auth-key', + privateKeyHex: RP_PRIVATE_KEY_HEX, + }, + purposes: [IonPublicKeyPurpose.Authentication, IonPublicKeyPurpose.AssertionMethod], + }, + ], + }, +}).then(value => { + console.log(`IDENTIFIER: ${value.did}`) +}).catch(reason => { + console.log(`WHOOPSIE: ${reason}`) +}) +export default agent diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/database/config.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/database/config.ts new file mode 100644 index 000000000..138446e10 --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/database/config.ts @@ -0,0 +1,24 @@ +import { Entities as VeramoDataStoreEntities, migrations as VeramoDataStoreMigrations } from '@veramo/data-store' +import { DataStoreConnectionEntities, DataStoreMigrations } from '@sphereon/ssi-sdk-data-store-common' +import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions' +import { + KeyValueStoreEntity, +} from '@veramo/kv-store' +import { kvStoreMigrations } from '@veramo/kv-store' + +const DB_CONNECTION_NAME = 'default' +const DB_ENCRYPTION_KEY = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' + +const sqliteConfig: SqliteConnectionOptions = { + type: 'sqlite', + database: '__tests__/database/test.sqlite', + entities: [...VeramoDataStoreEntities, ...DataStoreConnectionEntities, KeyValueStoreEntity], + migrations: [...VeramoDataStoreMigrations, ...DataStoreMigrations, ...kvStoreMigrations], + migrationsRun: false, // We run migrations from code to ensure proper ordering with Redux + synchronize: false, // We do not enable synchronize, as we use migrations from code + migrationsTransactionMode: 'each', // protect every migration with a separate transaction + logging: 'all', // 'all' means to enable all logging + logger: 'advanced-console' +} + +export {sqliteConfig, DB_CONNECTION_NAME, DB_ENCRYPTION_KEY} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/database/databaseService.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/database/databaseService.ts new file mode 100644 index 000000000..6775b2e6f --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/database/databaseService.ts @@ -0,0 +1,36 @@ +import Debug from 'debug' +import { DataSource } from 'typeorm' + +import { sqliteConfig } from './config' + +const debug = Debug(`demo:databaseService`) + +/** + * Todo, move to a class + */ +const dataSources = new Map() + +export const getDbConnection = async (dbName: string): Promise => { + if (sqliteConfig.synchronize) { + return Promise.reject( + `WARNING: Migrations need to be enabled in this app! Adjust the database configuration and set migrationsRun and synchronize to false` + ) + } + + if (dataSources.has(dbName)) { + return dataSources.get(dbName) + } + + const dataSource = await new DataSource({ ...sqliteConfig, name: dbName }).initialize() + dataSources.set(dbName, dataSource) + if (sqliteConfig.migrationsRun) { + debug( + `Migrations are currently managed from config. Please set migrationsRun and synchronize to false to get consistent behaviour. We run migrations from code explicitly` + ) + } else { + debug(`Running ${dataSource.migrations.length} migration(s) from code if needed...`) + await dataSource.runMigrations() + debug(`${dataSource.migrations.length} migration(s) from code were inspected and applied`) + } + return dataSource +} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/database/index.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/database/index.ts new file mode 100644 index 000000000..4b18e08ce --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/database/index.ts @@ -0,0 +1,2 @@ +export * from './config' +export * from './databaseService' diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/localAgent.test.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/localAgent.test.ts deleted file mode 100644 index 65f1f9aa3..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/localAgent.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as fs from 'fs' -import { getConfig } from '@veramo/cli/build/setup' -import { createObjects } from '@veramo/cli/build/lib/objectCreator' -import didAuthSiopOpAuthenticatorAgentLogic from './shared/didAuthSiopOpAuthenticatorAgentLogic' -import { PresentationSignCallback } from '@sphereon/did-auth-siop' - -jest.setTimeout(30000) - -function getFile(path: string) { - return fs.readFileSync(path, 'utf-8') -} - -function getFileAsJson(path: string) { - return JSON.parse(getFile(path)) -} - -let agent: any - -const presentationSignCallback: PresentationSignCallback = async (args) => { - const presentationSignProof = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/psc/psc.json') - - return { - ...args.presentation, - ...presentationSignProof, - } -} - -const setup = async (): Promise => { - const config = getConfig('packages/siopv2-openid4vp-op-auth/agent.yml') - config.agent.$args[0].plugins[1].$args[0] = presentationSignCallback - const { localAgent } = createObjects(config, { localAgent: '/agent' }) - agent = localAgent - - return true -} - -const tearDown = async (): Promise => { - return true -} - -const getAgent = () => agent -const testContext = { - getAgent, - setup, - tearDown, - isRestTest: false, -} - -xdescribe('Local integration tests', () => { - didAuthSiopOpAuthenticatorAgentLogic(testContext) -}) diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/presentationDefinitions.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/presentationDefinitions.ts new file mode 100644 index 000000000..70e8b6f4f --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/__tests__/presentationDefinitions.ts @@ -0,0 +1,97 @@ +import { IPresentationDefinition } from '@sphereon/pex' +import { Rules } from '@sphereon/pex-models' + + +export enum DefinitionIds { + ENTRA_VERIFIED_ID_ONLY = 'entra-only', + ENTRA_VERIFIED_ID_AND_SPHEREON = '9449e2db-791f-407c-b086-c21cc677d2e0' +} + + +export const entraAndSphereonCompatibleDef: IPresentationDefinition = { + + id: '9449e2db-791f-407c-b086-c21cc677d2e0', + purpose: 'You need to prove your Wallet Identity data', + submission_requirements: [{ + name: 'Sphereon Wallet Identity', + rule: Rules.Pick, + min: 0, + max: 1, + from: 'A', + }, + { + name: 'Microsoft Authenticator Identity', + rule: Rules.Pick, + min: 0, + max: 1, + from: 'B', + }], + input_descriptors: [{ + id: 'SphereonWalletId', + purpose: 'Checking your Sphereon Wallet information', + name: 'Wallet Identity', + group: ['A'], + schema: [{ uri: 'https://sphereon-opensource.github.io/ssi-mobile-wallet/context/sphereon-wallet-identity-v1.jsonld' }], + }, + { + 'id': 'TrueIdentity', + 'name': 'TrueIdentity', + group: ['B'], + 'purpose': 'To verify your demo identity', + 'schema': [ + { + 'uri': 'TrueIdentity', + }, + ], + 'constraints': { + 'fields': [ + { + 'path': [ + '$.issuer', + '$.vc.issuer', + '$.iss', + ], + 'filter': { + 'type': 'string', + 'pattern': 'did:ion:EiDXOEH-YmaP2ZvxoCI-lA5zT1i5ogjgH6foIc2LFC83nQ:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWdfODEwYmQ1Y2EiLCJwdWJsaWNLZXlKd2siOnsiY3J2Ijoic2VjcDI1NmsxIiwia3R5IjoiRUMiLCJ4IjoiRUZwd051UDMyMmJVM1dQMUR0Smd4NjdMMENVVjFNeE5peHFQVk1IMkw5USIsInkiOiJfZlNUYmlqSUpqcHNxTDE2Y0lFdnh4ZjNNYVlNWThNYnFFcTA2NnlWOWxzIn0sInB1cnBvc2VzIjpbImF1dGhlbnRpY2F0aW9uIiwiYXNzZXJ0aW9uTWV0aG9kIl0sInR5cGUiOiJFY2RzYVNlY3AyNTZrMVZlcmlmaWNhdGlvbktleTIwMTkifV0sInNlcnZpY2VzIjpbeyJpZCI6ImxpbmtlZGRvbWFpbnMiLCJzZXJ2aWNlRW5kcG9pbnQiOnsib3JpZ2lucyI6WyJodHRwczovL2RpZC53b29kZ3JvdmVkZW1vLmNvbS8iXX0sInR5cGUiOiJMaW5rZWREb21haW5zIn0seyJpZCI6Imh1YiIsInNlcnZpY2VFbmRwb2ludCI6eyJpbnN0YW5jZXMiOlsiaHR0cHM6Ly9iZXRhLmh1Yi5tc2lkZW50aXR5LmNvbS92MS4wLzNjMzJlZDQwLThhMTAtNDY1Yi04YmE0LTBiMWU4Njg4MjY2OCJdfSwidHlwZSI6IklkZW50aXR5SHViIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlCUlNqWlFUYjRzOXJzZnp0T2F3OWVpeDg3N1l5d2JYc2lnaFlMb2xTSV9KZyJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQXZDTkJoODlYZTVkdUk1dE1wU2ZyZ0k2aVNMMmV2QS0tTmJfUElmdFhfOGciLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUN2RFdOTFhzcE1sbGJfbTFJal96ZV9SaWNKOWdFLUM1b2dlN1NnZTc5cy1BIn19', + }, + }, + ], + }, + }, + ], +} + + +export const entraVerifiedIdPresentation: IPresentationDefinition = { + + 'id': '081ea6b1-9009-4ec0-b41a-0afcf668bd50', + 'input_descriptors': [ + { + 'id': 'TrueIdentity', + 'name': 'TrueIdentity', + 'purpose': 'To verify your demo identity', + 'schema': [ + { + 'uri': 'TrueIdentity', + }, + ], + 'constraints': { + 'fields': [ + { + 'path': [ + '$.issuer', + '$.vc.issuer', + '$.iss', + ], + 'filter': { + 'type': 'string', + 'pattern': 'did.*', + }, + }, + ], + }, + }, + ], + +} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/restAgent.test.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/restAgent.test.ts deleted file mode 100644 index 24f9678d3..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/restAgent.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as fs from 'fs' -import 'cross-fetch/polyfill' -// @ts-ignore -import express from 'express' -import { IAgent, createAgent, IAgentOptions, IDataStore } from '@veramo/core' -import { AgentRestClient } from '@veramo/remote-client' -import { Server } from 'http' -import { AgentRouter, RequestWithAgentRouter } from '@veramo/remote-server' -import { getConfig } from '@veramo/cli/build/setup' -import { createObjects } from '@veramo/cli/build/lib/objectCreator' -import { Siopv2RelyingParty, ISiopv2RelyingParty } from '../src' -import { Resolver } from 'did-resolver' -import { getDidKeyResolver } from '@veramo/did-provider-key' -import { DIDResolverPlugin } from '@veramo/did-resolver' -import { getUniResolver } from '@sphereon/did-uni-client' -import didAuthSiopOpAuthenticatorAgentLogic from './shared/didAuthSiopOpAuthenticatorAgentLogic' -import { PresentationSignCallback } from '@sphereon/did-auth-siop' - -jest.setTimeout(30000) - -function getFile(path: string) { - return fs.readFileSync(path, 'utf-8') -} - -function getFileAsJson(path: string) { - return JSON.parse(getFile(path)) -} - -const port = 3002 -const basePath = '/agent' -let serverAgent: IAgent -let restServer: Server - -const presentationSignCallback: PresentationSignCallback = async (args) => { - const presentationSignProof = getFileAsJson('./packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/psc/psc.json') - - return { - ...args.presentation, - ...presentationSignProof, - } -} - -const getAgent = (options?: IAgentOptions) => - createAgent({ - ...options, - plugins: [ - new Siopv2RelyingParty(presentationSignCallback), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...getDidKeyResolver(), - ...getUniResolver('lto', { resolveUrl: 'https://uniresolver.test.sphereon.io/1.0/identifiers' }), - ...getUniResolver('factom', { resolveUrl: 'https://uniresolver.test.sphereon.io/1.0/identifiers' }), - }), - }), - new AgentRestClient({ - url: 'http://localhost:' + port + basePath, - enabledMethods: serverAgent.availableMethods(), - schema: serverAgent.getSchema(), - }), - ], - }) - -const setup = async (): Promise => { - const config = getConfig('packages/siopv2-openid4vp-op-auth/agent.yml') - config.agent.$args[0].plugins[1].$args[0] = presentationSignCallback - const { agent } = createObjects(config, { agent: '/agent' }) - agent.registerCustomApprovalForSiop({ key: 'success', customApproval: () => Promise.resolve() }) - agent.registerCustomApprovalForSiop({ key: 'failure', customApproval: () => Promise.reject(new Error('denied')) }) - serverAgent = agent - - const agentRouter = AgentRouter({ - exposedMethods: serverAgent.availableMethods(), - }) - - const requestWithAgent = RequestWithAgentRouter({ - agent: serverAgent, - }) - - return new Promise((resolve) => { - const app = express() - app.use(basePath, requestWithAgent, agentRouter) - restServer = app.listen(port, () => { - resolve(true) - }) - }) -} - -const tearDown = async (): Promise => { - restServer.close() - return true -} - -const testContext = { - getAgent, - setup, - tearDown, - isRestTest: true, -} - -xdescribe('REST integration tests', () => { - didAuthSiopOpAuthenticatorAgentLogic(testContext) -}) diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts b/packages/siopv2-oid4vp-rp-rest/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts deleted file mode 100644 index 809bfbc8e..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts +++ /dev/null @@ -1,470 +0,0 @@ -import * as fs from 'fs' -import { IDataStore, TAgent, VerifiableCredential } from '@veramo/core' -import { IAuthRequestDetails, ISiopv2RelyingParty, IPresentationWithDefinition } from '../../src' -import { - AuthorizationRequest, - OP, - ParsedAuthorizationRequestURI, - PresentationDefinitionWithLocation, - ResponseContext, - ResponseMode, - ResponseType, - SubjectIdentifierType, - UrlEncodingFormat, - VerificationMode, - VerifiedAuthorizationRequest, -} from '@sphereon/did-auth-siop' -import { mapIdentifierKeysToDoc } from '@veramo/utils' -import { CredentialMapper } from '@sphereon/ssi-types' -import { mapIdentifierKeysToDocWithJwkSupport } from '@sphereon/ssi-sdk-did-utils' - -function getFile(path: string) { - return fs.readFileSync(path, 'utf-8') -} - -function getFileAsJson(path: string) { - return JSON.parse(getFile(path)) -} - -const nock = require('nock') -jest.mock('@veramo/utils', () => ({ - ...jest.requireActual('@veramo/utils'), - mapIdentifierKeysToDoc: jest.fn(), -})) - -jest.mock('@sphereon/ssi-sdk-did-utils', () => ({ - ...jest.requireActual('@sphereon/ssi-sdk-did-utils'), - mapIdentifierKeysToDocWithJwkSupport: jest.fn(), -})) - -type ConfiguredAgent = TAgent - -const didMethod = 'ethr' -const did = 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a' -const identifier = { - did, - provider: '', - controllerKeyId: `${did}#controller`, - keys: [ - { - kid: `${did}#controller`, - kms: '', - type: 'Secp256k1' as const, - publicKeyHex: '1e21e21e...', - privateKeyHex: 'eqfcvnqwdnwqn...', - }, - ], - services: [], -} -const authKeys = [ - { - kid: `${did}#controller`, - kms: '', - type: 'Secp256k1', - publicKeyHex: '1e21e21e...', - privateKeyHex: 'eqfcvnqwdnwqn...', - meta: { - verificationMethod: { - id: `${did}#controller`, - type: 'EcdsaSecp256k1RecoveryMethod2020', - controller: did, - blockchainAccountId: '0xB9C5714089478a327F09197987f16f9E5d936E8a@eip155:1', - publicKeyHex: '1e21e21e...', - }, - }, - }, -] - -console.log(identifier) -const sessionId = 'sessionId' -const otherSessionId = 'other_sessionId' -const redirectUrl = 'http://example/ext/get-auth-request-url' -const stateId = '2hAyTM7PB3SGJaeGU7QeTJ' -const nonce = 'o5qwML7DnrcLMs9Vdizyz9' -const scope = 'openid' -const openIDURI = - 'openid://?response_type=id_token' + - '&scope=openid' + - '&client_id=' + - did + - '&redirect_uri=' + - redirectUrl + - '&iss=' + - did + - '&response_mode=post' + - '&response_context=rp' + - '&nonce=' + - nonce + - '&state=' + - stateId + - '®istration=registration_value' + - '&request=ey...' -const registration = { - did_methods_supported: [`did:${didMethod}:`], - subject_identifiers_supported: SubjectIdentifierType.DID, - credential_formats_supported: [], -} -const authorizationRequest: ParsedAuthorizationRequestURI = { - encodedUri: 'uri_example', - encodingFormat: UrlEncodingFormat.FORM_URL_ENCODED, - scheme: 'scheme2022122200', - requestObjectJwt: 'ey...', - authorizationRequestPayload: { - response_type: ResponseType.ID_TOKEN, - scope, - client_id: did, - redirect_uri: redirectUrl, - iss: did, - response_mode: ResponseMode.POST, - response_context: ResponseContext.RP, - nonce, - stateId, - registration, - request: 'ey...', - }, - registration, -} -const authorizationVerificationMockedResult = { - payload: {}, - verifyOpts: {}, -} - -const createAuthorizationResponseMockedResult = { - didResolutionResult: { - didResolutionMetadata: {}, - didDocument: { - id: did, - }, - didDocumentMetadata: {}, - }, - issuer: did, - signer: { - id: did, - type: 'authentication', - controller: did, - }, - jwt: 'ey...', - authorizationRequestPayload: { - scope, - response_type: ResponseType.ID_TOKEN, - client_id: did, - redirect_uri: redirectUrl, - response_mode: ResponseMode.POST, - response_context: ResponseContext.RP, - nonce: nonce, - }, - verifyOpts: { - verification: { - mode: VerificationMode.INTERNAL, - resolveOpts: {}, - }, - }, -} - -export default (testContext: { - getAgent: () => ConfiguredAgent - setup: () => Promise - tearDown: () => Promise - isRestTest: boolean -}) => { - describe('DID Auth SIOP OP Authenticator Agent Plugin', () => { - let agent: ConfiguredAgent - - beforeAll(async () => { - await testContext.setup() - agent = testContext.getAgent() - - const idCardCredential: VerifiableCredential = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vc/vc_idCardCredential.json' - ) - await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: idCardCredential }) - - const driverLicenseCredential: VerifiableCredential = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vc/vc_driverLicense.json' - ) - await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: driverLicenseCredential }) - - nock(redirectUrl).get(`?stateId=${stateId}`).times(5).reply(200, openIDURI) - - const mockedMapIdentifierKeysToDocMethod = mapIdentifierKeysToDoc as jest.Mock - mockedMapIdentifierKeysToDocMethod.mockReturnValue(Promise.resolve(authKeys)) - - const mockedMapIdentifierKeysToDocMethodWithJwkSupport = mapIdentifierKeysToDocWithJwkSupport as jest.Mock - mockedMapIdentifierKeysToDocMethodWithJwkSupport.mockReturnValue(Promise.resolve(authKeys)) - - const mockedparseAuthorizationRequestURIMethod = jest.fn() - OP.prototype.parseAuthorizationRequestURI = mockedparseAuthorizationRequestURIMethod - mockedparseAuthorizationRequestURIMethod.mockReturnValue(Promise.resolve(authorizationRequest)) - - const mockedverifyAuthorizationRequestMethod = jest.fn() - OP.prototype.verifyAuthorizationRequest = mockedverifyAuthorizationRequestMethod - mockedverifyAuthorizationRequestMethod.mockReturnValue(Promise.resolve(authorizationVerificationMockedResult)) - - const mockedcreateAuthorizationResponse = jest.fn() - OP.prototype.createAuthorizationResponse = mockedcreateAuthorizationResponse - mockedcreateAuthorizationResponse.mockReturnValue(Promise.resolve(createAuthorizationResponseMockedResult)) - - const mocksubmitAuthorizationResponseMethod = jest.fn() - OP.prototype.submitAuthorizationResponse = mocksubmitAuthorizationResponseMethod - mocksubmitAuthorizationResponseMethod.mockReturnValue(Promise.resolve({ status: 200, statusText: 'example_value' })) - - await agent.siopRegisterOPSession({ - sessionId, - requestJwtOrUri: openIDURI, - }) - }) - - afterAll(testContext.tearDown) - - it('should register OP session', async () => { - const sessionId = 'new_session_id' - const result = await agent.siopRegisterOPSession({ - sessionId, - requestJwtOrUri: openIDURI, - }) - - expect(result.id).toEqual(sessionId) - }) - - it('should remove OP session', async () => { - await agent.siopRegisterOPSession({ - sessionId: otherSessionId, - requestJwtOrUri: openIDURI, - }) - await agent.siopRemoveOPSession({ - sessionId: otherSessionId, - }) - - await expect( - agent.siopGetOPSession({ - sessionId: otherSessionId, - }) - ).rejects.toThrow(`No session found for id: ${otherSessionId}`) - }) - - if (!testContext.isRestTest) { - it('should register custom approval function', async () => { - await expect( - agent.siopRegisterOPCustomApproval({ - key: 'test_register', - customApproval: (verifiedAuthenticationRequest: VerifiedAuthorizationRequest) => Promise.resolve(), - }) - ).resolves.not.toThrow() - }) - - it('should remove custom approval function', async () => { - await agent.siopRegisterOPCustomApproval({ - key: 'test_delete', - customApproval: (verifiedAuthenticationRequest: VerifiedAuthorizationRequest) => Promise.resolve(), - }) - const result = await agent.siopRemoveOPCustomApproval({ - key: 'test_delete', - }) - - expect(result).toEqual(true) - }) - } - - it('should authenticate with DID SIOP without custom approval', async () => { - const result = await agent.authenticateWithSiop({ - sessionId, - stateId, - redirectUrl, - }) - - expect(result.status).toEqual(200) - }) - - it('should authenticate with DID SIOP with custom approval', async () => { - const result = await agent.authenticateWithSiop({ - sessionId, - stateId, - redirectUrl, - customApproval: testContext.isRestTest - ? 'success' - : (verifiedAuthenticationRequest: VerifiedAuthorizationRequest) => { - return Promise.resolve() - }, - }) - - expect(result.status).toEqual(200) - }) - - it('should not authenticate with DID SIOP with unknown custom approval key', async () => { - const customApprovalKey = 'some_random_key' - await expect( - agent.authenticateWithSiop({ - sessionId, - stateId, - redirectUrl, - customApproval: customApprovalKey, - }) - ).rejects.toThrow(`Custom approval not found for key: ${customApprovalKey}`) - }) - - it('should not authenticate with DID SIOP when custom approval fails', async () => { - const denied = 'denied' - await expect( - agent.authenticateWithSiop({ - sessionId, - stateId, - redirectUrl, - customApproval: testContext.isRestTest - ? 'failure' - : (verifiedAuthenticationRequest: VerifiedAuthorizationRequest) => { - return Promise.reject(new Error(denied)) - }, - }) - ).rejects.toThrow(denied) - }) - - it('should get authenticate request from RP', async () => { - const result = await agent.getSiopAuthorizationRequestFromRP({ - sessionId, - stateId, - redirectUrl, - }) - - expect(result).toEqual(authorizationRequest) - }) - - it('should get authentication details with single credential', async () => { - const pd_single: PresentationDefinitionWithLocation = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' - ) - const vp_single: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) - const presentation = CredentialMapper.toWrappedVerifiablePresentation(vp_single.presentation) - presentation.presentation.presentation_submission!.id = expect.any(String) - - const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ - sessionId, - verifiedAuthorizationRequest: { - ...createAuthorizationResponseMockedResult, - presentationDefinitions: [pd_single], - authorizationRequest: {} as AuthorizationRequest, - versions: [], - payload: {}, - }, - signingOptions: { - nonce: 'nonce202212272050', - domain: 'domain202212272051', - }, - }) - - expect(result).toMatchObject({ - id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - verifiablePresentationMatches: [vp_single], - }) - }) - - it('should get authentication details with getting specific credentials', async () => { - const pdSingle: PresentationDefinitionWithLocation = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_single.json' - ) - const vpSingle: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_single.json' - ) - const presentation = CredentialMapper.toWrappedVerifiablePresentation(vpSingle.presentation) - presentation.presentation.presentation_submission!.id = expect.any(String) - - const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ - sessionId, - verifiedAuthorizationRequest: { - ...createAuthorizationResponseMockedResult, - presentationDefinitions: [pdSingle], - authorizationRequest: {} as AuthorizationRequest, - versions: [], - payload: {}, - }, - credentialFilter: { - where: [ - { - column: 'id', - value: ['https://example.com/credentials/1872'], - }, - ], - }, - }) - - expect(result).toMatchObject({ - id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - verifiablePresentationMatches: [vpSingle], - }) - }) - - it('should get authentication details with multiple credentials', async () => { - const pdMultiple: PresentationDefinitionWithLocation = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_multiple.json' - ) - const vpMultiple: IPresentationWithDefinition = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/vp/vp_multiple.json' - ) - const presentation = CredentialMapper.toWrappedVerifiablePresentation(vpMultiple.presentation) - presentation.presentation.presentation_submission!.id = expect.any(String) - - const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ - sessionId, - verifiedAuthorizationRequest: { - ...createAuthorizationResponseMockedResult, - presentationDefinitions: [pdMultiple], - authorizationRequest: {} as AuthorizationRequest, - versions: [], - payload: {}, - }, - signingOptions: { - nonce: 'nonce202212272050', - domain: 'domain202212272051', - }, - }) - - expect(result).toMatchObject({ - id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - verifiablePresentationMatches: [vpMultiple], - }) - }) - - it('should verify authentication request URI with did methods supported provided', async () => { - authorizationRequest.registration.did_methods_supported = [`did:${didMethod}:`] - - const result = await agent.verifySiopAuthorizationRequestURI({ - sessionId, - requestURI: authorizationRequest, - }) - - expect(result).toEqual(authorizationVerificationMockedResult) - }) - - it('should verify authentication request URI without did methods supported provided', async () => { - authorizationRequest.registration.did_methods_supported = [] - - const result = await agent.verifySiopAuthorizationRequestURI({ - sessionId, - requestURI: authorizationRequest, - }) - - expect(result).toEqual(authorizationVerificationMockedResult) - }) - - it('should send authentication response', async () => { - const pdMultiple: PresentationDefinitionWithLocation = getFileAsJson( - './packages/siopv2-openid4vp-op-auth/__tests__/vc_vp_examples/pd/pd_multiple.json' - ) - - const result = await agent.sendSiopAuthorizationResponse({ - sessionId, - verifiedAuthorizationRequest: { - ...createAuthorizationResponseMockedResult, - presentationDefinitions: [pdMultiple], - authorizationRequest: {} as AuthorizationRequest, - versions: [], - authorizationRequestPayload: {}, - payload: {}, - }, - }) - - expect(result.status).toEqual(200) - }) - }) -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_multiple.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_multiple.json deleted file mode 100644 index 8c86517e8..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_multiple.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "definition": { - "id": "Credentials", - "input_descriptors": [ - { - "id": "ID Card Credential and Driver's License", - "schema": [ - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" - }, - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/DriversLicense" - } - ], - "constraints": { - "fields": [ - { - "path": ["$.issuer.id"], - "filter": { - "type": "string", - "pattern": "did:example:[issuer|ebfeb1f712ebc6f1c276e12ec21]" - } - } - ] - } - } - ] - }, - "location": "presentation_definition" -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_single.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_single.json deleted file mode 100644 index 81a68f376..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/pd/pd_single.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "definition": { - "id": "Credentials", - "input_descriptors": [ - { - "id": "ID Card Credential", - "schema": [ - { - "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" - } - ], - "constraints": { - "fields": [ - { - "path": ["$.issuer.id"], - "filter": { - "type": "string", - "pattern": "did:example:issuer" - } - } - ] - } - } - ] - }, - "location": "presentation_definition" -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/psc/psc.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/psc/psc.json deleted file mode 100644 index 9755bd33f..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/psc/psc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_driverLicense.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_driverLicense.json deleted file mode 100644 index daa0f0f2b..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_driverLicense.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": "https://example.com/credentials/1873", - "type": ["VerifiableCredential", "DriversLicense"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], - "issuer": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "John", - "family_name": "Doe", - "birthdate": "1975-01-05" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_idCardCredential.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_idCardCredential.json deleted file mode 100644 index 01abcea0e..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vc/vc_idCardCredential.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": "https://example.com/credentials/1872", - "type": ["VerifiableCredential", "IDCardCredential"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "issuer": { - "id": "did:example:issuer" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "Fredrik", - "family_name": "Stremberg", - "birthdate": "1949-01-22" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_multiple.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_multiple.json deleted file mode 100644 index fdb562154..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_multiple.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "format": "ldp_vp", - "location": "authorization_response", - "presentation": { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], - "presentation_submission": { - "definition_id": "Credentials", - "descriptor_map": [ - { - "format": "ldp_vc", - "id": "ID Card Credential and Driver's License", - "path": "$.verifiableCredential[0]" - }, - { - "format": "ldp_vc", - "id": "ID Card Credential and Driver's License", - "path": "$.verifiableCredential[1]" - } - ], - "id": "8oBenRGlNXd0Sp770bCb3" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - }, - "type": ["VerifiablePresentation", "PresentationSubmission"], - "verifiableCredential": [ - { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "credentialSubject": { - "birthdate": "1949-01-22", - "family_name": "Stremberg", - "given_name": "Fredrik" - }, - "id": "https://example.com/credentials/1872", - "issuanceDate": "2010-01-01T19:23:24Z", - "issuer": { - "id": "did:example:issuer" - }, - "proof": { - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "created": "2018-09-14T21:19:10Z", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", - "proofPurpose": "authentication", - "type": "RsaSignature2018", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" - }, - "type": ["VerifiableCredential", "IDCardCredential"] - }, - { - "id": "https://example.com/credentials/1873", - "type": ["VerifiableCredential", "DriversLicense"], - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], - "issuer": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" - }, - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "given_name": "John", - "family_name": "Doe", - "birthdate": "1975-01-05" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - } - } - ] - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_single.json b/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_single.json deleted file mode 100644 index 3ca91b205..000000000 --- a/packages/siopv2-oid4vp-rp-rest/__tests__/vc_vp_examples/vp/vp_single.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "format": "ldp_vp", - "location": "authorization_response", - "presentation": { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], - "presentation_submission": { - "definition_id": "Credentials", - "descriptor_map": [ - { - "format": "ldp_vc", - "id": "ID Card Credential", - "path": "$.verifiableCredential[0]" - } - ], - "id": "8oBenRGlNXd0Sp770bCb3" - }, - "proof": { - "type": "RsaSignature2018", - "created": "2018-09-14T21:19:10Z", - "proofPurpose": "authentication", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" - }, - "type": ["VerifiablePresentation", "PresentationSubmission"], - "verifiableCredential": [ - { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], - "credentialSubject": { - "birthdate": "1949-01-22", - "family_name": "Stremberg", - "given_name": "Fredrik" - }, - "id": "https://example.com/credentials/1872", - "issuanceDate": "2010-01-01T19:23:24Z", - "issuer": { - "id": "did:example:issuer" - }, - "proof": { - "challenge": "1f44d55f-f161-4938-a659-f8026467f126", - "created": "2018-09-14T21:19:10Z", - "domain": "4jt78h47fh47", - "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", - "proofPurpose": "authentication", - "type": "RsaSignature2018", - "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" - }, - "type": ["VerifiableCredential", "IDCardCredential"] - } - ] - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/package.json b/packages/siopv2-oid4vp-rp-rest/package.json index c75e9c838..a06ca2104 100644 --- a/packages/siopv2-oid4vp-rp-rest/package.json +++ b/packages/siopv2-oid4vp-rp-rest/package.json @@ -1,12 +1,14 @@ { - "name": "@sphereon/ssi-sdk-siopv2-openid4vp-rp-rest", + "name": "@sphereon/ssi-sdk-siopv2-openid4vp-rp-rest-api", "version": "0.9.0", "source": "src/index.ts", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsc --build", - "build:clean": "tsc --build --clean && tsc --build" + "build:clean": "tsc --build --clean && tsc --build", + "start:prod": "node build/index.js", + "start:dev": "ts-node __tests__/RestAPI.ts" }, "dependencies": { "@sphereon/ssi-sdk-siopv2-oid4vp-common": "^0.9.0", @@ -16,21 +18,19 @@ "@veramo/core": "4.2.0", "@veramo/credential-w3c": "4.2.0", "@veramo/kv-store": "file:.yalc/@veramo/kv-store", - "cross-fetch": "^3.1.5", - "dotenv-flow": "^3.2.0", "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", "cors": "^2.8.5", + "cross-fetch": "^3.1.5", + "dotenv-flow": "^3.2.0", "express": "^4.18.2", "short-uuid": "^4.2.2", "uuid": "^8.3.2" }, "devDependencies": { "@sphereon/did-uni-client": "^0.6.0", - "@veramo/cli": "4.2.0", - "@veramo/did-provider-key": "4.2.0", - "@veramo/did-resolver": "4.2.0", - "@veramo/utils": "4.2.0", + "@sphereon/ssi-sdk-data-store-common": "^0.9.0", + "@sphereon/ssi-sdk-jwk-did-provider": "^0.9.0", "@types/body-parser": "^1.19.2", "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.13", @@ -39,8 +39,15 @@ "@types/express": "^4.17.13", "@types/express-http-proxy": "^1.6.3", "@types/node": "^18.15.0", + "@veramo/cli": "4.2.0", + "@veramo/did-provider-ion": "4.2.0", + "@veramo/did-provider-key": "4.2.0", + "@veramo/did-provider-web": "4.2.0", + "@veramo/did-resolver": "4.2.0", + "@veramo/utils": "4.2.0", "did-resolver": "^4.1.0", - "nock": "^13.2.1" + "nock": "^13.2.1", + "ts-node": "^10.9.1" }, "files": [ "dist/**/*", diff --git a/packages/siopv2-oid4vp-rp-rest/src/SIOPv2RPRestAPI.ts b/packages/siopv2-oid4vp-rp-rest/src/SIOPv2RPRestAPI.ts new file mode 100644 index 000000000..978ecf2eb --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/src/SIOPv2RPRestAPI.ts @@ -0,0 +1,269 @@ +// noinspection JSUnusedGlobalSymbols + +import * as dotenv from 'dotenv-flow' +import express, { Express, Response } from 'express' +import cookieParser from 'cookie-parser' +import uuid from 'short-uuid' +// import * as core from "express-serve-static-core"; +import { + AuthorizationRequestState, + AuthorizationResponseState, + AuthorizationResponseStateStatus, + PresentationDefinitionLocation, + VerifiedAuthorizationResponse, +} from '@sphereon/did-auth-siop' +import bodyParser from 'body-parser' +import { + AuthorizationRequestStateStatus, + AuthStatusResponse, + GenerateAuthRequestURIResponse, + uriWithBase, +} from '@sphereon/ssi-sdk-siopv2-oid4vp-common' +import { ISIOPv2RP } from '@sphereon/ssi-sdk-siopv2-oid4vp-rp-auth' +import { RequestWithAgent } from './request-agent-router' +import { TAgent } from '@veramo/core' +import { IPresentationExchange } from '@sphereon/ssi-sdk-presentation-exchange' + +export class SIOPv2RPRestAPI { + public express: Express + private agent: TAgent + + + constructor(agent: TAgent) { + this.agent = agent + dotenv.config() + + this.express = express() + const port = process.env.PORT || 5000 + const secret = process.env.COOKIE_SIGNING_KEY + + this.express.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*') + // Request methods you wish to allow + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE') + + // Request headers you wish to allow + res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type') + + // Set to true if you need the website to include cookies in the requests sent + // to the API (e.g. in case you use sessions) + res.setHeader('Access-Control-Allow-Credentials', 'true') + next() + }) + // this.express.use(cors({ credentials: true })); + // this.express.use('/proxy', proxy('www.gssoogle.com')); + this.express.use(bodyParser.urlencoded({ extended: true })) + this.express.use(bodyParser.json()) + this.express.use(cookieParser(secret)) + this.express.listen(port as number, '0.0.0.0', () => console.log(`Listening on port ${port}`)) + + // Webapp endpoints + this.createAuthRequestWebappEndpoint() + this.authStatusWebappEndpoint() + this.deleteAuthRequestStateWebappEndpoint() + + // SIOPv2 endpoints + this.getAuthRequestSIOPv2Endpoint() + this.verifyAuthResponseSIOPv2Endpoint() + } + + private static sendErrorResponse(response: Response, statusCode: number, message: string) { + response.statusCode = statusCode + response.status(statusCode).send(message) + } + + private deleteAuthRequestStateWebappEndpoint() { + this.express.delete('/webapp/definitions/:definitionId/auth-requests/:correlationId', async (request, response) => { + const correlationId: string = request.params.correlationId + const definitionId: string = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') + } + response.statusCode = 200 + return response.send(this.agent.siopDeleteAuthState({ definitionId, correlationId })) + }) + } + + private authStatusWebappEndpoint() { + this.express.post('/webapp/auth-status', async (request: RequestWithAgent, response) => { + console.log('Received auth-status request...') + const correlationId: string = request.body.correlationId as string + const definitionId: string = request.body.definitionId as string + const requestState = correlationId && definitionId ? await this.agent.siopGetAuthRequestState({ + correlationId, + definitionId, + errorOnNotFound: false, + }) : undefined + if (!requestState || !definitionId || !correlationId) { + console.log( + `No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}`, + ) + response.statusCode = 404 + + const statusBody: AuthStatusResponse = { + status: requestState ? requestState.status : AuthorizationRequestStateStatus.ERROR, + error: 'No authentication request mapping could be found for the given URL.', + correlationId, + definitionId, + lastUpdated: requestState ? requestState.lastUpdated : Date.now(), + } + return response.send(statusBody) + } + + let responseState + if (requestState.status === 'sent') { + responseState = await this.agent.siopGetAuthResponseState({ + correlationId, + definitionId, + errorOnNotFound: false, + }) + } + const overallState: AuthorizationRequestState | AuthorizationResponseState = responseState ?? requestState + + const statusBody: AuthStatusResponse = { + status: overallState.status, + ...(overallState.error ? { error: overallState.error?.message } : {}), + correlationId, + definitionId, + lastUpdated: overallState.lastUpdated, + ...(responseState && responseState.status === 'verified' ? { payload: await responseState.response.mergedPayloads() } : {}), + } + console.log(`Will send auth status: ${JSON.stringify(statusBody)}`) + if (overallState.status === AuthorizationRequestStateStatus.ERROR || overallState.status === AuthorizationResponseStateStatus.ERROR) { + response.statusCode = 500 + return response.send(statusBody) + } + response.statusCode = 200 + return response.send(statusBody) + }) + } + + private createAuthRequestWebappEndpoint() { + this.express.post('/webapp/definitions/:definitionId/auth-requests', (request: RequestWithAgent, response) => { + // if (!request.agent) throw Error('No agent configured') + const definitionId = request.params.definitionId + const state: string = uuid.uuid() + const correlationId = state + const requestByReferenceURI = uriWithBase(`/siop/definitions/${definitionId}/auth-requests/${correlationId}`) + const redirectURI = uriWithBase(`/siop/definitions/${definitionId}/auth-responses/${correlationId}`) + + + this.agent.siopCreateAuthRequestURI({ + definitionId, + correlationId, + state, + requestByReferenceURI, + redirectURI, + }).then( + authRequestURI => { + const authRequestBody: GenerateAuthRequestURIResponse = { + correlationId, + definitionId, + authRequestURI, + authStatusURI: `${uriWithBase('/webapp/auth-status')}`, + } + console.log(`Auth Request URI data: ${authRequestBody}`) + return response.send(authRequestBody) + }, + ).catch((e: Error) => { + console.error(e, e.stack) + return SIOPv2RPRestAPI.sendErrorResponse(response, 500, 'Could not create an authorization request URI: ' + e.message) + }) + }) + } + + + + private verifyAuthResponseSIOPv2Endpoint() { + this.express.post('/siop/definitions/:definitionId/auth-responses/:correlationId', async (request, response) => { + const correlationId = request.params.correlationId + const definitionId = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') + } + console.log('Authorization Response (siop-sessions') + console.log(JSON.stringify(request.body, null, 2)) + const responseURI = request.body + const definition = await this.agent.pexDefinitionGet(definitionId) + if (!definition) { + response.statusCode = 404 + response.statusMessage = `No definition ${definitionId}` + return response.send() + } + this.agent.siopVerifyAuthResponse({ + authorizationResponse: responseURI, + correlationId, + definitionId, + presentationDefinitions: [ + { + location: PresentationDefinitionLocation.CLAIMS_VP_TOKEN, + definition, + }, + ], + }) + .then((verifiedResponse: VerifiedAuthorizationResponse) => { + console.log('verifiedResponse: ', JSON.stringify(verifiedResponse, null, 2)) + + const wrappedPresentation = verifiedResponse?.oid4vpSubmission?.presentations[0] + if (wrappedPresentation) { + const credentialSubject = wrappedPresentation.presentation.verifiableCredential[0].credential.credentialSubject + console.log('AND WE ARE DONE!') + console.log(JSON.stringify(credentialSubject, null, 2)) + console.log(JSON.stringify(wrappedPresentation.presentation, null, 2)) + response.statusCode = 200 + // todo: delete session + } else { + response.statusCode = 500 + response.statusMessage = 'Missing Credentials' + } + return response.send() + }) + .catch((reason) => { + console.error('verifyAuthenticationResponseJwt failed:', reason) + }) + response.statusCode = 500 + response.statusMessage = 'Missing Credentials' + return response.send() + }) + } + + private getAuthRequestSIOPv2Endpoint() { + this.express.get('/siop/definitions/:definitionId/auth-requests/:correlationId', async (request, response) => { + const correlationId = request.params.correlationId + const definitionId = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') + } + const requestState = await this.agent.siopGetAuthRequestState({ + correlationId, + definitionId, + errorOnNotFound: false, + }) + if (!requestState) { + console.log(`No authorization request could be found for the given url in the state manager. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, `No authorization request could be found`) + } + const requestObject = await requestState.request?.requestObject?.toJwt() + console.log('JWT Request object:') + console.log(requestObject) + + let error: string | undefined + try { + response.statusCode = 200 + return response.send(requestObject) + } catch (e) { + error = typeof e === 'string' ? e : e instanceof Error ? e.message : undefined + } finally { + this.agent.siopUpdateAuthRequestState({ + correlationId, + definitionId, + state: AuthorizationRequestStateStatus.SENT, + error, + }) + } + }) + } +} diff --git a/packages/siopv2-oid4vp-rp-rest/src/agent/Siopv2RelyingPartyREST.ts b/packages/siopv2-oid4vp-rp-rest/src/agent/Siopv2RelyingPartyREST.ts deleted file mode 100644 index 1fe9903ea..000000000 --- a/packages/siopv2-oid4vp-rp-rest/src/agent/Siopv2RelyingPartyREST.ts +++ /dev/null @@ -1,244 +0,0 @@ -// noinspection JSUnusedGlobalSymbols - -import * as dotenv from 'dotenv-flow' -import express, { Express, Response } from 'express' -import cookieParser from 'cookie-parser' -import uuid from 'short-uuid' -// import * as core from "express-serve-static-core"; -import { - AuthorizationRequestState, - AuthorizationRequestStateStatus, - AuthorizationResponseState, - AuthorizationResponseStateStatus, - PresentationDefinitionLocation, - RP, - VerifiedAuthorizationResponse, -} from '@sphereon/did-auth-siop' -import bodyParser from 'body-parser' -import { - AuthStatusResponse, - GenerateAuthRequestURIResponse, - uriWithBase, -} from '@sphereon/ssi-sdk-siopv2-oid4vp-common' -import { IRequiredContext } from '../types' - - -export class RestAPI { - public express: Express - private context: IRequiredContext - - - constructor(context: IRequiredContext) { - this.context = context - dotenv.config() - - this.express = express() - const port = process.env.PORT || 5000 - const secret = process.env.COOKIE_SIGNING_KEY - - this.express.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*') - // Request methods you wish to allow - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE') - - // Request headers you wish to allow - res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type') - - // Set to true if you need the website to include cookies in the requests sent - // to the API (e.g. in case you use sessions) - res.setHeader('Access-Control-Allow-Credentials', 'true') - next() - }) - // this.express.use(cors({ credentials: true })); - // this.express.use('/proxy', proxy('www.gssoogle.com')); - this.express.use(bodyParser.urlencoded({ extended: true })) - this.express.use(bodyParser.json()) - this.express.use(cookieParser(secret)) - this.express.listen(port as number, '0.0.0.0', () => console.log(`Listening on port ${port}`)) - this.registerWebAppEndpoints() - this.registerOpenID4VPEndpoints() - } - - private static sendErrorResponse(response: Response, statusCode: number, message: string) { - response.statusCode = statusCode - response.status(statusCode).send(message) - } - - - private agent() { - return this.context.agent - } - - private async rp(definitionId?: string): Promise { - if (definitionId && !this.agent().pexDefinitionExists(definitionId)) { - throw Error(`Definition ${definitionId} not found`) - } - return this.agent().siopRPInstance({ definitionId }).then(ins => { - return ins.get() - }) - } - - private registerWebAppEndpoints() { - this.express.get('/webapp/definitions/:definitionId/auth-request-uri', (request, response) => { - const definitionId = request.params.definitionId - const state: string = uuid.uuid() - const nonce: string = uuid.uuid() - const correlationId = state - const requestByReferenceURI = uriWithBase(`/ext/definitions/${definitionId}/auth-requests/${correlationId}`) - const redirectURI = uriWithBase(`/ext/definitions/${definitionId}/auth-responses/${correlationId}`) - - this.rp(definitionId).then(rp => rp.createAuthorizationRequestURI({ - correlationId, - nonce, - state, - requestByReferenceURI, - redirectURI, - // definitionId? - }).then(authRequest => { - const authRequestBody: GenerateAuthRequestURIResponse = { - correlationId, - definitionId, - authRequestURI: authRequest.encodedUri, - authStatusURI: `${uriWithBase('/webapp/auth-status')}`, - } - console.log(`Auth Request URI data: ${authRequestBody}`) - return response.send(authRequestBody) - }), - ).catch((e: Error) => { - console.error(e, e.stack) - return RestAPI.sendErrorResponse(response, 500, 'Could not create an authorization request URI: ' + e.message) - }) - }) - - this.express.post('/webapp/auth-status', async (request, response) => { - console.log('Received auth-status request...') - const correlationId: string = request.body.correlationId as string - const definitionId: string = request.body.definitionId as string - const rp = await this.rp(definitionId) - const requestState = await rp.sessionManager.getRequestStateByCorrelationId(correlationId, false) - if (!requestState || !definitionId) { - console.log(`No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}`) - response.statusCode = 404 - - const statusBody: AuthStatusResponse = { - status: requestState ? requestState.status : AuthorizationRequestStateStatus.ERROR, - error: 'No authentication request mapping could be found for the given URL.', - correlationId, - definitionId, - lastUpdated: requestState ? requestState.lastUpdated : Date.now(), - } - return response.send(statusBody) - } - - let responseState - if (requestState.status === 'sent') { - responseState = await rp.sessionManager.getResponseStateByCorrelationId(correlationId, false) - } - const overallState: AuthorizationRequestState | AuthorizationResponseState = responseState ?? requestState - - const statusBody: AuthStatusResponse = { - status: overallState.status, - ...(overallState.error ? { error: overallState.error?.message } : {}), - correlationId, - definitionId, - lastUpdated: overallState.lastUpdated, - ...(responseState && responseState.status === 'verified' ? { payload: await responseState.response.mergedPayloads() } : {}), - } - console.log(`Will send auth status: ${JSON.stringify(statusBody)}`) - if (overallState.status === AuthorizationRequestStateStatus.ERROR || overallState.status === AuthorizationResponseStateStatus.ERROR) { - response.statusCode = 500 - return response.send(statusBody) - } - response.statusCode = 200 - return response.send(statusBody) - }) - - this.express.delete('/webapp/definitions/:definitionId/auth-requests/:correlationId', async (request, response) => { - const correlationId: string = request.params.correlationId - const definitionId: string = request.params.definitionId - const rp = await this.rp(definitionId) - rp.sessionManager.deleteStateForCorrelationId(correlationId) - }, - ) - } - - private registerOpenID4VPEndpoints() { - this.express.get('/ext/definitions/:definitionId/auth-requests/:correlationId', async (request, response) => { - const correlationId = request.params.correlationId - const definitionId = request.params.definitionId - if (!correlationId || !definitionId) { - console.log('No authorization request could be found for the given url.') - return RestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') - } - const rp = await this.rp(definitionId) - const requestState = await rp.sessionManager.getRequestStateByCorrelationId(correlationId, false) - if (!requestState) { - console.log('No authentication request could be found in registry. CorrelationID: ' + correlationId) - return RestAPI.sendErrorResponse(response, 404, `No authorization request could be found for ${correlationId}`) - } - const requestObject = await requestState.request?.requestObject?.toJwt() - console.log('JWT Request object:') - console.log(requestObject) - - let error = undefined - try { - response.statusCode = 200 - return response.send(requestObject) - } catch (e) { - error = e - } finally { - if (rp) { - rp.signalAuthRequestRetrieved({ correlationId, error: error ? error as Error : undefined }) - } - } - }) - - this.express.post('/ext/definitions/:definitionId/auth-responses/:correlationId', async (request, response) => { - // const correlationId = request.params.correlationId - const definitionId = request.params.definitionId - console.log('Authorization Response (siop-sessions') - console.log(JSON.stringify(request.body, null, 2)) - const jwt = request.body - const definition = await this.agent().pexDefinitionGet(definitionId) - if (!definition) { - response.statusCode = 404 - response.statusMessage = `No definition ${definitionId}` - return response.send() - } - - const rp = await this.rp(definitionId) - - rp.verifyAuthorizationResponse(jwt, { - presentationDefinitions: [{ - location: PresentationDefinitionLocation.CLAIMS_VP_TOKEN, - definition, - }], - }) - .then((verifiedResponse: VerifiedAuthorizationResponse) => { - console.log('verifiedResponse: ', JSON.stringify(verifiedResponse, null, 2)) - - const wrappedPresentation = verifiedResponse?.oid4vpSubmission?.presentations[0] - if (wrappedPresentation) { - const credentialSubject = wrappedPresentation.presentation.verifiableCredential[0].credential.credentialSubject - console.log('AND WE ARE DONE!') - console.log(JSON.stringify(credentialSubject, null, 2)) - console.log(JSON.stringify(wrappedPresentation.presentation, null, 2)) - response.statusCode = 200 - // todo: delete session - } else { - response.statusCode = 500 - response.statusMessage = 'Missing Credentials' - } - return response.send() - }) - .catch(reason => { - console.error('verifyAuthenticationResponseJwt failed:', reason) - - }) - response.statusCode = 500 - response.statusMessage = 'Missing Credentials' - return response.send() - }, - ) - } -} diff --git a/packages/siopv2-oid4vp-rp-rest/src/index.ts b/packages/siopv2-oid4vp-rp-rest/src/index.ts index a8abc5072..3ff10f06f 100644 --- a/packages/siopv2-oid4vp-rp-rest/src/index.ts +++ b/packages/siopv2-oid4vp-rp-rest/src/index.ts @@ -1,7 +1,5 @@ /** * @public */ -const schema = require('../plugin.schema.json') -export { schema } -export * from './agent/Siopv2RelyingPartyREST' +export * from './SIOPv2RPRestAPI' export * from './types' diff --git a/packages/siopv2-oid4vp-rp-rest/src/request-agent-router.ts b/packages/siopv2-oid4vp-rp-rest/src/request-agent-router.ts new file mode 100644 index 000000000..b4f1e0130 --- /dev/null +++ b/packages/siopv2-oid4vp-rp-rest/src/request-agent-router.ts @@ -0,0 +1,47 @@ +import { IAgent } from '@veramo/core' +import { Request, Router } from 'express' + +export interface RequestWithAgent extends Request { + agent?: IAgent +} + +/** + * @public + */ +export interface RequestWithAgentRouterOptions { + /** + * Optional. Pre-configured agent + */ + agent?: IAgent + + /** + * Optional. Function that returns a Promise that resolves to a configured agent for specific request + */ + getAgentForRequest?: (req: Request) => Promise +} + +/** + * Creates an expressjs router that adds a Veramo agent to the request object. + * + * This is needed by all other routers provided by this package to be able to perform their functions. + * + * @param options - Initialization option + * @returns Expressjs router + * + * @public + */ +export const RequestWithAgentRouter = (options: RequestWithAgentRouterOptions): Router => { + const router = Router() + router.use(async (req: RequestWithAgent, res, next) => { + if (options.agent) { + req.agent = options.agent + } else if (options.getAgentForRequest) { + req.agent = await options.getAgentForRequest(req) + } else { + throw Error('[RequestWithAgentRouter] agent or getAgentForRequest is required') + } + next() + }) + + return router +} diff --git a/packages/siopv2-oid4vp-rp-rest/src/types.ts b/packages/siopv2-oid4vp-rp-rest/src/types.ts index 5808f33af..5e2214efe 100644 --- a/packages/siopv2-oid4vp-rp-rest/src/types.ts +++ b/packages/siopv2-oid4vp-rp-rest/src/types.ts @@ -7,6 +7,9 @@ import { IKeyManager, IResolver, } from '@veramo/core' -import { ISiopv2RelyingParty } from '@sphereon/ssi-sdk-siopv2-openid4vp-rp-auth' +import { ISIOPv2RP } from '@sphereon/ssi-sdk-siopv2-oid4vp-rp-auth' +import { IPresentationExchange } from '@sphereon/ssi-sdk-presentation-exchange' -export type IRequiredContext = IAgentContext +export type IRequiredContext = IAgentContext< + IDataStoreORM & IResolver & IDIDManager & IKeyManager & ICredentialIssuer & ICredentialVerifier & ISIOPv2RP & IPresentationExchange +> diff --git a/packages/siopv2-oid4vp-rp-rest/tsconfig.json b/packages/siopv2-oid4vp-rp-rest/tsconfig.json index da198fdde..ec6a3fd3d 100644 --- a/packages/siopv2-oid4vp-rp-rest/tsconfig.json +++ b/packages/siopv2-oid4vp-rp-rest/tsconfig.json @@ -6,5 +6,11 @@ "declarationDir": "dist", "esModuleInterop": true }, - "references": [{ "path": "../siopv2-oid4vp-common" }, { "path": "../siopv2-oid4vp-rp-auth" }, { "path": "../ssi-types" }, { "path": "../ssi-sdk-core" }, { "path": "../did-utils" }] + "references": [ + { "path": "../siopv2-oid4vp-common" }, + { "path": "../siopv2-oid4vp-rp-auth" }, + { "path": "../ssi-types" }, + { "path": "../ssi-sdk-core" }, + { "path": "../did-utils" } + ] } diff --git a/yarn.lock b/yarn.lock index 45870bcaf..e77b39ef5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -370,6 +370,18 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@decentralized-identity/ion-sdk@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@decentralized-identity/ion-sdk/-/ion-sdk-0.6.0.tgz#f87ea3f06fa3ff3d1170a5f09621896f38bd1cd4" + integrity sha512-dOwpl9YK5A3f2Cmw2NB8AlzzFlB2u3on/nFxsp2KxR8fQMLYeGNYnC5eOdSwInFgZNeQVgnNhKpZU1YiSf6hQA== + dependencies: + "@noble/ed25519" "1.7.1" + "@noble/secp256k1" "1.7.0" + canonicalize "1.0.1" + multiformats "9.9.0" + multihashes "4.0.3" + uri-js "4.4.0" + "@did-core/data-model@^0.1.1-unstable.13": version "0.1.1-unstable.15" resolved "https://registry.yarnpkg.com/@did-core/data-model/-/data-model-0.1.1-unstable.15.tgz#51ef2e99adebdf2d8ba8c43ed537f33916d6b8a8" @@ -1587,11 +1599,21 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/ed25519@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" + integrity sha512-Rk4SkJFaXZiznFyC/t77Q0NKS4FL7TLJJsVG2V2oiEq3kJVeTdxysEe/yRWSpnWMe808XRDJ+VFh5pt/FN5plw== + "@noble/hashes@1.2.0", "@noble/hashes@^1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/secp256k1@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1" + integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -2451,6 +2473,24 @@ fast-text-encoding "^1.0.3" isomorphic-webcrypto "^2.3.8" +"@sphereon/ion-pow@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@sphereon/ion-pow/-/ion-pow-0.2.0.tgz#e275c00a04d699b786f02792364eb77bc65f7d0b" + integrity sha512-SpEG4mV5D+K/jrqGI9QSBPPKO5+Kpu6F3cINBKbWiz+ZI4boWwz9JAdNspD45YnnMqTbR14CDEGtHwOaHboJQg== + dependencies: + "@sphereon/isomorphic-argon2" "^1.0.0" + cross-fetch "^3.1.5" + debug "^4.3.4" + uint8arrays "^3.1.0" + +"@sphereon/isomorphic-argon2@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@sphereon/isomorphic-argon2/-/isomorphic-argon2-1.0.1.tgz#3e2b262bc1378c63ac721fdb6f6cb5b518c1138f" + integrity sha512-Z40mdiuuZjII19FfIsti9JFGqX56jhpaeZb135BZayJPaRSbi8JnbJ3pzulJJAHsymkWzVqMqt242fBKpualHg== + dependencies: + argon2-browser "^1.18.0" + uint8arrays "^3.1.1" + "@sphereon/isomorphic-webcrypto@^2.4.0-unstable.3": version "2.4.0-unstable.3" resolved "https://registry.yarnpkg.com/@sphereon/isomorphic-webcrypto/-/isomorphic-webcrypto-2.4.0-unstable.3.tgz#b6125dd9b4a1f5e9ed02e5a271cf43ef7d43b260" @@ -2469,11 +2509,6 @@ str2buf "^1.3.0" webcrypto-shim "^0.1.7" -"@sphereon/jsencrypt@3.3.2-unstable.0": - version "3.3.2-unstable.0" - resolved "https://registry.yarnpkg.com/@sphereon/jsencrypt/-/jsencrypt-3.3.2-unstable.0.tgz#25538a6071e0db0a4f6f284967da447228c0babe" - integrity sha512-HurQBvkjAW8t4oZ629sAKTAEi7B/rjYfmSJOquGdyJZeMPT+5R/9DW6i2eQv6kWHWLBa1Iji14zMUvM/NmF8Ew== - "@sphereon/lto-did-ts@0.1.8-unstable.0": version "0.1.8-unstable.0" resolved "https://registry.yarnpkg.com/@sphereon/lto-did-ts/-/lto-did-ts-0.1.8-unstable.0.tgz#831fe98c759c2b27dd57807c5def88eb998b0a95" @@ -2519,6 +2554,16 @@ nanoid "^3.3.4" string.prototype.matchall "^4.0.8" +"@sphereon/ssi-sdk-data-store-common@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk-data-store-common/-/ssi-sdk-data-store-common-0.9.0.tgz#226e23131660861d2891583d1a0084363ae21fb3" + integrity sha512-QIxQzxWEyF9k13b8rMTQFChZfbLUwCaw23k4AHVOoH2jqhhIT6z3nP4EG/6OW5+DVC3AVe4QxuLgQgGxwErrHg== + dependencies: + "@veramo/core" "4.2.0" + "@veramo/utils" "4.2.0" + debug "^4.3.4" + typeorm "^0.3.10" + "@sphereon/wellknown-dids-client@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@sphereon/wellknown-dids-client/-/wellknown-dids-client-0.1.3.tgz#4711599ed732903e9f45fe051660f925c9b508a4" @@ -3374,6 +3419,11 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" +"@types/json-buffer@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" + integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== + "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -3949,6 +3999,25 @@ debug "^4.3.3" ethr-did "^2.3.6" +"@veramo/did-provider-ion@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@veramo/did-provider-ion/-/did-provider-ion-4.2.0.tgz#bf917a83a28d2356f0a09a2c6ae32220ab717fc7" + integrity sha512-Fo5L7wd587ohFXEYbRb2a8H7n8RjqcCyc2KABrCkmHi5rdhuOf3/3k5RqJ6xtFq76NBwb9UMtNt9spm7aFrIFg== + dependencies: + "@decentralized-identity/ion-sdk" "^0.6.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@sphereon/ion-pow" "^0.2.0" + "@stablelib/ed25519" "^1.0.2" + "@trust/keyto" "^1.0.1" + "@veramo/core" "^4.2.0" + "@veramo/did-manager" "^4.2.0" + "@veramo/key-manager" "^4.2.0" + "@veramo/kms-local" "^4.2.0" + canonicalize "^1.0.8" + debug "^4.3.3" + uint8arrays "^3.0.0" + "@veramo/did-provider-key@4.2.0", "@veramo/did-provider-key@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@veramo/did-provider-key/-/did-provider-key-4.2.0.tgz#cf1305e4b395248976461bedfb104d85fa25bb4d" @@ -4020,10 +4089,9 @@ uint8arrays "^3.0.0" "@veramo/kv-store@file:packages/presentation-exchange/.yalc/@veramo/kv-store": - version "5.1.2" + version "4.3.0" dependencies: - "@veramo/core-types" "5.1.2" - "@veramo/utils" "5.1.2" + "@veramo/utils" "4.3.0" debug "^4.3.4" events "^3.3.0" json-buffer "^3.0.1" @@ -4042,10 +4110,9 @@ uint8arrays "^3.1.1" "@veramo/kv-store@file:packages/siopv2-oid4vp-rp-rest/.yalc/@veramo/kv-store": - version "5.1.2" + version "4.3.0" dependencies: - "@veramo/core-types" "5.1.2" - "@veramo/utils" "5.1.2" + "@veramo/utils" "4.3.0" debug "^4.3.4" events "^3.3.0" json-buffer "^3.0.1" @@ -4106,7 +4173,7 @@ debug "^4.3.3" url-parse "^1.5.4" -"@veramo/utils@4.2.0", "@veramo/utils@5.1.2", "@veramo/utils@^4.2.0": +"@veramo/utils@4.2.0", "@veramo/utils@4.3.0", "@veramo/utils@5.1.2", "@veramo/utils@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@veramo/utils/-/utils-4.2.0.tgz#43f5b90306aef7a3bb9dcee58eb03a6f4d573d5c" integrity sha512-jHkli0Qz9rFsWzPAdfJP3P2MFxvVMZPDXZvtVBm8x1fjAGrw/Htz/c5drhDAeBXnqPd9011/7cyvp6AOvdbc8Q== @@ -4439,6 +4506,11 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argon2-browser@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/argon2-browser/-/argon2-browser-1.18.0.tgz#f35820211e0a431aed7f82b9348477234be69bec" + integrity sha512-ImVAGIItnFnvET1exhsQB7apRztcoC5TnlSqernMJDUjbc/DLq3UEYeXFrLPrlaIl8cVfwnXb6wX2KpFf2zxHw== + argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -5209,11 +5281,21 @@ caniuse-lite@^1.0.30001449: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001464.tgz#888922718df48ce5e33dcfe1a2af7d42676c5eb7" integrity sha512-oww27MtUmusatpRpCGSOneQk2/l5czXANDSFvsc7VuOQ86s3ANhZetpwXNf1zY/zdfP63Xvjz325DAdAoES13g== +canonicalize@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9" + integrity sha512-N3cmB3QLhS5TJ5smKFf1w42rJXWe6C1qP01z4dxJiI5v269buii4fLHWETDyf7yEd0azGLNC63VxNMiPd2u0Cg== + canonicalize@^1.0.1, canonicalize@^1.0.3, canonicalize@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== +canonicalize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-2.0.0.tgz#32be2cef4446d67fd5348027a384cae28f17226a" + integrity sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== + cardinal@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" @@ -6205,6 +6287,24 @@ did-jwt@^6.11.0, did-jwt@^6.11.2, did-jwt@^6.2.2, did-jwt@^6.3.0, did-jwt@^6.9.0 multiformats "^9.6.5" uint8arrays "^3.0.0" +did-jwt@^6.11.6: + version "6.11.6" + resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" + integrity sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== + dependencies: + "@stablelib/ed25519" "^1.0.2" + "@stablelib/random" "^1.0.1" + "@stablelib/sha256" "^1.0.1" + "@stablelib/x25519" "^1.0.2" + "@stablelib/xchacha20poly1305" "^1.0.1" + bech32 "^2.0.0" + canonicalize "^2.0.0" + did-resolver "^4.0.0" + elliptic "^6.5.4" + js-sha3 "^0.8.0" + multiformats "^9.6.5" + uint8arrays "^3.0.0" + did-resolver@^3.1.5: version "3.2.2" resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.2.tgz#6f4e252a810f785d1b28a10265fad6dffee25158" @@ -10778,7 +10878,7 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multibase@^4.0.6: +multibase@^4.0.1, multibase@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== @@ -10798,15 +10898,24 @@ multiformats@9.7.1: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.1.tgz#ab348e5fd6f8e7fb3fd56033211bda48854e2173" integrity sha512-TaVmGEBt0fhxiNJMGphBfB+oGvUxFs8KgGvgl8d3C+GWtrFcvXdJ2196eg+dYhmSFClmgFfSfJEklo+SZzdNuw== +multiformats@9.9.0, multiformats@^9.4.2, multiformats@^9.6.5: + version "9.9.0" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== + multiformats@^11.0.1: version "11.0.2" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-11.0.2.tgz#b14735efc42cd8581e73895e66bebb9752151b60" integrity sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg== -multiformats@^9.4.2, multiformats@^9.6.5: - version "9.9.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" - integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== +multihashes@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== + dependencies: + multibase "^4.0.1" + uint8arrays "^3.0.0" + varint "^5.0.2" multimatch@5.0.0: version "5.0.0" @@ -14195,7 +14304,7 @@ uid-safe@~2.1.5: dependencies: random-bytes "~1.0.0" -uint8arrays@^3.0.0, uint8arrays@^3.1.1: +uint8arrays@^3.0.0, uint8arrays@^3.1.0, uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== @@ -14302,6 +14411,13 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" +uri-js@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -14420,6 +14536,11 @@ validator@^13.7.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== +varint@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + varint@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0"