UNPKG

@sphereon/ssi-sdk.ebsi-support

Version:

237 lines • 13.3 kB
"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.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