@sphereon/ssi-sdk.wellknown-did-issuer
Version:
579 lines (570 loc) • 24.1 kB
JavaScript
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