@sphereon/ssi-sdk.presentation-exchange
Version:
599 lines (595 loc) • 22.4 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 = {
IDidAuthSiopOpAuthenticator: {
components: {
schemas: {
IGetSiopSessionArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
additionalProperties: false
},
required: ["sessionId"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.getSessionForSiop } "
},
IRegisterSiopSessionArgs: {
type: "object",
properties: {
identifier: {
type: "object",
properties: {
did: {
type: "string"
},
alias: {
type: "string"
},
provider: {
type: "string"
},
controllerKeyId: {
type: "string"
},
keys: {
type: "array",
items: {
type: "object",
properties: {
additionalProperties: true
}
}
},
services: {
type: "array",
items: {
type: "object",
properties: {
additionalProperties: true
}
}
}
},
additionalProperties: false,
required: ["did", "provider", "keys", "services"]
},
sessionId: {
type: "string"
},
expiresIn: {
type: "number"
},
additionalProperties: false
},
required: ["identifier"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.registerSessionForSiop } "
},
IRemoveSiopSessionArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
additionalProperties: false
},
required: ["sessionId"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.removeSessionForSiop } "
},
IAuthenticateWithSiopArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
stateId: {
type: "string"
},
redirectUrl: {
type: "string"
},
additionalProperties: false
},
required: ["sessionId", "stateId", "redirectUrl"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.authenticateWithSiop } "
},
IResponse: {
type: "object",
properties: {
status: {
type: "number"
},
additionalProperties: true
},
required: ["status"],
description: "Result of {@link DidAuthSiopOpAuthenticator.authenticateWithSiop & DidAuthSiopOpAuthenticator.sendSiopAuthenticationResponse } "
},
IGetSiopAuthenticationRequestFromRpArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
stateId: {
type: "string"
},
redirectUrl: {
type: "string"
},
additionalProperties: false
},
required: ["sessionId", "stateId", "redirectUrl"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.getSiopAuthenticationRequestFromRP } "
},
ParsedAuthenticationRequestURI: {
type: "object",
properties: {
jwt: {
type: "string"
},
requestPayload: {
type: "object",
properties: {
additionalProperties: true
}
},
registration: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["jwt", "requestPayload", "registration"],
description: "Result of {@link DidAuthSiopOpAuthenticator.getSiopAuthenticationRequestFromRP } "
},
IGetSiopAuthenticationRequestDetailsArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
verifiedAuthenticationRequest: {
type: "object",
properties: {
additionalProperties: true
}
},
credentialFilter: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["sessionId", "verifiedAuthenticationRequest"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.getSiopAuthenticationRequestDetails } "
},
IAuthRequestDetails: {
type: "object",
properties: {
id: {
type: "string"
},
alsoKnownAs: {
type: "array",
items: {
type: "string"
}
},
vpResponseOpts: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["id", "vpResponseOpts"],
description: "Result of {@link DidAuthSiopOpAuthenticator.getSiopAuthenticationRequestDetails } "
},
IVerifySiopAuthenticationRequestUriArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
ParsedAuthenticationRequestURI: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["sessionId", "ParsedAuthenticationRequestURI"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.verifySiopAuthenticationRequestURI } "
},
VerifiedAuthorizationRequest: {
type: "object",
properties: {
payload: {
type: "object",
properties: {
additionalProperties: true
}
},
presentationDefinitions: {
type: "object",
properties: {
additionalProperties: true
}
},
verifyOpts: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["payload", "verifyOpts"],
description: "Result of {@link DidAuthSiopOpAuthenticator.verifySiopAuthenticationRequestURI } "
},
ISendSiopAuthenticationResponseArgs: {
type: "object",
properties: {
sessionId: {
type: "string"
},
verifiedAuthenticationRequest: {
type: "object",
properties: {
additionalProperties: true
}
},
verifiablePresentationResponse: {
type: "object",
properties: {
additionalProperties: true
}
},
additionalProperties: false
},
required: ["sessionId", "verifiedAuthenticationRequest"],
description: "Arguments needed for {@link DidAuthSiopOpAuthenticator.sendSiopAuthenticationResponse } "
}
},
methods: {
getSessionForSiop: {
description: "Get SIOP session",
arguments: {
$ref: "#/components/schemas/IGetSiopSessionArgs"
},
returnType: "object"
},
registerSessionForSiop: {
description: "Register SIOP session",
arguments: {
$ref: "#/components/schemas/IRegisterSiopSessionArgs"
},
returnType: "object"
},
removeSessionForSiop: {
description: "Remove SIOP session",
arguments: {
$ref: "#/components/schemas/IRemoveSiopSessionArgs"
},
returnType: "boolean"
},
authenticateWithSiop: {
description: "Authenticate using DID Auth SIOP",
arguments: {
$ref: "#/components/schemas/IAuthenticateWithSiopArgs"
},
returnType: {
$ref: "#/components/schemas/Response"
}
},
getSiopAuthenticationRequestFromRP: {
description: "Get authentication request from RP",
arguments: {
$ref: "#/components/schemas/IGetSiopAuthenticationRequestFromRpArgs"
},
returnType: {
$ref: "#/components/schemas/ParsedAuthenticationRequestURI"
}
},
getSiopAuthenticationRequestDetails: {
description: "Get authentication request details",
arguments: {
$ref: "#/components/schemas/IGetSiopAuthenticationRequestDetailsArgs"
},
returnType: {
$ref: "#/components/schemas/IAuthRequestDetails"
}
},
verifySiopAuthenticationRequestURI: {
description: "Verify authentication request URI",
arguments: {
$ref: "#/components/schemas/IVerifySiopAuthenticationRequestUriArgs"
},
returnType: {
$ref: "#/components/schemas/VerifiedAuthorizationRequest"
}
},
sendSiopAuthenticationResponse: {
description: "Send authentication response",
arguments: {
$ref: "#/components/schemas/ISendSiopAuthenticationResponseArgs"
},
returnType: {
$ref: "#/components/schemas/IRequiredContext"
}
}
}
}
}
};
}
});
// src/agent/PresentationExchange.ts
import { PEX } from "@sphereon/pex";
import { CredentialMapper, JWT_PROOF_TYPE_2020 } from "@sphereon/ssi-types";
import { toDIDs } from "@sphereon/ssi-sdk-ext.did-utils";
import { verifiableCredentialForRoleFilter } from "@sphereon/ssi-sdk.credential-store";
var PresentationExchange = class {
static {
__name(this, "PresentationExchange");
}
schema = schema.IDidAuthSiopOpAuthenticator;
pex = new PEX();
methods = {
pexValidateDefinition: this.pexValidateDefinition.bind(this),
pexDefinitionVersion: this.pexDefinitionVersion.bind(this),
pexDefinitionFilterCredentials: this.pexDefinitionFilterCredentials.bind(this),
pexDefinitionFilterCredentialsPerInputDescriptor: this.pexDefinitionFilterCredentialsPerInputDescriptor.bind(this)
};
constructor(opts) {
}
async pexValidateDefinition(args) {
const { definition } = args;
const invalids = [];
try {
const result = PEX.validateDefinition(definition);
const validations = Array.isArray(result) ? result : [
result
];
invalids.push(...validations.filter((v) => v.status === "error"));
} catch (error) {
invalids.push({
status: "error",
message: typeof error === "string" ? error : typeof error === "object" && "message" in error ? error.message : "unknown error",
tag: "validation"
});
}
if (invalids.length > 0) {
throw Error(`Invalid definition. ${invalids.map((v) => v.message).toString()}`);
}
return true;
}
async pexDefinitionVersion(presentationDefinition) {
return PEX.definitionVersionDiscovery(presentationDefinition);
}
async pexDefinitionFilterCredentials(args, context) {
const credentials = await this.pexFilterCredentials(args.credentialFilterOpts, context);
const holderDIDs = args.holderDIDs ? toDIDs(args.holderDIDs) : toDIDs(await context.agent.dataStoreORMGetIdentifiers());
const selectResults = this.pex.selectFrom(args.presentationDefinition, credentials ?? [], {
...args,
holderDIDs,
limitDisclosureSignatureSuites: args.limitDisclosureSignatureSuites ?? [
"BbsBlsSignature2020"
]
});
return {
id: args.presentationDefinition.id,
selectResults,
filteredCredentials: selectResults.verifiableCredential?.map((vc) => CredentialMapper.storedCredentialToOriginalFormat(vc)) ?? []
};
}
async pexDefinitionFilterCredentialsPerInputDescriptor(args, context) {
const origDefinition = args.presentationDefinition;
const credentials = await this.pexFilterCredentials(args.credentialFilterOpts ?? {}, context);
const holderDIDs = args.holderDIDs ? toDIDs(args.holderDIDs) : toDIDs(await context.agent.dataStoreORMGetIdentifiers());
const limitDisclosureSignatureSuites = args.limitDisclosureSignatureSuites;
const promises = /* @__PURE__ */ new Map();
origDefinition.input_descriptors.forEach((inputDescriptor) => {
const presentationDefinition = {
id: inputDescriptor.id,
input_descriptors: [
inputDescriptor
]
};
const credentialRole = args.credentialFilterOpts.credentialRole;
promises.set(inputDescriptor, this.pexDefinitionFilterCredentials({
credentialFilterOpts: {
credentialRole,
verifiableCredentials: credentials
},
// @ts-ignore
presentationDefinition,
holderDIDs,
limitDisclosureSignatureSuites
}, context));
});
await Promise.all(promises.values());
const result = [];
for (const entry of promises.entries()) {
result.push({
...await entry[1],
inputDescriptor: entry[0]
});
}
return result;
}
async pexFilterCredentials(filterOpts, context) {
if (filterOpts.verifiableCredentials && filterOpts.verifiableCredentials.length > 0) {
return filterOpts.verifiableCredentials;
}
const filter = verifiableCredentialForRoleFilter(filterOpts.credentialRole, filterOpts.filter);
const uniqueCredentials = await context.agent.crsGetUniqueCredentials({
filter
});
return uniqueCredentials.map((uniqueVC) => {
const vc = uniqueVC.uniformVerifiableCredential;
const proof = Array.isArray(vc.proof) ? vc.proof : [
vc.proof
];
const jwtProof = proof.find((p) => p?.type === JWT_PROOF_TYPE_2020);
return jwtProof ? jwtProof.jwt : vc;
});
}
};
// src/functions.ts
import { isManagedIdentifierDidOpts, isManagedIdentifierDidResult, isManagedIdentifierX5cResult } from "@sphereon/ssi-sdk-ext.identifier-resolution";
import { CredentialMapper as CredentialMapper2 } from "@sphereon/ssi-types";
async function createPEXPresentationSignCallback(args, context) {
function determineProofFormat(determineArgs) {
const { format, presentationDefinition, presentation } = determineArgs;
var formatOptions = format ?? presentationDefinition.format ?? args.format;
if (!formatOptions && presentationDefinition.input_descriptors.length == 1 && "format" in presentationDefinition.input_descriptors[0]) {
formatOptions = presentationDefinition.input_descriptors[0].format;
}
if (!formatOptions) {
if (CredentialMapper2.isSdJwtDecodedCredentialPayload(presentation.decodedPayload)) {
return "vc+sd-jwt";
} else if (CredentialMapper2.isMsoMdocDecodedPresentation(presentation.decodedPayload)) {
return "mso_mdoc";
} else if (CredentialMapper2.isW3cPresentation(presentation.decodedPayload)) {
if (typeof presentation.signedPayload === "string") {
return "jwt";
}
return "lds";
}
return "jwt";
} else if (typeof formatOptions === "string") {
return formatOptions;
}
const formats = new Set(Object.keys(formatOptions).map((form) => form.includes("ldp") ? "lds" : form.includes("vc+sd-jwt") ? "vc+sd-jwt" : "jwt"));
if (formats.size === 1) {
return formats.values().next().value;
}
formats.keys().next();
if (formats.has("vc+sd-jwt")) {
return "vc+sd-jwt";
} else if (formats.has("jwt")) {
return "jwt";
}
return "lds";
}
__name(determineProofFormat, "determineProofFormat");
return async ({ presentation, domain, presentationDefinition, format, challenge }) => {
const proofFormat = determineProofFormat({
format,
presentationDefinition,
presentation
});
const { idOpts } = args;
const CLOCK_SKEW = 120;
if (args.skipDidResolution && isManagedIdentifierDidOpts(idOpts)) {
idOpts.offlineWhenNoDIDRegistered = true;
}
if ("compactSdJwtVc" in presentation) {
if (proofFormat !== "vc+sd-jwt") {
return Promise.reject(Error(`presentation payload does not match proof format ${proofFormat}`));
}
const presentationResult = await context.agent.createSdJwtPresentation({
...idOpts?.method === "oid4vci-issuer" && {
holder: idOpts?.issuer
},
presentation: presentation.compactSdJwtVc,
kb: {
payload: {
...presentation.kbJwt?.payload,
iat: presentation.kbJwt?.payload?.iat ?? Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
nonce: challenge ?? presentation.kbJwt?.payload?.nonce,
aud: presentation.kbJwt?.payload?.aud ?? domain ?? args.domain
}
}
});
return CredentialMapper2.storedPresentationToOriginalFormat(presentationResult.presentation);
} else {
const resolution = await context.agent.identifierManagedGet(idOpts);
if (proofFormat === "vc+sd-jwt") {
return Promise.reject(Error(`presentation payload does not match proof format ${proofFormat}`));
}
let header;
if (!presentation.holder) {
presentation.holder = resolution.issuer;
}
if (proofFormat === "jwt") {
header = {
...(isManagedIdentifierDidResult(resolution) || isManagedIdentifierX5cResult(resolution)) && resolution.kid && {
kid: resolution.kid
},
...isManagedIdentifierX5cResult(resolution) && {
jwk: resolution.jwk
}
};
if (presentation.verifier || !presentation.aud) {
presentation.aud = Array.isArray(presentation.verifier) ? presentation.verifier : presentation.verifier ?? domain ?? args.domain;
delete presentation.verifier;
}
if (!presentation.nbf) {
if (presentation.issuanceDate) {
const converted = Date.parse(presentation.issuanceDate);
if (!isNaN(converted)) {
presentation.nbf = Math.floor(converted / 1e3);
}
} else {
presentation.nbf = Math.floor(Date.now() / 1e3 - CLOCK_SKEW);
}
}
if (!presentation.iat) {
presentation.iat = presentation.nbf;
}
if (!presentation.exp) {
if (presentation.expirationDate) {
const converted = Date.parse(presentation.expirationDate);
if (!isNaN(converted)) {
presentation.exp = Math.floor(converted / 1e3);
}
} else {
presentation.exp = presentation.nbf + 600 + CLOCK_SKEW;
}
}
if (!presentation.vp) {
presentation.vp = {};
}
if (!presentation.vp.holder) {
presentation.vp.holder = presentation.holder;
}
}
const vp = await context.agent.createVerifiablePresentation({
presentation,
removeOriginalFields: false,
keyRef: resolution.kmsKeyRef,
// domain: domain ?? args.domain, // handled above, and did-jwt-vc creates an array even for 1 entry
challenge: challenge ?? args.challenge,
fetchRemoteContexts: args.fetchRemoteContexts !== false,
proofFormat,
header
});
return CredentialMapper2.storedPresentationToOriginalFormat(vp);
}
};
}
__name(createPEXPresentationSignCallback, "createPEXPresentationSignCallback");
// src/index.ts
var schema = require_plugin_schema();
export {
PresentationExchange,
createPEXPresentationSignCallback,
schema
};
//# sourceMappingURL=index.js.map