UNPKG

ng-smart-on-fhir

Version:

[![SRDC](https://www.srdc.com.tr/wp-content/uploads/2014/12/srdc-wp.png)](https://srdc.com.tr/en)

136 lines 19.5 kB
import { Injectable } from '@angular/core'; import { firstValueFrom } from "rxjs"; import * as jose from 'jose'; import * as pako from 'pako'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common/http"; /** * SMART Health Link Service * Executes the requests, parses and validates SMART Health Links as defined in the HL7 documentations */ export class ShlService { get emptyBundle() { return { resourceType: 'Bundle', type: 'document', entry: [] }; } constructor(http) { this.http = http; } async handleShl(shl) { if (shl.startsWith('shlink:/')) { shl = shl.slice('shlink:/'.length); } const metadata = JSON.parse(atob(shl)); if (!metadata.url) { throw new Error('URL is missing in SMART Health Link'); } let payload; if (metadata.flag?.includes('U')) { payload = await firstValueFrom(this.http.get(metadata.url + '?recipient=' + window.location.origin)) .then((data) => data.files.filter((file) => file.contentType === 'application/smart-health-card' && file.embedded).map((file) => file.embedded)); } else { let passcode; if (metadata.flag?.includes('P')) { passcode = prompt('Enter the passcode for SMART Health Link'); } payload = await firstValueFrom(this.http.post(metadata.url, { recipient: window.location.origin, passcode })).then((data) => data.files.filter((file) => file.contentType === 'application/smart-health-card' && file.embedded).map((file) => file.embedded)); } if (!payload) { throw new Error('Cannot get SMART Health Link content.'); } const shcs = await this.decryptSHLPayloadAndGetSHCs(payload, metadata.key); const bundles = await Promise.all(shcs.map(shc => this.getSingleBundle(shc))); return { label: metadata.label, shc: shcs, verified: await Promise.all(shcs.map(_shc => this.verifyShc(_shc))).then(results => results.every(value => value)), bundle: Object.assign(this.emptyBundle, { entry: bundles.filter(_ => _).map(bundle => bundle.entry).reduce((arr, entries) => arr.concat(entries), []) }) }; } async decryptSHLPayloadAndGetSHCs(payload, key) { const SHCs = []; for (const encrypted of payload) { const data = await jose.compactDecrypt(encrypted, jose.base64url.decode(key)); SHCs.push(...JSON.parse(new TextDecoder().decode(data.plaintext)).verifiableCredential); } return SHCs; } async handleShc(shc) { if (shc.startsWith('shc:/')) { shc = shc.slice('shc:/'.length); } return { shc: [shc], bundle: await this.getSingleBundle(shc), verified: await this.verifyShc(shc) }; } async getSingleBundle(shc) { return await this.parseShc(shc).then(data => data.vc.credentialSubject.fhirBundle); } async parseShc(shc) { try { const [h, p, s] = shc.split('.').map((part) => this.base64UrlToBytes(part)); return JSON.parse(this.inflate(p)); } catch (error) { console.error('Error parsing SMART Health Card:', error); throw error; } } async verifyShc(shc) { try { await jose.compactVerify(shc, jose.createLocalJWKSet(await this.fetchJWKs(shc))); return true; } catch (err) { return false; } } async fetchJWKs(shc) { try { const card = JSON.parse(this.inflate(this.base64UrlToBytes(shc.split('.')[1]))); const issuer = card.iss; const jwksUrl = `${issuer.replace(/\/$/, '')}/.well-known/jwks`; const jwksResponse = await firstValueFrom(this.http.get(jwksUrl)); return jwksResponse; } catch (error) { console.error('Error fetching JWKs:', error); throw new Error('Unable to fetch JWKs for signature verification'); } } inflate(data) { try { const decompressed = pako.inflateRaw(data, { to: 'string' }); return decompressed; } catch (error) { console.error('Error inflating data:', error); throw new Error('Failed to inflate data'); } } base64UrlToBytes(base64Url) { const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); const binaryString = atob(base64); return new Uint8Array([...binaryString].map((char) => char.charCodeAt(0))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ShlService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ShlService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ShlService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.HttpClient }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hsLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9zbWFydC1vbi1maGlyL3NyYy9saWIvc2VydmljZXMvc2hsLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLFVBQVUsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDOzs7QUFFN0I7OztHQUdHO0FBSUgsTUFBTSxPQUFPLFVBQVU7SUFFckIsSUFBSSxXQUFXO1FBQ2IsT0FBTztZQUNMLFlBQVksRUFBRSxRQUFRO1lBQ3RCLElBQUksRUFBRSxVQUFVO1lBQ2hCLEtBQUssRUFBRSxFQUFFO1NBQ1YsQ0FBQTtJQUNILENBQUM7SUFFRCxZQUFvQixJQUFnQjtRQUFoQixTQUFJLEdBQUosSUFBSSxDQUFZO0lBQ3BDLENBQUM7SUFFRCxLQUFLLENBQUMsU0FBUyxDQUFDLEdBQVc7UUFDekIsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFBQyxHQUFHLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUE7UUFBQyxDQUFDO1FBQ3RFLE1BQU0sUUFBUSxHQUFnRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQ25HLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFBO1FBQ3hELENBQUM7UUFDRCxJQUFJLE9BQU8sQ0FBQztRQUNaLElBQUksUUFBUSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsR0FBRyxDQUFDLEVB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LE1BQU07OzRGQUVQLFVBQVU7a0JBSHRCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtJbmplY3RhYmxlfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHtIdHRwQ2xpZW50fSBmcm9tIFwiQGFuZ3VsYXIvY29tbW9uL2h0dHBcIjtcclxuaW1wb3J0IHtmaXJzdFZhbHVlRnJvbX0gZnJvbSBcInJ4anNcIjtcclxuaW1wb3J0ICogYXMgam9zZSBmcm9tICdqb3NlJztcclxuaW1wb3J0ICogYXMgcGFrbyBmcm9tICdwYWtvJztcclxuXHJcbi8qKlxyXG4gKiBTTUFSVCBIZWFsdGggTGluayBTZXJ2aWNlXHJcbiAqIEV4ZWN1dGVzIHRoZSByZXF1ZXN0cywgcGFyc2VzIGFuZCB2YWxpZGF0ZXMgU01BUlQgSGVhbHRoIExpbmtzIGFzIGRlZmluZWQgaW4gdGhlIEhMNyBkb2N1bWVudGF0aW9uc1xyXG4gKi9cclxuQEluamVjdGFibGUoe1xyXG4gIHByb3ZpZGVkSW46ICdyb290J1xyXG59KVxyXG5leHBvcnQgY2xhc3MgU2hsU2VydmljZSB7XHJcblxyXG4gIGdldCBlbXB0eUJ1bmRsZSgpOiBmaGlyNC5CdW5kbGUge1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzb3VyY2VUeXBlOiAnQnVuZGxlJyxcclxuICAgICAgdHlwZTogJ2RvY3VtZW50JyxcclxuICAgICAgZW50cnk6IFtdXHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGh0dHA6IEh0dHBDbGllbnQpIHtcclxuICB9XHJcblxyXG4gIGFzeW5jIGhhbmRsZVNobChzaGw6IHN0cmluZykge1xyXG4gICAgaWYgKHNobC5zdGFydHNXaXRoKCdzaGxpbms6LycpKSB7IHNobCA9IHNobC5zbGljZSgnc2hsaW5rOi8nLmxlbmd0aCkgfVxyXG4gICAgY29uc3QgbWV0YWRhdGE6IHsgdXJsOiBzdHJpbmcsIGxhYmVsPzogc3RyaW5nLCBmbGFnPzogc3RyaW5nLCBrZXk6IHN0cmluZyB9ID0gSlNPTi5wYXJzZShhdG9iKHNobCkpXHJcbiAgICBpZiAoIW1ldGFkYXRhLnVybCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VSTCBpcyBtaXNzaW5nIGluIFNNQVJUIEhlYWx0aCBMaW5rJylcclxuICAgIH1cclxuICAgIGxldCBwYXlsb2FkO1xyXG4gICAgaWYgKG1ldGFkYXRhLmZsYWc/LmluY2x1ZGVzKCdVJykpIHtcclxuICAgICAgcGF5bG9hZCA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKHRoaXMuaHR0cC5nZXQobWV0YWRhdGEudXJsICsgJz9yZWNpcGllbnQ9JyArIHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4pKVxyXG4gICAgICAgIC50aGVuKChkYXRhOiBhbnkpID0+IGRhdGEuZmlsZXMuZmlsdGVyKChmaWxlOiBhbnkpID0+IGZpbGUuY29udGVudFR5cGUgPT09ICdhcHBsaWNhdGlvbi9zbWFydC1oZWFsdGgtY2FyZCcgJiYgZmlsZS5lbWJlZGRlZCkubWFwKChmaWxlOiBhbnkpID0+IGZpbGUuZW1iZWRkZWQpKVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHBhc3Njb2RlO1xyXG4gICAgICBpZiAobWV0YWRhdGEuZmxhZz8uaW5jbHVkZXMoJ1AnKSkge1xyXG4gICAgICAgIHBhc3Njb2RlID0gcHJvbXB0KCdFbnRlciB0aGUgcGFzc2NvZGUgZm9yIFNNQVJUIEhlYWx0aCBMaW5rJylcclxuICAgICAgfVxyXG4gICAgICBwYXlsb2FkID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20odGhpcy5odHRwLnBvc3QobWV0YWRhdGEudXJsLCB7XHJcbiAgICAgICAgcmVjaXBpZW50OiB3aW5kb3cubG9jYXRpb24ub3JpZ2luLFxyXG4gICAgICAgIHBhc3Njb2RlXHJcbiAgICAgIH0pKS50aGVuKChkYXRhOiBhbnkpID0+IGRhdGEuZmlsZXMuZmlsdGVyKChmaWxlOiBhbnkpID0+IGZpbGUuY29udGVudFR5cGUgPT09ICdhcHBsaWNhdGlvbi9zbWFydC1oZWFsdGgtY2FyZCcgJiYgZmlsZS5lbWJlZGRlZCkubWFwKChmaWxlOiBhbnkpID0+IGZpbGUuZW1iZWRkZWQpKVxyXG4gICAgfVxyXG4gICAgaWYgKCFwYXlsb2FkKSB7IHRocm93IG5ldyBFcnJvcignQ2Fubm90IGdldCBTTUFSVCBIZWFsdGggTGluayBjb250ZW50LicpIH1cclxuICAgIGNvbnN0IHNoY3MgPSBhd2FpdCB0aGlzLmRlY3J5cHRTSExQYXlsb2FkQW5kR2V0U0hDcyhwYXlsb2FkLCBtZXRhZGF0YS5rZXkpXHJcbiAgICBjb25zdCBidW5kbGVzID0gYXdhaXQgUHJvbWlzZS5hbGwoc2hjcy5tYXAoc2hjID0+IHRoaXMuZ2V0U2luZ2xlQnVuZGxlKHNoYykpKVxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgbGFiZWw6IG1ldGFkYXRhLmxhYmVsLFxyXG4gICAgICBzaGM6IHNoY3MsXHJcbiAgICAgIHZlcmlmaWVkOiBhd2FpdCBQcm9taXNlLmFsbChzaGNzLm1hcChfc2hjID0+IHRoaXMudmVyaWZ5U2hjKF9zaGMpKSkudGhlbihyZXN1bHRzID0+IHJlc3VsdHMuZXZlcnkodmFsdWUgPT4gdmFsdWUpKSxcclxuICAgICAgYnVuZGxlOiA8ZmhpcjQuQnVuZGxlPk9iamVjdC5hc3NpZ24odGhpcy5lbXB0eUJ1bmRsZSwge1xyXG4gICAgICAgIGVudHJ5OiBidW5kbGVzLmZpbHRlcihfID0+IF8pLm1hcChidW5kbGUgPT4gYnVuZGxlLmVudHJ5KS5yZWR1Y2UoKGFyciwgZW50cmllcykgPT4gYXJyLmNvbmNhdChlbnRyaWVzKSwgW10pXHJcbiAgICAgIH0pXHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBhc3luYyBkZWNyeXB0U0hMUGF5bG9hZEFuZEdldFNIQ3MocGF5bG9hZDogc3RyaW5nW10sIGtleTogc3RyaW5nKSB7XHJcbiAgICBjb25zdCBTSENzOiBzdHJpbmdbXSA9IFtdXHJcbiAgICBmb3IgKGNvbnN0IGVuY3J5cHRlZCBvZiBwYXlsb2FkKSB7XHJcbiAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBqb3NlLmNvbXBhY3REZWNyeXB0KGVuY3J5cHRlZCwgam9zZS5iYXNlNjR1cmwuZGVjb2RlKGtleSkpXHJcbiAgICAgIFNIQ3MucHVzaCguLi5KU09OLnBhcnNlKG5ldyBUZXh0RGVjb2RlcigpLmRlY29kZShkYXRhLnBsYWludGV4dCkpLnZlcmlmaWFibGVDcmVkZW50aWFsKVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIFNIQ3M7XHJcbiAgfVxyXG5cclxuICBhc3luYyBoYW5kbGVTaGMoc2hjOiBzdHJpbmcpIHtcclxuICAgIGlmIChzaGMuc3RhcnRzV2l0aCgnc2hjOi8nKSkgeyBzaGMgPSBzaGMuc2xpY2UoJ3NoYzovJy5sZW5ndGgpIH1cclxuICAgIHJldHVybiB7XHJcbiAgICAgIHNoYzogW3NoY10sXHJcbiAgICAgIGJ1bmRsZTogYXdhaXQgdGhpcy5nZXRTaW5nbGVCdW5kbGUoc2hjKSxcclxuICAgICAgdmVyaWZpZWQ6IGF3YWl0IHRoaXMudmVyaWZ5U2hjKHNoYylcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICBhc3luYyBnZXRTaW5nbGVCdW5kbGUoc2hjOiBzdHJpbmcpIHtcclxuICAgIHJldHVybiBhd2FpdCB0aGlzLnBhcnNlU2hjKHNoYykudGhlbihkYXRhID0+IGRhdGEudmMuY3JlZGVudGlhbFN1YmplY3QuZmhpckJ1bmRsZSlcclxuICB9XHJcblxyXG4gIHByaXZhdGUgYXN5bmMgcGFyc2VTaGMoc2hjOiBzdHJpbmcpOiBQcm9taXNlPGFueT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgY29uc3QgW2gsIHAsIHNdID0gc2hjLnNwbGl0KCcuJykubWFwKChwYXJ0KSA9PiB0aGlzLmJhc2U2NFVybFRvQnl0ZXMocGFydCkpO1xyXG4gICAgICByZXR1cm4gSlNPTi5wYXJzZSh0aGlzLmluZmxhdGUocCkpO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgcGFyc2luZyBTTUFSVCBIZWFsdGggQ2FyZDonLCBlcnJvcik7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBhc3luYyB2ZXJpZnlTaGMoc2hjOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IGpvc2UuY29tcGFjdFZlcmlmeShzaGMsIGpvc2UuY3JlYXRlTG9jYWxKV0tTZXQoYXdhaXQgdGhpcy5mZXRjaEpXS3Moc2hjKSkpO1xyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGFzeW5jIGZldGNoSldLcyhzaGM6IHN0cmluZyk6IFByb21pc2U8am9zZS5KU09OV2ViS2V5U2V0PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBjb25zdCBjYXJkID0gSlNPTi5wYXJzZSh0aGlzLmluZmxhdGUodGhpcy5iYXNlNjRVcmxUb0J5dGVzKHNoYy5zcGxpdCgnLicpWzFdKSkpO1xyXG4gICAgICBjb25zdCBpc3N1ZXIgPSBjYXJkLmlzcztcclxuICAgICAgY29uc3Qgandrc1VybCA9IGAke2lzc3Vlci5yZXBsYWNlKC9cXC8kLywgJycpfS8ud2VsbC1rbm93bi9qd2tzYDtcclxuXHJcbiAgICAgIGNvbnN0IGp3a3NSZXNwb25zZSA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKHRoaXMuaHR0cC5nZXQ8am9zZS5KU09OV2ViS2V5U2V0Pihqd2tzVXJsKSk7XHJcbiAgICAgIHJldHVybiBqd2tzUmVzcG9uc2U7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBmZXRjaGluZyBKV0tzOicsIGVycm9yKTtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmFibGUgdG8gZmV0Y2ggSldLcyBmb3Igc2lnbmF0dXJlIHZlcmlmaWNhdGlvbicpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBpbmZsYXRlKGRhdGE6IFVpbnQ4QXJyYXkpOiBzdHJpbmcge1xyXG4gICAgdHJ5IHtcclxuICAgICAgY29uc3QgZGVjb21wcmVzc2VkID0gcGFrby5pbmZsYXRlUmF3KGRhdGEsIHsgdG86ICdzdHJpbmcnIH0pO1xyXG4gICAgICByZXR1cm4gZGVjb21wcmVzc2VkO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgaW5mbGF0aW5nIGRhdGE6JywgZXJyb3IpO1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBpbmZsYXRlIGRhdGEnKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgYmFzZTY0VXJsVG9CeXRlcyhiYXNlNjRVcmw6IHN0cmluZyk6IFVpbnQ4QXJyYXkge1xyXG4gICAgY29uc3QgYmFzZTY0ID0gYmFzZTY0VXJsLnJlcGxhY2UoLy0vZywgJysnKS5yZXBsYWNlKC9fL2csICcvJyk7XHJcbiAgICBjb25zdCBiaW5hcnlTdHJpbmcgPSBhdG9iKGJhc2U2NCk7XHJcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWy4uLmJpbmFyeVN0cmluZ10ubWFwKChjaGFyKSA9PiBjaGFyLmNoYXJDb2RlQXQoMCkpKTtcclxuICB9XHJcblxyXG5cclxufVxyXG4iXX0=