├── README.md ├── authorization.md ├── export.md ├── implementations.md ├── sample-jwks ├── ES384.private.json ├── ES384.public.json ├── RS384.private.json ├── RS384.public.json └── authorization-example-jwks-and-signatures.ipynb └── security-risk-assessment-report.pdf /README.md: -------------------------------------------------------------------------------- 1 | # FHIR Bulk Data Access 2 | 3 | ## Note 4 | 5 | This repository is no longer being used for official development of the specification. Please see: 6 | 7 | 1. https://hl7.org/fhir/uv/bulkdata for the formal publication (first published Auguest 2019) 8 | 2. https://github.com/hl7/bulk-data for the GitHub repository in active use, within the HL7 org 9 | 3. https://build.fhir.org/ig/HL7/bulk-data for the up-to-the-minute continuous integration build of the spec 10 | 11 | ## Resources 12 | - [Overview Presentation](https://docs.google.com/presentation/d/14ZHmam9hwz6-SsCG1YqUIQnJ56bvSqEatebltgEVR6c/edit?usp=sharing) 13 | - [Client and Server Implementations](./implementations.md) 14 | - [Argonaut Project: Bulk Data Export Security Risk Assessment Report](./security-risk-assessment-report.pdf) 15 | - [Discussion Group (FHIR Zulip "Bulk Data" Track)](https://chat.fhir.org/#narrow/stream/bulk.20data) 16 | -------------------------------------------------------------------------------- /authorization.md: -------------------------------------------------------------------------------- 1 | # SMART Backend Services: Authorization Guide 2 | 3 | Content is being standardized through HL7 at: https://github.com/HL7/bulk-data/blob/master/spec/authorization/index.md 4 | -------------------------------------------------------------------------------- /export.md: -------------------------------------------------------------------------------- 1 | # FHIR Bulk Data Export Implementation Guide 2 | 3 | Content is being standardized through HL7 at: https://github.com/HL7/bulk-data/blob/master/spec/export/index.md 4 | -------------------------------------------------------------------------------- /implementations.md: -------------------------------------------------------------------------------- 1 | # FHIR Bulk Data Implementations 2 | 3 | ## Server 4 | 5 | - SMART Reference Implementation - NodeJS (supports v0.2) 6 | 7 | https://github.com/smart-on-fhir/bulk-data-server (code) 8 | 9 | https://bulk-data.smarthealthit.org (online) 10 | 11 | - HL7 (supports v0.1) 12 | 13 | https://test.fhir.org/r3 14 | 15 | - Cerner (supports v0.2) 16 | 17 | https://fhir-open.stagingcerner.com/beta/a758f80e-aa74-4118-80aa-98cc75846c76/Patient/$export (open) 18 | 19 | https://fhir-ehr.stagingcerner.com/beta/a758f80e-aa74-4118-80aa-98cc75846c76/token (authorization) 20 | - client ids 21 | - sample_jwks_es384 (if using ES384 [Sample JWKs](sample-jwks)) 22 | - sample_jwks_rs384 (if using RS384 [Sample JWKs](sample-jwks)) 23 | 24 | https://fhir-ehr.stagingcerner.com/beta/a758f80e-aa74-4118-80aa-98cc75846c76/Patient/$export (secure) 25 | 26 | - ONC (supports v0.1) 27 | 28 | http://52.70.192.201/open-fhir/fhir/ (open) 29 | 30 | http://52.70.192.201/secure-fhir/view/newuser.html (registration) 31 | 32 | http://52.70.192.201/secure-fhir/fhir/ (secure) 33 | 34 | - CMS BCDA 35 | 36 | https://sandbox.bcda.cms.gov/api/v1/metadata 37 | 38 | - CMS Blue Button 39 | 40 | https://sandbox.bluebutton.cms.gov/v1/fhir/metadata 41 | 42 | - CMS DPC 43 | https://sandbox.dpc.cms.gov/api/v1/metadata 44 | 45 | ## Client 46 | 47 | - SMART Reference Implementation - NodeJS (supports v0.2) 48 | 49 | https://github.com/smart-on-fhir/sample-apps-stu3/tree/master/fhir-downloader 50 | 51 | - Python client application to stream data from a Bulk FHIR API into BigQuery, determining schema on the fly (supports v0.2) 52 | 53 | https://github.com/jmandel/fhir-bulk-data-to-bigquery 54 | 55 | - Python client with auth support that converts a bulk data server into a generator for lightweight iteration through bulk resources returned (supports v0.2) 56 | 57 | https://github.com/plangthorne/python-fhir (code) 58 | 59 | https://github.com/plangthorne/python-fhir/blob/master/demo/BulkDataDemo.ipynb (demo notebook) 60 | 61 | - Go client that fetches data and stores to local FS, google cloud storage and/or export to bigquery (supports v0.1) 62 | 63 | https://github.com/toby-hu/test/tree/master/client 64 | -------------------------------------------------------------------------------- /sample-jwks/ES384.private.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "EC", 5 | "crv": "P-384", 6 | "x": "pyqburM9y8HPcFhNosDW6ngoVEJitqXi_HJgI5hYqX21PoFDwCbYirGy_o0oapIU", 7 | "y": "95bxkCnMKoHvgbcPAIAcgzzu8scd81mXfM_IzuJFNfkUNkGYBFGvuEU1KPi5j1ob", 8 | "key_ops": [ 9 | "verify" 10 | ], 11 | "ext": true, 12 | "kid": "cd520211e5661dbba2256f67f6d53f97", 13 | "alg": "ES384" 14 | }, 15 | { 16 | "kty": "EC", 17 | "crv": "P-384", 18 | "d": "hQCNmfvZEUjOon8zLc0bULlmrDPFHrieFHRVZUGMuiQscx9IO7MT03TsaCPdPv0u", 19 | "x": "pyqburM9y8HPcFhNosDW6ngoVEJitqXi_HJgI5hYqX21PoFDwCbYirGy_o0oapIU", 20 | "y": "95bxkCnMKoHvgbcPAIAcgzzu8scd81mXfM_IzuJFNfkUNkGYBFGvuEU1KPi5j1ob", 21 | "key_ops": [ 22 | "sign" 23 | ], 24 | "ext": true, 25 | "kid": "cd520211e5661dbba2256f67f6d53f97", 26 | "alg": "ES384" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /sample-jwks/ES384.public.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "EC", 5 | "crv": "P-384", 6 | "x": "pyqburM9y8HPcFhNosDW6ngoVEJitqXi_HJgI5hYqX21PoFDwCbYirGy_o0oapIU", 7 | "y": "95bxkCnMKoHvgbcPAIAcgzzu8scd81mXfM_IzuJFNfkUNkGYBFGvuEU1KPi5j1ob", 8 | "key_ops": [ 9 | "verify" 10 | ], 11 | "ext": true, 12 | "kid": "cd520211e5661dbba2256f67f6d53f97", 13 | "alg": "ES384" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /sample-jwks/RS384.private.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "RSA", 5 | "alg": "RS384", 6 | "n": "wJq2RHIA-7RT6q4go7wjcbHdW7ck7Kz22A8wf-kN7Wi5CWvhFG2_Y7nQp1lDpb2IKMQr-Q4n_vgJ6d5rWPspJpSPY7iffUK4ipQCEbzID5DJ6fQMBZOfCTXyxkuMh3jYGKEF3Ziw2oxbM1H9j-eJAPtrj5stUG6kVoXowegdox-bSjWP0iI5PnkwUNzcekLMug4M3LRluEQgGR9O_BAML6-w3igZ_rZA_gunyrLAMbfmCVaceW5ohLp679kyM7U6W2gDK_NbkDKcINUakVmPeoG5h8RzgGzvGrySR0k0VDFiZv60Ua07DqHTeDGH9e4NV07AECae-oykIj5NDCs3pw", 7 | "e": "AQAB", 8 | "key_ops": [ 9 | "verify" 10 | ], 11 | "ext": true, 12 | "kid": "eee9f17a3b598fd86417a980b591fbe6" 13 | }, 14 | { 15 | "kty": "RSA", 16 | "alg": "RS384", 17 | "n": "wJq2RHIA-7RT6q4go7wjcbHdW7ck7Kz22A8wf-kN7Wi5CWvhFG2_Y7nQp1lDpb2IKMQr-Q4n_vgJ6d5rWPspJpSPY7iffUK4ipQCEbzID5DJ6fQMBZOfCTXyxkuMh3jYGKEF3Ziw2oxbM1H9j-eJAPtrj5stUG6kVoXowegdox-bSjWP0iI5PnkwUNzcekLMug4M3LRluEQgGR9O_BAML6-w3igZ_rZA_gunyrLAMbfmCVaceW5ohLp679kyM7U6W2gDK_NbkDKcINUakVmPeoG5h8RzgGzvGrySR0k0VDFiZv60Ua07DqHTeDGH9e4NV07AECae-oykIj5NDCs3pw", 18 | "e": "AQAB", 19 | "d": "O7k9v6eiSmq2gtUP5fXW_9BplaEK4CEaQhEjtuYrnWyVxCghmVYWvPPHkb0KTwCgkhOSlx4epN-BI3YGz4bCUeZLOF7thcgEtWQD6EAjwT_ifJtihvAppo-GAps2rmN4jtqPmRFZ9csEFLvd5pujThyoU9WIjaJhbzsC2-4AEq6WgCDsjdxJ9AXz379vUaoSFAk_ETMRnFSUP-dCJqi_yUrS5h2Tr6rosKP3I_93tt2p2wtOfIjaq7fYipXS7_daHh7hSehEcRHkHI3faaDKY0UwqJa4icHtbX8KgayP6NPUQ-Xv8GMIii3cksRqktDuODHgqGpfkOCii4loS0B-wQ", 20 | "p": "58yRMh3SBFkK0n9ulWDADANqXVGwozMHf9m5nh0sDFSy9v8dTCvTBbzP2wN4pP0cYhIrPYyeBm724lvp7FFzgIz7u2UUIU_Q2-x5VWPy97ZI-V5_eooC58y8DdNbi89D4TzsTJaraEhcqcFH2gI4R-RP01ViKDg2EOzYu2105xE", 21 | "q": "1LaQOiUsaGO7T7aIKgLxvLs7uEekqoSN3tl-ALZxO8RhUyRYjmtH51aKLq8bublqM3XoXBSA4TVm07qUBmWKfHCCz8QhorDFDVgVlEGUMcdmyBmoH5RaEMj4R19oG7C-emP3TFCfzjlnRELuP3v-HdEe6SxoADQwYzyAjduSYzc", 22 | "dp": "oIbQ_s4cBZrMnd5WbOil1yv-W0YZd8v9I5Nasp8tRBTcI6WlWnz3FQAfSmNrB4eqQlimzWc2gOoT28sfguMdhCcepjZn7HHkCIoJtRMUzmvUua2xxuERBgqJKWH4Aii1r6SLWLb3Wa7TTVRnOBlVdKQujAKTiZr0BmCf75zr2qE", 23 | "dq": "BJA-G-E8SKkLFbS2yx_xC7mAmH2A_N-HI6bK2z0OxNd7twrqk3OdwUrMACBlmeBudNgsufz-ntZEdHpmPpTjGbRYOhjdF95u-9BN9jZJ9Z9vhw912eeW3xFQskdLtnxeOcX3Qj3gj84PdxlwfxAr7XvVC--V85srBpX_tAtn4pU", 24 | "qi": "z24jzWhTRZ-x_zsH2wiSKmqg0wXOWO_BCnHA7lC6mMZCj-mQqY-PrZbrrii46ZoGxKWt12bnlHs5OCHtcwcuLrczXCyZPWImbG6Aqch7GVeChzBhdrnflUgt5Y1TmDLMrFmXUIZ2mSMbyN5xZZ4IwfoAq1fOLvXeQny4er2pyxo", 25 | "key_ops": [ 26 | "sign" 27 | ], 28 | "ext": true, 29 | "kid": "eee9f17a3b598fd86417a980b591fbe6" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /sample-jwks/RS384.public.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "RSA", 5 | "alg": "RS384", 6 | "n": "wJq2RHIA-7RT6q4go7wjcbHdW7ck7Kz22A8wf-kN7Wi5CWvhFG2_Y7nQp1lDpb2IKMQr-Q4n_vgJ6d5rWPspJpSPY7iffUK4ipQCEbzID5DJ6fQMBZOfCTXyxkuMh3jYGKEF3Ziw2oxbM1H9j-eJAPtrj5stUG6kVoXowegdox-bSjWP0iI5PnkwUNzcekLMug4M3LRluEQgGR9O_BAML6-w3igZ_rZA_gunyrLAMbfmCVaceW5ohLp679kyM7U6W2gDK_NbkDKcINUakVmPeoG5h8RzgGzvGrySR0k0VDFiZv60Ua07DqHTeDGH9e4NV07AECae-oykIj5NDCs3pw", 7 | "e": "AQAB", 8 | "key_ops": [ 9 | "verify" 10 | ], 11 | "ext": true, 12 | "kid": "eee9f17a3b598fd86417a980b591fbe6" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /sample-jwks/authorization-example-jwks-and-signatures.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# !pip3 install python-jose\n", 10 | "\n", 11 | "import json\n", 12 | "import jose.jwk\n", 13 | "import jose.jwt\n", 14 | "import jose.constants\n", 15 | "\n", 16 | "def get_signing_key(filename):\n", 17 | " with open(filename) as private_key_file:\n", 18 | " signing_keyset = json.load(private_key_file)\n", 19 | " signing_key = [k for k in signing_keyset[\"keys\"] if \"sign\" in k[\"key_ops\"]][0]\n", 20 | " return signing_key\n", 21 | " \n", 22 | "jwt_claims = {\n", 23 | " \"iss\": \"bili_monitor\",\n", 24 | " \"sub\": \"bili_monitor\",\n", 25 | " \"aud\": \"https://authorize.smarthealthit.org/token\",\n", 26 | " \"exp\": 1422568860,\n", 27 | " \"jti\": \"random-non-reusable-jwt-id-123\"\n", 28 | "}" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "\n", 41 | "# Encoded JWT with RS384 Signature\n" 42 | ] 43 | }, 44 | { 45 | "data": { 46 | "text/plain": [ 47 | "'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzM4NCIsImtpZCI6ImVlZTlmMTdhM2I1OThmZDg2NDE3YTk4MGI1OTFmYmU2In0.eyJpc3MiOiJiaWxpX21vbml0b3IiLCJzdWIiOiJiaWxpX21vbml0b3IiLCJhdWQiOiJodHRwczovL2F1dGhvcml6ZS5zbWFydGhlYWx0aGl0Lm9yZy90b2tlbiIsImV4cCI6MTQyMjU2ODg2MCwianRpIjoicmFuZG9tLW5vbi1yZXVzYWJsZS1qd3QtaWQtMTIzIn0.l2E3-ThahEzJ_gaAK8sosc9uk1uhsISmJfwQOtooEcgUiqkdMFdAUE7sr8uJN0fTmTP9TUxssFEAQnCOF8QjkMXngEruIL190YVlwukGgv1wazsi_ptI9euWAf2AjOXaPFm6t629vzdznzVu08EWglG70l41697AXnFK8GUWSBf_8WHrcmFwLD_EpO_BWMoEIGDOOLGjYzOB_eN6abpUo4GCB9gX2-U8IGXAU8UG-axLb35qY7Mczwq9oxM9Z0_IcC8R8TJJQFQXzazo9YZmqts6qQ4pRlsfKpy9IzyLzyR9KZyKLZalBytwkr2lW7QU3tC-xPrf43jQFVKr07f9dA'" 48 | ] 49 | }, 50 | "execution_count": 2, 51 | "metadata": {}, 52 | "output_type": "execute_result" 53 | } 54 | ], 55 | "source": [ 56 | "print(\"\\n# Encoded JWT with RS384 Signature\")\n", 57 | "rsa_signing_jwk = get_signing_key(\"RS384.private.json\")\n", 58 | "jose.jwt.encode(\n", 59 | " jwt_claims,\n", 60 | " rsa_signing_jwk,\n", 61 | " algorithm='RS384',\n", 62 | " headers={\"kid\": rsa_signing_jwk[\"kid\"]})" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 3, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "\n", 75 | "# Encoded JWT with ES384 Signature\n" 76 | ] 77 | }, 78 | { 79 | "data": { 80 | "text/plain": [ 81 | "'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCIsImtpZCI6ImNkNTIwMjExZTU2NjFkYmJhMjI1NmY2N2Y2ZDUzZjk3In0.eyJpc3MiOiJiaWxpX21vbml0b3IiLCJzdWIiOiJiaWxpX21vbml0b3IiLCJhdWQiOiJodHRwczovL2F1dGhvcml6ZS5zbWFydGhlYWx0aGl0Lm9yZy90b2tlbiIsImV4cCI6MTQyMjU2ODg2MCwianRpIjoicmFuZG9tLW5vbi1yZXVzYWJsZS1qd3QtaWQtMTIzIn0.ijKknbYSIa-Ja6qjErSDakTHaaI--k91ll0z-yRaKeiYESoVGV6Qq6_5FyDMGmX-WQPfs57pDgb1iQAE3YogxqufFDDEbirAijTg8GaUjHuahpdBUuVLe5pdZj7c7BsB'" 82 | ] 83 | }, 84 | "execution_count": 3, 85 | "metadata": {}, 86 | "output_type": "execute_result" 87 | } 88 | ], 89 | "source": [ 90 | "print(\"\\n# Encoded JWT with ES384 Signature\")\n", 91 | "ec_signing_jwk = get_signing_key(\"ES384.private.json\")\n", 92 | "jose.jwt.encode(\n", 93 | " jwt_claims,\n", 94 | " ec_signing_jwk,\n", 95 | " algorithm='ES384',\n", 96 | " headers={\"kid\": ec_signing_jwk[\"kid\"]})" 97 | ] 98 | } 99 | ], 100 | "metadata": { 101 | "kernelspec": { 102 | "display_name": "Python 3", 103 | "language": "python", 104 | "name": "python3" 105 | }, 106 | "language_info": { 107 | "codemirror_mode": { 108 | "name": "ipython", 109 | "version": 3 110 | }, 111 | "file_extension": ".py", 112 | "mimetype": "text/x-python", 113 | "name": "python", 114 | "nbconvert_exporter": "python", 115 | "pygments_lexer": "ipython3", 116 | "version": "3.6.5" 117 | } 118 | }, 119 | "nbformat": 4, 120 | "nbformat_minor": 2 121 | } 122 | -------------------------------------------------------------------------------- /security-risk-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smart-on-fhir/fhir-bulk-data-docs/498327385ecebe074ff2016daf2fd4ed4d9924fd/security-risk-assessment-report.pdf --------------------------------------------------------------------------------