UNPKG

@sphereon/did-auth-siop

Version:

Self Issued OpenID V2 (SIOPv2) and OpenID 4 Verifiable Presentations (OID4VP)

258 lines 13.1 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InMemoryRPSessionManager = void 0; const types_1 = require("../types"); /** * Please note that this session manager is not really meant to be used in large production settings, as it stores everything in memory! * It also doesn't do scheduled cleanups. It runs a cleanup whenever a request or response is received. In a high-volume production setting you will want scheduled cleanups running in the background * Since this is a low level library we have not created a full-fledged implementation. * We suggest to create your own implementation using the event system of the library */ class InMemoryRPSessionManager { static getKeysForCorrelationId(mapping, correlationId) { return Object.entries(mapping) .filter((entry) => entry[1] === correlationId) .map((filtered) => Number.parseInt(filtered[0])); } constructor(eventEmitter, opts) { var _a; this.authorizationRequests = {}; this.authorizationResponses = {}; // stored by hashcode this.nonceMapping = {}; // stored by hashcode this.stateMapping = {}; if (!eventEmitter) { throw Error('RP Session manager depends on an event emitter in the application'); } this.maxAgeInSeconds = (_a = opts === null || opts === void 0 ? void 0 : opts.maxAgeInSeconds) !== null && _a !== void 0 ? _a : 5 * 60; eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_REQUEST_CREATED_SUCCESS, this.onAuthorizationRequestCreatedSuccess.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_REQUEST_CREATED_FAILED, this.onAuthorizationRequestCreatedFailed.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_REQUEST_SENT_SUCCESS, this.onAuthorizationRequestSentSuccess.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_REQUEST_SENT_FAILED, this.onAuthorizationRequestSentFailed.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_RESPONSE_RECEIVED_SUCCESS, this.onAuthorizationResponseReceivedSuccess.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_RESPONSE_RECEIVED_FAILED, this.onAuthorizationResponseReceivedFailed.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_RESPONSE_VERIFIED_SUCCESS, this.onAuthorizationResponseVerifiedSuccess.bind(this)); eventEmitter.on(types_1.AuthorizationEvents.ON_AUTH_RESPONSE_VERIFIED_FAILED, this.onAuthorizationResponseVerifiedFailed.bind(this)); } getRequestStateByCorrelationId(correlationId, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('correlationId', correlationId, this.authorizationRequests, errorOnNotFound); }); } getRequestStateByNonce(nonce, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('nonce', nonce, this.authorizationRequests, errorOnNotFound); }); } getRequestStateByState(state, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('state', state, this.authorizationRequests, errorOnNotFound); }); } getResponseStateByCorrelationId(correlationId, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('correlationId', correlationId, this.authorizationResponses, errorOnNotFound); }); } getResponseStateByNonce(nonce, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('nonce', nonce, this.authorizationResponses, errorOnNotFound); }); } getResponseStateByState(state, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getFromMapping('state', state, this.authorizationResponses, errorOnNotFound); }); } getFromMapping(type, value, mapping, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { const correlationId = yield this.getCorrelationIdImpl(type, value, errorOnNotFound); const result = mapping[correlationId]; if (!result && errorOnNotFound) { throw Error(`Could not find '${type}' belonging to correlation id '${correlationId}'`); } return result; }); } onAuthorizationRequestCreatedSuccess(event) { return __awaiter(this, void 0, void 0, function* () { try { this.updateState('request', event, types_1.AuthorizationRequestStateStatus.CREATED); this.cleanup().catch((error) => console.log(JSON.stringify(error))); } catch (error) { console.log(JSON.stringify(error)); } }); } onAuthorizationRequestCreatedFailed(event) { return __awaiter(this, void 0, void 0, function* () { this.cleanup().catch((error) => console.log(JSON.stringify(error))); this.updateState('request', event, types_1.AuthorizationRequestStateStatus.ERROR); }); } onAuthorizationRequestSentSuccess(event) { return __awaiter(this, void 0, void 0, function* () { this.cleanup().catch((error) => console.log(JSON.stringify(error))); this.updateState('request', event, types_1.AuthorizationRequestStateStatus.SENT); }); } onAuthorizationRequestSentFailed(event) { return __awaiter(this, void 0, void 0, function* () { this.cleanup().catch((error) => console.log(JSON.stringify(error))); this.updateState('request', event, types_1.AuthorizationRequestStateStatus.ERROR); }); } onAuthorizationResponseReceivedSuccess(event) { return __awaiter(this, void 0, void 0, function* () { this.cleanup().catch((error) => console.log(JSON.stringify(error))); this.updateState('response', event, types_1.AuthorizationResponseStateStatus.RECEIVED); }); } onAuthorizationResponseReceivedFailed(event) { return __awaiter(this, void 0, void 0, function* () { this.cleanup().catch((error) => console.log(JSON.stringify(error))); this.updateState('response', event, types_1.AuthorizationResponseStateStatus.ERROR); }); } onAuthorizationResponseVerifiedFailed(event) { return __awaiter(this, void 0, void 0, function* () { this.updateState('response', event, types_1.AuthorizationResponseStateStatus.ERROR); }); } onAuthorizationResponseVerifiedSuccess(event) { return __awaiter(this, void 0, void 0, function* () { this.updateState('response', event, types_1.AuthorizationResponseStateStatus.VERIFIED); }); } getCorrelationIdByNonce(nonce, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getCorrelationIdImpl('nonce', nonce, errorOnNotFound); }); } getCorrelationIdByState(state, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { return yield this.getCorrelationIdImpl('state', state, errorOnNotFound); }); } getCorrelationIdImpl(type, value, errorOnNotFound) { return __awaiter(this, void 0, void 0, function* () { if (!value || !type) { throw Error('No type or value provided'); } if (type === 'correlationId') { return value; } const hash = hashCode(value); const correlationId = type === 'nonce' ? this.nonceMapping[hash] : this.stateMapping[hash]; if (!correlationId && errorOnNotFound) { throw Error(`Could not find ${type} value for ${value}`); } return correlationId; }); } updateMapping(mapping, event, key, value, allowExisting) { const hash = hashcodeForValue(event, key); const existing = mapping[hash]; if (existing) { if (!allowExisting) { throw Error(`Mapping exists for key ${key} and we do not allow overwriting values`); } else if (value && existing !== value) { throw Error(`Value changed for key ${key} from ${existing} to ${value}`); } } if (!value) { delete mapping[hash]; } else { mapping[hash] = value; } } updateState(type, event, status) { if (!event) { throw new Error('event not present'); } else if (!event.correlationId) { throw new Error(`'${type} ${status}' event without correlation id received`); } try { const eventState = Object.assign(Object.assign(Object.assign(Object.assign({ correlationId: event.correlationId }, (type === 'request' ? { request: event.subject } : {})), (type === 'response' ? { response: event.subject } : {})), (event.error ? { error: event.error } : {})), { status, timestamp: event.timestamp, lastUpdated: event.timestamp }); if (type === 'request') { this.authorizationRequests[event.correlationId] = eventState; this.updateMapping(this.nonceMapping, event, 'nonce', event.correlationId, true); this.updateMapping(this.stateMapping, event, 'state', event.correlationId, true); } else { this.authorizationResponses[event.correlationId] = eventState; } } catch (error) { console.log(`Error in update state happened: ${error}`); // TODO VDX-166 handle error } } deleteStateForCorrelationId(correlationId) { return __awaiter(this, void 0, void 0, function* () { InMemoryRPSessionManager.cleanMappingForCorrelationId(this.nonceMapping, correlationId).catch((error) => console.log(JSON.stringify(error))); InMemoryRPSessionManager.cleanMappingForCorrelationId(this.stateMapping, correlationId).catch((error) => console.log(JSON.stringify(error))); delete this.authorizationRequests[correlationId]; delete this.authorizationResponses[correlationId]; }); } static cleanMappingForCorrelationId(mapping, correlationId) { return __awaiter(this, void 0, void 0, function* () { const keys = InMemoryRPSessionManager.getKeysForCorrelationId(mapping, correlationId); if (keys && keys.length > 0) { keys.forEach((key) => delete mapping[key]); } }); } cleanup() { return __awaiter(this, void 0, void 0, function* () { const now = Date.now(); const maxAgeInMS = this.maxAgeInSeconds * 1000; const cleanupCorrelations = (reqByCorrelationId) => { const correlationId = reqByCorrelationId[0]; const authRequest = reqByCorrelationId[1]; if (authRequest) { const ts = authRequest.lastUpdated || authRequest.timestamp; if (maxAgeInMS !== 0 && now > ts + maxAgeInMS) { this.deleteStateForCorrelationId(correlationId); } } }; Object.entries(this.authorizationRequests).forEach((reqByCorrelationId) => { cleanupCorrelations.call(this, reqByCorrelationId); }); Object.entries(this.authorizationResponses).forEach((resByCorrelationId) => { cleanupCorrelations.call(this, resByCorrelationId); }); }); } } exports.InMemoryRPSessionManager = InMemoryRPSessionManager; function hashcodeForValue(event, key) { const value = event.subject.getMergedProperty(key); if (!value) { throw Error(`No value found for key ${key} in Authorization Request`); } return hashCode(value); } function hashCode(s) { let h = 1; for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0; return h; } //# sourceMappingURL=InMemoryRPSessionManager.js.map