UNPKG

@sphereon/ssi-sdk.wellknown-did-issuer

Version:

579 lines (570 loc) • 24.1 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; // plugin.schema.json var require_plugin_schema = __commonJS({ "plugin.schema.json"(exports, module) { module.exports = { IWellKnownDidIssuer: { components: { schemas: { IIssueDidConfigurationResourceArgs: { type: "object", properties: { issuances: { type: "array", items: { type: "object", properties: { additionalProperties: true } } }, credentialIssuance: { type: "string" }, save: { type: "boolean" }, additionalProperties: false }, required: ["issuances", "credentialIssuance"], description: "Arguments needed for {@link IWellKnownDidIssuer.addLinkedDomainsService } " }, IIssueDomainLinkageCredentialArgs: { type: "object", properties: { did: { type: "string" }, origin: { type: "string" }, serviceId: { type: "string" }, issuanceDate: { type: "date" }, expirationDate: { type: "date" }, save: { type: "boolean" }, options: { type: "object", properties: { additionalProperties: true } }, credentialIssuance: { type: "string" }, additionalProperties: false }, required: ["did", "origin", "expirationDate", "options"], description: "Arguments needed for {@link IWellKnownDidIssuer.issueDomainLinkageCredential } " }, IGetDidConfigurationResourceArgs: { type: "object", properties: { origin: { type: "string" }, additionalProperties: false }, required: ["origin"], description: "Arguments needed for {@link IWellKnownDidIssuer.getDidConfigurationResource } " }, IAddLinkedDomainsServiceArgs: { type: "object", properties: { did: { type: "string" }, origin: { type: "string" }, serviceId: { type: "string" }, additionalProperties: false }, required: ["did", "origin"], description: "Arguments needed for {@link IWellKnownDidIssuer.addLinkedDomainsService } " }, ISaveDidConfigurationResourceArgs: { type: "object", properties: { origin: { type: "string" }, didConfigurationResource: { type: "object", properties: { additionalProperties: true } }, additionalProperties: false }, required: ["did", "origin"], description: "Arguments needed for {@link IWellKnownDidIssuer.saveDidConfigurationResource } " }, IRemoveCredentialIssuanceArgs: { type: "object", properties: { origin: { callbackName: "string" }, additionalProperties: false }, required: ["callbackName"], description: "Arguments needed for {@link IWellKnownDidIssuer.removeCredentialIssuance } " } }, methods: { addLinkedDomainsService: { description: "Adds a LinkedDomains service to the DID", arguments: { $ref: "#/components/schemas/IAddLinkedDomainsServiceArgs" } }, getDidConfigurationResource: { description: "Retrieves a DID configuration resource from the database", arguments: { $ref: "#/components/schemas/IGetDidConfigurationResourceArgs" }, returnType: "object" }, issueDidConfigurationResource: { description: "Issues a DID configuration resource", arguments: { $ref: "#/components/schemas/IAddLinkedDomainsServiceArgs" }, returnType: "object" }, issueDomainLinkageCredential: { description: "Retrieves a domain linkage credential", arguments: { $ref: "#/components/schemas/IIssueDomainLinkageCredentialArgs" }, returnType: "object" }, removeCredentialIssuance: { description: "Removes a registered callback", arguments: { $ref: "#/components/schemas/IRemoveCredentialIssuanceArgs" }, returnType: "boolean" }, saveDidConfigurationResource: { description: "Saves a DID configuration resource to the database", arguments: { $ref: "#/components/schemas/ISaveDidConfigurationResourceArgs" } } } } } }; } }); // src/entities/DidConfigurationResourceEntity.ts import { Credential, Identifier, Claim } from "@veramo/data-store"; import { asArray, computeEntryHash, extractIssuer } from "@veramo/utils"; import { Entity, Column, PrimaryColumn, CreateDateColumn, UpdateDateColumn, ManyToMany, JoinTable, BaseEntity } from "typeorm"; function _ts_decorate(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); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } __name(_ts_decorate, "_ts_decorate"); function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } __name(_ts_metadata, "_ts_metadata"); var DidConfigurationResourceEntity = class extends BaseEntity { static { __name(this, "DidConfigurationResourceEntity"); } origin; context; // TODO cascade of delete should be true when the VC is only attached to one DID configuration resource linkedDids; createdAt; lastUpdatedAt; }; _ts_decorate([ PrimaryColumn({ nullable: false }), _ts_metadata("design:type", String) ], DidConfigurationResourceEntity.prototype, "origin", void 0); _ts_decorate([ Column({ nullable: false }), _ts_metadata("design:type", String) ], DidConfigurationResourceEntity.prototype, "context", void 0); _ts_decorate([ ManyToMany(() => Credential, (credential) => credential.hash, { nullable: false, cascade: true, onDelete: "NO ACTION" }), JoinTable({ name: "DidConfigurationResourceCredentials" }), _ts_metadata("design:type", typeof Array === "undefined" ? Object : Array) ], DidConfigurationResourceEntity.prototype, "linkedDids", void 0); _ts_decorate([ CreateDateColumn({ name: "created_at", nullable: false }), _ts_metadata("design:type", typeof Date === "undefined" ? Object : Date) ], DidConfigurationResourceEntity.prototype, "createdAt", void 0); _ts_decorate([ UpdateDateColumn({ name: "last_updated_at", nullable: false }), _ts_metadata("design:type", typeof Date === "undefined" ? Object : Date) ], DidConfigurationResourceEntity.prototype, "lastUpdatedAt", void 0); DidConfigurationResourceEntity = _ts_decorate([ Entity("DidConfigurationResource") ], DidConfigurationResourceEntity); var didConfigurationResourceFrom = /* @__PURE__ */ __name((didConfigurationResource) => { return { "@context": didConfigurationResource.context, linked_dids: linkedDidsFrom(didConfigurationResource.linkedDids) }; }, "didConfigurationResourceFrom"); var linkedDidsFrom = /* @__PURE__ */ __name((credentials) => { return credentials.map((credential) => credential?.raw?.proof?.type === "JwtProof2020" && typeof credential?.raw?.proof?.jwt === "string" ? credential.raw.proof.jwt : credential.raw); }, "linkedDidsFrom"); var createCredentialEntity = /* @__PURE__ */ __name((vci) => { const vc = vci; const credential = new Credential(); credential.context = asArray(vc["@context"]); credential.type = asArray(vc.type || []); credential.id = vc.id; if (vc.issuanceDate) { credential.issuanceDate = new Date(vc.issuanceDate); } if (vc.expirationDate) { credential.expirationDate = new Date(vc.expirationDate); } const issuer = new Identifier(); issuer.did = extractIssuer(vc); credential.issuer = issuer; if (vc.credentialSubject.id) { const subject = new Identifier(); subject.did = vc.credentialSubject.id; credential.subject = subject; } credential.claims = []; for (const type in vc.credentialSubject) { if (vc.credentialSubject.hasOwnProperty(type)) { const value = vc.credentialSubject[type]; if (type !== "id") { const isObj = typeof value === "function" || typeof value === "object" && !!value; const claim = new Claim(); claim.hash = computeEntryHash(JSON.stringify(vc) + type); claim.type = type; claim.value = isObj ? JSON.stringify(value) : value; claim.isObj = isObj; claim.issuer = credential.issuer; claim.subject = credential.subject; claim.expirationDate = credential.expirationDate; claim.issuanceDate = credential.issuanceDate; claim.credentialType = credential.type; claim.context = credential.context; credential.claims.push(claim); } } } credential.raw = vci; return credential; }, "createCredentialEntity"); // src/agent/WellKnownDidIssuer.ts import { CredentialCorrelationType, CredentialRole } from "@sphereon/ssi-sdk.credential-store"; import { CredentialMapper, parseDid } from "@sphereon/ssi-types"; import { ServiceTypesEnum, WellKnownDidIssuer as Issuer } from "@sphereon/wellknown-dids-client"; import { normalizeCredential } from "did-jwt-vc"; import { v4 as uuidv4 } from "uuid"; import { RegulationType } from "@sphereon/ssi-sdk.data-store-types"; var WellKnownDidIssuer = class { static { __name(this, "WellKnownDidIssuer"); } dbConnection; schema = schema.IWellKnownDidVerifier; methods = { addLinkedDomainsService: this.addLinkedDomainsService.bind(this), getDidConfigurationResource: this.getDidConfigurationResource.bind(this), issueDidConfigurationResource: this.issueDidConfigurationResource.bind(this), issueDomainLinkageCredential: this.issueDomainLinkageCredential.bind(this), registerCredentialIssuance: this.registerCredentialIssuance.bind(this), removeCredentialIssuance: this.removeCredentialIssuance.bind(this), saveDidConfigurationResource: this.saveDidConfigurationResource.bind(this) }; credentialIssuances; didConfigurationResourceRelations = [ "linkedDids" ]; constructor(dbConnection, args) { this.dbConnection = dbConnection; this.credentialIssuances = args && args.credentialIssuances || {}; } /** {@inheritDoc IWellKnownDidIssuer.registerSignatureVerification} */ async registerCredentialIssuance(args, context) { if (this.credentialIssuances[args.callbackName] !== void 0) { return Promise.reject(new Error(`Credential issuance with callbackName: ${args.callbackName} already present`)); } this.credentialIssuances[args.callbackName] = args.credentialIssuance; } /** {@inheritDoc IWellKnownDidIssuer.removeSignatureVerification} */ async removeCredentialIssuance(args, context) { return delete this.credentialIssuances[args.callbackName]; } /** {@inheritDoc IWellKnownDidIssuer.issueDidConfigurationResource} */ async issueDidConfigurationResource(args, context) { if (!args.issuances.every((issuance) => issuance.origin === args.issuances[0].origin)) { return Promise.reject(Error("All verifiableCredentials should be issued for the same origin")); } const addServices = args.issuances.map((issuance) => this.addLinkedDomainsService({ did: issuance.did, origin: issuance.origin, serviceId: issuance.serviceId }, context)); return Promise.all(addServices).then(async () => new Issuer().issueDidConfigurationResource({ issuances: await this.mapIssuances(args.issuances), issueCallback: typeof args.credentialIssuance === "string" ? await this.getCredentialIssuance(args.credentialIssuance) : args.credentialIssuance }).then(async (didConfigurationResource) => { if (args.save) { await this.saveDidConfigurationResource({ origin: args.issuances[0].origin, didConfigurationResource }, context); } return didConfigurationResource; }).catch((error) => Promise.reject(Error(`Unable to issue DID configuration resource. Error: ${error.message}`)))); } /** {@inheritDoc IWellKnownDidIssuer.saveDidConfigurationResource} */ async saveDidConfigurationResource(args, context) { const didConfigurationEntity = { origin: args.origin, context: args.didConfigurationResource["@context"], linkedDids: args.didConfigurationResource.linked_dids.map((credential) => createCredentialEntity(this.normalizeCredential(credential))) }; await (await this.dbConnection).getRepository(DidConfigurationResourceEntity).save(didConfigurationEntity, { transaction: true }); } /** {@inheritDoc IWellKnownDidIssuer.getDidConfigurationResource} */ async getDidConfigurationResource(args, context) { const result = await (await this.dbConnection).getRepository(DidConfigurationResourceEntity).findOne({ where: { origin: args.origin }, relations: this.didConfigurationResourceRelations }); if (!result) { return Promise.reject(Error(`No DID configuration resource found for origin: ${args.origin}`)); } return didConfigurationResourceFrom(result); } /** {@inheritDoc IWellKnownDidIssuer.issueDomainLinkageCredential} */ async issueDomainLinkageCredential(args, context) { const did = parseDid(args.did).did; if (new URL(args.origin).origin !== args.origin) { return Promise.reject(Error(`Origin ${args.origin} is not valid`)); } if (new URL(args.origin).protocol !== "https:") { return Promise.reject(Error(`Origin ${args.origin} is not a https URL`)); } if (args.issuanceDate && isNaN(Date.parse(args.issuanceDate))) { return Promise.reject(Error(`IssuanceDate ${args.issuanceDate} is not a valid date`)); } if (isNaN(Date.parse(args.expirationDate))) { return Promise.reject(Error(`ExpirationDate ${args.expirationDate} is not a valid date`)); } const credentialIssuance = typeof args.credentialIssuance === "string" ? await this.getCredentialIssuance(args.credentialIssuance) : args.credentialIssuance; return new Issuer().issueDomainLinkageCredential({ did, origin: args.origin, issuanceDate: args.issuanceDate, expirationDate: args.expirationDate, options: args.options, issueCallback: credentialIssuance }).then(async (credential) => { if (args.save) { await this.saveDomainLinkageCredential(credential, context); } return credential; }).catch((error) => Promise.reject(Error(`Unable to issue domain linkage credential for DID: ${did}. Error: ${error.message}`))); } /** {@inheritDoc IWellKnownDidIssuer.addLinkedDomainsService} */ async addLinkedDomainsService(args, context) { const did = parseDid(args.did).did; if (new URL(args.origin).origin !== args.origin) { return Promise.reject(Error(`Origin ${args.origin} is not valid`)); } if (new URL(args.origin).protocol !== "https:") { return Promise.reject(Error(`Origin ${args.origin} is not a https URL`)); } context.agent.didManagerGet({ did }).catch(() => Promise.reject(Error("DID cannot be found"))).then(async (identifier) => { if (!identifier.services || identifier.services.filter( // TODO we should also check for the origins in the serviceEndpoint objects when we start supporting multiple origins (service) => service.type === ServiceTypesEnum.LINKED_DOMAINS && service.serviceEndpoint === args.origin ).length === 0) { await context.agent.didManagerAddService({ did: identifier.did, service: { id: args.serviceId || uuidv4(), type: ServiceTypesEnum.LINKED_DOMAINS, // TODO We should support a serviceEndpoint object here when we update to Veramo 3.1.6.next-165 or higher, as then we can support multiple origins serviceEndpoint: args.origin } }); } }).catch((error) => Promise.reject(Error(`Unable to add LinkedDomains service to DID: ${args.did}. Error: ${error.message}`))); } async getCredentialIssuance(callbackName) { if (this.credentialIssuances[callbackName] === void 0) { return Promise.reject(new Error(`Credential issuance not found for callbackName: ${callbackName}`)); } return this.credentialIssuances[callbackName]; } async saveDomainLinkageCredential(credential, context) { const vc = this.normalizeCredential(credential); return context.agent.crsAddCredential({ credential: { rawDocument: JSON.stringify(vc), credentialRole: CredentialRole.ISSUER, regulationType: RegulationType.NON_REGULATED, kmsKeyRef: "FIXME", identifierMethod: "did", issuerCorrelationId: CredentialMapper.issuerCorrelationIdFromIssuerType(vc.issuer), issuerCorrelationType: CredentialCorrelationType.DID, subjectCorrelationId: CredentialMapper.issuerCorrelationIdFromIssuerType(vc.issuer), subjectCorrelationType: CredentialCorrelationType.DID } }); } normalizeCredential(credential) { return typeof credential === "string" ? normalizeCredential(credential) : credential; } async mapIssuances(issuances) { const promises = issuances.map(async (issuance) => { return { ...issuance, issueCallback: typeof issuance.credentialIssuance === "string" ? await this.getCredentialIssuance(issuance.credentialIssuance) : issuance.credentialIssuance }; }); return Promise.all(promises); } }; // src/migrations/generic/1-CreateWellknownDidIssuer.ts import Debug from "debug"; // src/migrations/postgres/1661165115000-CreateWellknownDidIssuer.ts var CreateWellknownDidIssuer1661165115000 = class { static { __name(this, "CreateWellknownDidIssuer1661165115000"); } name = "CreateWellknownDidIssuer1661165115000"; async up(queryRunner) { await queryRunner.query(`CREATE TABLE "DidConfigurationResource" ("origin" varchar NOT NULL, "context" varchar NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "last_updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_Origin" PRIMARY KEY ("origin"))`); await queryRunner.query(`CREATE TABLE "DidConfigurationResourceCredentials" ("didConfigurationResourceOrigin" varchar NOT NULL, "credentialHash" varchar NOT NULL)`); } async down(queryRunner) { await queryRunner.query(`DROP TABLE "DidConfigurationResource"`); await queryRunner.query(`DROP TABLE "DidConfigurationResourceCredentials"`); } }; // src/migrations/sqlite/1661161799000-CreateWellknownDidIssuer.ts var CreateWellknownDidIssuer1661161799000 = class { static { __name(this, "CreateWellknownDidIssuer1661161799000"); } name = "CreateWellknownDidIssuer1661161799000"; async up(queryRunner) { await queryRunner.query(`CREATE TABLE "DidConfigurationResource" ("origin" varchar PRIMARY KEY NOT NULL, "context" varchar NOT NULL, "created_at" datetime NOT NULL DEFAULT (datetime('now')), "last_updated_at" datetime NOT NULL DEFAULT (datetime('now')))`); await queryRunner.query(`CREATE TABLE "DidConfigurationResourceCredentials" ("didConfigurationResourceOrigin" varchar NOT NULL, "credentialHash" varchar NOT NULL)`); } async down(queryRunner) { await queryRunner.query(`DROP TABLE "DidConfigurationResource"`); await queryRunner.query(`DROP TABLE "DidConfigurationResourceCredentials"`); } }; // src/migrations/generic/1-CreateWellknownDidIssuer.ts var debug = Debug("sphereon:ssi-sdk:migrations"); var CreateWellknownDidIssuer1661162010000 = class { static { __name(this, "CreateWellknownDidIssuer1661162010000"); } name = "CreateWellknownDidIssuer1661162010000"; async up(queryRunner) { debug("migration: creating well-known DID tables"); const dbType = queryRunner.connection.driver.options.type; if (dbType === "postgres") { debug("using postgres migration file"); const mig = new CreateWellknownDidIssuer1661165115000(); const up = await mig.up(queryRunner); debug("Migration statements executed"); return up; } else if (dbType === "sqlite" || dbType === "react-native" || dbType === "expo") { debug("using sqlite/react-native migration file"); const mig = new CreateWellknownDidIssuer1661161799000(); const up = await mig.up(queryRunner); debug("Migration statements executed"); return up; } else { return Promise.reject(`Migrations are currently only supported for sqlite, react-native, expor and postgres. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`); } } async down(queryRunner) { debug("reverting well-known DID tables"); const dbType = queryRunner.connection.driver.options.type; if (dbType === "postgres") { debug("using postgres migration file"); const mig = new CreateWellknownDidIssuer1661165115000(); const down = await mig.down(queryRunner); debug("Migration statements executed"); return down; } else if (dbType === "sqlite" || dbType === "react-native" || dbType === "expo") { debug("using sqlite/react-native migration file"); const mig = new CreateWellknownDidIssuer1661161799000(); const down = await mig.down(queryRunner); debug("Migration statements executed"); return down; } else { return Promise.reject(`Migrations are currently only supported for sqlite, react-native, expor and postgres. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`); } } }; // src/migrations/generic/index.ts var WellknownDidIssuerMigrations = [ CreateWellknownDidIssuer1661162010000 ]; // src/index.ts var schema = require_plugin_schema(); var WellknownDidIssuerEntities = [ DidConfigurationResourceEntity ]; export { DidConfigurationResourceEntity, WellKnownDidIssuer, WellknownDidIssuerEntities, WellknownDidIssuerMigrations, schema }; //# sourceMappingURL=index.js.map