@sphereon/ssi-sdk.ebsi-support
Version:
237 lines • 13.3 kB
JavaScript
;
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.EbsiSupport = exports.ebsiSupportMethods = void 0;
const did_auth_siop_1 = require("@sphereon/did-auth-siop");
const oid4vci_common_1 = require("@sphereon/oid4vci-common");
const ssi_types_1 = require("@sphereon/ssi-types");
const cross_fetch_1 = __importDefault(require("cross-fetch"));
const functions_1 = require("../did/functions");
const functions_2 = require("../functions");
const index_1 = require("../index");
const uuid_1 = require("uuid");
const did_auth_siop_adapter_1 = require("@sphereon/did-auth-siop-adapter");
const ssi_sdk_core_1 = require("@sphereon/ssi-sdk.core");
exports.ebsiSupportMethods = [
'ebsiCreateDidOnLedger',
'ebsiWellknownMetadata',
'ebsiAuthorizationServerJwks',
'ebsiPresentationDefinitionGet',
'ebsiAccessTokenGet',
'ebsiCreateAttestationAuthRequestURL',
'ebsiGetAttestation',
];
class EbsiSupport {
constructor() {
this.schema = index_1.schema.IEbsiSupport;
this.methods = {
ebsiCreateDidOnLedger: this.ebsiCreateDidOnLedger.bind(this),
ebsiWellknownMetadata: this.ebsiWellknownMetadata.bind(this),
ebsiAuthorizationServerJwks: this.ebsiAuthorizationServerJwks.bind(this),
ebsiPresentationDefinitionGet: this.ebsiPresentationDefinitionGet.bind(this),
ebsiAccessTokenGet: this.ebsiAccessTokenGet.bind(this),
ebsiCreateAttestationAuthRequestURL: functions_2.ebsiCreateAttestationAuthRequestURL.bind(this),
ebsiGetAttestation: functions_2.ebsiGetAttestation.bind(this),
};
}
ebsiCreateDidOnLedger(args, context) {
return __awaiter(this, void 0, void 0, function* () {
return yield (0, functions_1.ebsiCreateDidOnLedger)(args, context);
});
}
ebsiWellknownMetadata(args) {
return __awaiter(this, void 0, void 0, function* () {
const url = (0, functions_1.determineWellknownEndpoint)(args);
return yield (yield (0, cross_fetch_1.default)(url, {
method: 'GET',
headers: {
Accept: 'application/json',
},
})).json();
});
}
ebsiAuthorizationServerJwks(args) {
return __awaiter(this, void 0, void 0, function* () {
const discoveryMetadata = yield this.ebsiWellknownMetadata(Object.assign(Object.assign({}, args), { type: 'openid-configuration' }));
return yield (yield (0, cross_fetch_1.default)(`${discoveryMetadata.jwks_uri}`, {
method: 'GET',
headers: {
Accept: 'application/jwk-set+json',
},
})).json();
});
}
ebsiPresentationDefinitionGet(args) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { scope, apiOpts, openIDMetadata } = args;
const discoveryMetadata = openIDMetadata !== null && openIDMetadata !== void 0 ? openIDMetadata : (yield this.ebsiWellknownMetadata(Object.assign(Object.assign({}, apiOpts), { type: 'openid-configuration', system: (apiOpts === null || apiOpts === void 0 ? void 0 : apiOpts.mock) ? 'authorisation' : apiOpts === null || apiOpts === void 0 ? void 0 : apiOpts.system, version: (_a = apiOpts === null || apiOpts === void 0 ? void 0 : apiOpts.version) !== null && _a !== void 0 ? _a : 'v4' })));
return (yield (yield (0, cross_fetch_1.default)(`${discoveryMetadata.presentation_definition_endpoint}?scope=openid%20${scope}`, {
method: 'GET',
headers: {
Accept: 'application/json',
},
})).json());
});
}
ebsiAccessTokenGet(args, context) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { scope, idOpts, jwksUri, clientId, allVerifiableCredentials, redirectUri, environment, skipDidResolution = false } = args;
const identifier = yield context.agent.identifierManagedGetByDid(idOpts);
console.log(`Getting access token for ${identifier.did}, scope ${scope} and clientId=${clientId}, skipDidResolution=${skipDidResolution}...`);
const openIDMetadata = yield this.ebsiWellknownMetadata({
environment,
version: 'v4',
mock: undefined,
system: 'authorisation',
type: 'openid-configuration',
});
const definitionResponse = yield this.ebsiPresentationDefinitionGet(Object.assign(Object.assign({}, args), { openIDMetadata, apiOpts: { environment, version: 'v4', type: 'openid-configuration' } }));
const hasInputDescriptors = definitionResponse.input_descriptors.length > 0;
console.log(`PD response`, definitionResponse);
if (!hasInputDescriptors) {
// Yes EBSI expects VPs without a VC in some situations. This is not according to the PEX spec!
// They probably should have used SIOP in these cases. We need to go through hoops as our libs do not expect PDs/VPs without VCs :(
console.warn(`No INPUT descriptor returned for scope ${scope}`);
}
let attestationCredential = args.attestationCredential;
if (hasInputDescriptors && !attestationCredential) {
if (allVerifiableCredentials && allVerifiableCredentials.length > 0) {
const pexResult = yield context.agent.pexDefinitionFilterCredentials({
presentationDefinition: definitionResponse,
credentialFilterOpts: { credentialRole: args.credentialRole, verifiableCredentials: allVerifiableCredentials },
});
if (pexResult.filteredCredentials.length > 0) {
const filtered = pexResult.filteredCredentials
.map((cred) => ssi_types_1.CredentialMapper.toUniformCredential(cred, { hasher: ssi_sdk_core_1.defaultHasher }))
.filter((cred) => {
if (!cred.expirationDate) {
return cred;
}
else if (new Date(cred.expirationDate).getDate() >= Date.now()) {
return cred;
}
return undefined;
})
.filter((cred) => !!cred);
if (filtered.length > 0) {
attestationCredential = filtered[0];
}
}
}
if (!attestationCredential) {
console.log(`No attestation credential present. Will get one from within access token method!`);
const credentialIssuer = (_a = args.credentialIssuer) !== null && _a !== void 0 ? _a : (0, functions_1.ebsiGetIssuerMock)({ environment });
const authReqResult = yield context.agent.ebsiCreateAttestationAuthRequestURL({
credentialIssuer,
idOpts,
formats: ['jwt_vc'],
clientId,
redirectUri,
requestObjectOpts: {
iss: clientId,
requestObjectMode: oid4vci_common_1.CreateRequestObjectMode.REQUEST_OBJECT,
jwksUri,
},
credentialType: 'VerifiableAuthorisationToOnboard',
});
const attestationResult = yield context.agent.ebsiGetAttestation({
authReqResult,
clientId,
opts: { timeout: 30000 },
});
// @ts-ignore
attestationCredential = attestationResult.credentials[0].rawVerifiableCredential;
}
}
const definition = {
definition: definitionResponse,
location: did_auth_siop_1.PresentationDefinitionLocation.TOPLEVEL_PRESENTATION_DEF,
version: did_auth_siop_1.SupportedVersion.SIOPv2_D11,
};
const pexResult = hasInputDescriptors
? yield context.agent.pexDefinitionFilterCredentials({
presentationDefinition: definitionResponse,
credentialFilterOpts: { credentialRole: args.credentialRole, verifiableCredentials: [attestationCredential] },
})
: {
// LOL, let's see whether we can trick PEX to create a VP without VCs
filteredCredentials: [],
id: definitionResponse.id,
selectResults: { verifiableCredential: [], areRequiredCredentialsPresent: 'info' },
};
const opSession = yield context.agent.siopRegisterOPSession({
requestJwtOrUri: '', // Siop assumes we use an auth request, which we don't have in this case
op: { checkLinkedDomains: did_auth_siop_adapter_1.CheckLinkedDomain.NEVER },
providedPresentationDefinitions: [definition],
});
const oid4vp = yield opSession.getOID4VP({ allIdentifiers: [identifier.did] });
const vp = yield oid4vp.createVerifiablePresentation(args.credentialRole, { definition, credentials: pexResult.filteredCredentials }, {
proofOpts: { domain: openIDMetadata.issuer, nonce: (0, uuid_1.v4)(), created: new Date(Date.now() - 120000).toString() },
holder: identifier.did,
idOpts: idOpts,
skipDidResolution,
forceNoCredentialsInVP: !hasInputDescriptors,
});
const presentationSubmission = hasInputDescriptors
? vp.presentationSubmission
: { id: (0, uuid_1.v4)(), definition_id: definitionResponse.id, descriptor_map: [] };
console.log(`Presentation submission`, presentationSubmission);
const tokenRequestArgs = {
grant_type: 'vp_token',
vp_token: ssi_types_1.CredentialMapper.toCompactJWT(vp.verifiablePresentations[0]), // FIXME How are we going to send multiple presentations in a vp_token?
scope,
presentation_submission: presentationSubmission,
apiOpts: { environment, version: 'v4' },
openIDMetadata,
};
console.log(`Access token request:\r\n${JSON.stringify(tokenRequestArgs)}`);
const accessTokenResponse = yield this.getAccessToken(tokenRequestArgs);
console.log(`Access token response:\r\n${JSON.stringify(accessTokenResponse)}`);
if (!('access_token' in accessTokenResponse)) {
throw Error(`Error response: ${JSON.stringify(accessTokenResponse)}`);
}
return {
accessTokenResponse,
// vp,
scope,
// definition,
identifier: identifier,
};
});
}
getAccessToken(args) {
return __awaiter(this, void 0, void 0, function* () {
const { grant_type = 'vp_token', scope, vp_token, presentation_submission, apiOpts, openIDMetadata } = args;
const discoveryMetadata = openIDMetadata !== null && openIDMetadata !== void 0 ? openIDMetadata : (yield this.ebsiWellknownMetadata(Object.assign(Object.assign({}, apiOpts), { type: 'openid-configuration' })));
const request = {
grant_type,
scope: `openid ${scope}`,
vp_token,
presentation_submission: JSON.stringify(presentation_submission),
};
return yield (yield (0, cross_fetch_1.default)(`${discoveryMetadata.token_endpoint}`, {
method: 'POST',
headers: {
ContentType: 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
body: new URLSearchParams(request),
})).json();
});
}
}
exports.EbsiSupport = EbsiSupport;
//# sourceMappingURL=EbsiSupport.js.map