Skip to content

Instantly share code, notes, and snippets.

@niquola
Last active March 19, 2026 14:04
Show Gist options
  • Select an option

  • Save niquola/a21c5d584c0aec4e7f10b58e0b0cab97 to your computer and use it in GitHub Desktop.

Select an option

Save niquola/a21c5d584c0aec4e7f10b58e0b0cab97 to your computer and use it in GitHub Desktop.

Сравнение двух описаний SMART on FHIR

Сравнение файлов smart-on-fhir.md (оригинал, основан на спецификации) и smart-on-fhir-detailed.md (обзорный, основан на знаниях модели).

Общая характеристика

smart-on-fhir.md (оригинал) smart-on-fhir-detailed.md (обзорный)
Объём ~450 строк ~380 строк
Разделов 14 15
Источник Спецификация (FHIR KG) Знания модели
Фокус Спецификация изнутри Спецификация снаружи (с контекстом экосистемы и индустрии)

Есть в оригинале, нет в обзорном

Тема Что именно
Capability Sets Конкретные наборы capabilities для 4 типовых сценариев: Patient Access Standalone, Patient Access EHR Launch, Clinician Standalone, Clinician EHR Launch — с перечислением обязательных capabilities для каждого
User Access Brands Целый раздел о том, как организации публикуют свои «визитки» (Brand → Portal → Endpoint), чтобы пациент мог найти своего провайдера в приложении. Иерархия: один бренд → несколько порталов → несколько FHIR-эндпоинтов
Task Launch Профили FHIR Task (task-ehr-launch, task-standalone-launch) для программного запроса запуска SMART-приложения с appContext
App State Экспериментальная возможность хранить настройки приложения в EHR через Basic-ресурс: лимит 256KB, обязательный If-Match/ETag, запрет на клинические данные
Best Practices Конкретные рекомендации: напоминать об offline-доступе, отзывать токен при повторном использовании refresh token, DPoP/UDAP, описывать scopes простым языком
Требование к state Не менее 122 бит энтропии
Обратная совместимость scopes Маппинг v1→v2: .read.rs, .write.cud, .*.cruds
4 сценария использования Чёткая матрица 2×2: пациент/врач × standalone/EHR launch
Refresh token семантика Различие offline_access (работает после выхода) vs online_access (только пока пользователь онлайн) — в обзорном упомянуто в таблице, но не объяснено
Параметр aud Объяснено зачем: защита от утечки токена на поддельный сервер
Регистрация как шаг Выделена как отдельный Шаг 1 flow (однократно, вне полосы)

Есть в обзорном, нет в оригинале

Тема Что именно
Регуляторный контекст 21st Century Cures Act (2016), ONC Final Rule (2020), CMS Interoperability Rules — юридическая обязательность SMART в США
CDS Hooks Описание протокола + конкретный пример: Варфарин + Ибупрофен → предупреждение о кровотечении → suggestion заменить на Парацетамол
SMART Health Cards JWT в QR-коде, Verifiable Credential, FHIR Bundle (Patient + Immunization), офлайн-верификация
Таблица v1 vs v2 Наглядное сравнение по 7 параметрам
Практические реализации Список EHR (Epic, Cerner, Allscripts, MEDITECH), open-source серверов (HAPI, Microsoft, IBM, Google), песочниц (launch.smarthealthit.org, Logica, Epic Sandbox)
Ограничения и критика 5 конкретных проблем: сложность OAuth в медконтексте, неконсистентность реализаций, производительность REST/JSON, широкие scopes, трудность тестирования без EHR
История Josh Mandel, Ken Mandl, 2010 год, Boston Children's Hospital, финансирование ONC
Token Revocation Отдельно описан POST /auth/revoke (в оригинале есть только introspection)

Описано в обоих, но по-разному

Тема Оригинал Обзорный
Discovery Более полный JSON-пример (включает introspection_endpoint, capabilities список) Более полный JSON (включает scopes_supported, code_challenge_methods_supported)
Scopes Подробнее: принцип делегирования прав («скоупы не расширяют права пользователя»), конкретные примеры в таблице Подробнее: granular scopes с query-параметрами (?category=vital-signs)
Типы клиентов Подробнее: конкретный JWT header/payload для asymmetric auth, обязательные алгоритмы RS384/ES384 Короче, но яснее структурирован
Backend Services Подробнее: 4 конкретных use case, отличия от App Launch списком, нет refresh tokens Короче, но с полным code example
Introspection Подробнее: полный JSON-ответ с client_id, fhirUser, контекст launch Короче: минимальный JSON
Итоговая диаграмма ASCII-дерево всей архитектуры (App Launch / Backend / вспомогательные) Простая трёхуровневая диаграмма App → Auth+FHIR → EHR

Фактические расхождения

Прямых противоречий нет — оба описания корректны. Разница в глубине и фокусе.


Вердикт

Оригинал лучше как техническое справочное описание. Он точнее, полнее покрывает спецификацию (Brands, App State, Task Launch, Capability Sets), и основан на первоисточнике.

Обзорный лучше как вводный материал. Он даёт больше контекста (регуляторика, история, экосистема), более доступен для читателя, не знакомого с FHIR, и включает практическую информацию (где попробовать, какие EHR поддерживают).

Идеальный вариант — объединить: взять структуру и техническую точность оригинала, добавить из обзорного регуляторный контекст, сравнение v1/v2, CDS Hooks, практические реализации и критику.

SMART on FHIR — детальный обзор

1. Предыстория и мотивация

До появления SMART on FHIR интеграция с медицинскими системами выглядела так:

  • Каждый EHR-вендор (Epic, Cerner, Allscripts, MEDITECH) имел собственный проприетарный API
  • Разработчику приложения нужно было писать отдельную интеграцию под каждую систему
  • Авторизация реализовывалась кастомно — от VPN-туннелей до хардкод-паролей
  • Смена EHR-системы в клинике означала потерю всех сторонних приложений

Идея SMART (Substitutable Medical Applications, Reusable Technologies) родилась в 2010 году в Harvard Medical School / Boston Children's Hospital под руководством Josh Mandel и Ken Mandl. Первоначально проект финансировался ONC (Office of the National Coordinator for Health IT).

Цель: создать "app store" для медицины — экосистему, где приложения подключаются к любой EHR так же просто, как веб-приложения авторизуются через Google/Facebook.


2. Архитектура

SMART on FHIR стоит на трёх столпах:

┌─────────────────────────────────────────────┐
│              SMART Application              │
│         (веб, мобильное, десктоп)           │
└──────────┬──────────────────┬───────────────┘
           │                  │
           ▼                  ▼
┌──────────────────┐ ┌───────────────────────┐
│  OAuth 2.0 /     │ │   FHIR REST API       │
│  Authorization   │ │   (данные пациентов)  │
│  Server          │ │                       │
└──────────────────┘ └───────────────────────┘
           │                  │
           ▼                  ▼
┌─────────────────────────────────────────────┐
│           EHR / Clinical System             │
└─────────────────────────────────────────────┘

Столп 1 — FHIR как язык данных. Все клинические данные передаются в формате FHIR-ресурсов через REST API.

Столп 2 — OAuth 2.0 как протокол авторизации. Стандартный, хорошо изученный механизм, адаптированный для медицинского контекста.

Столп 3 — Launch Context как механизм передачи клинического контекста (кто пациент, кто врач, какой encounter).


3. Discovery — как приложение узнаёт возможности сервера

Прежде чем начать авторизацию, приложение должно узнать, какие возможности поддерживает FHIR-сервер. Для этого используется Well-Known URI:

GET https://ehr.example.com/fhir/.well-known/smart-configuration

Ответ (JSON):

{
  "authorization_endpoint": "https://ehr.example.com/auth/authorize",
  "token_endpoint": "https://ehr.example.com/auth/token",
  "capabilities": [
    "launch-ehr",
    "launch-standalone",
    "client-public",
    "client-confidential-symmetric",
    "client-confidential-asymmetric",
    "context-ehr-patient",
    "sso-openid-connect",
    "permission-v2"
  ],
  "scopes_supported": ["openid", "fhirUser", "launch", "patient/*.rs", "user/*.rs"],
  "code_challenge_methods_supported": ["S256"]
}

Альтернативно, эта информация доступна через FHIR CapabilityStatement (расширение security).


4. Сценарии запуска (Launch)

4.1 EHR Launch

Приложение запускается изнутри EHR. Типичный сценарий: врач работает с картой пациента и кликает на кнопку/иконку приложения.

Шаг 1: EHR открывает URL приложения с параметрами:
        https://app.example.com/launch?iss=https://ehr.example.com/fhir&launch=abc123

Шаг 2: Приложение извлекает iss (адрес FHIR-сервера) и launch (одноразовый токен)

Шаг 3: Приложение обращается к .well-known/smart-configuration
        чтобы узнать authorization_endpoint

Шаг 4: Приложение перенаправляет пользователя на авторизацию:
        GET /auth/authorize?
          response_type=code&
          client_id=my-app&
          redirect_uri=https://app.example.com/callback&
          scope=launch patient/Observation.rs openid fhirUser&
          launch=abc123&
          state=xyz789&
          code_challenge=...&
          code_challenge_method=S256

Шаг 5: Authorization Server проверяет launch-токен,
        определяет контекст (пациент, encounter),
        спрашивает согласие если нужно

Шаг 6: Редирект обратно в приложение с authorization code:
        https://app.example.com/callback?code=AUTH_CODE&state=xyz789

Шаг 7: Приложение обменивает code на access token:
        POST /auth/token
        grant_type=authorization_code&
        code=AUTH_CODE&
        redirect_uri=...&
        code_verifier=...

Шаг 8: Ответ с токеном и контекстом:
        {
          "access_token": "eyJ...",
          "token_type": "Bearer",
          "expires_in": 3600,
          "scope": "launch patient/Observation.rs openid fhirUser",
          "patient": "Patient/123",
          "encounter": "Encounter/456",
          "id_token": "eyJ..."
        }

Ключевой момент: поле patient в ответе токена — это контекст, переданный из EHR. Приложению не нужно спрашивать "какой пациент?" — EHR уже сообщил это.

4.2 Standalone Launch

Приложение запускается самостоятельно, вне контекста EHR. Типичный сценарий: пациент открывает мобильное приложение для просмотра своих медицинских данных.

Отличия от EHR Launch:

  • Нет параметра launch — приложение само знает iss
  • Приложение запрашивает scope launch/patient чтобы получить picker выбора пациента
  • Пользователь сам выбирает пациента (или Authorization Server определяет его автоматически, если это пациент-портал)
GET /auth/authorize?
  response_type=code&
  client_id=my-app&
  redirect_uri=...&
  scope=launch/patient patient/Observation.rs openid fhirUser&
  aud=https://ehr.example.com/fhir&
  state=...&
  code_challenge=...&
  code_challenge_method=S256

5. Scopes — система разрешений

Scopes определяют, что именно приложение может делать с данными. SMART v2 использует формат:

<context>/<resourceType>.<interactions>

Контексты

Контекст Значение
patient Доступ ограничен данными конкретного пациента из контекста
user Доступ определяется правами текущего пользователя (врач может видеть многих пациентов)
system Backend-to-backend доступ без пользователя

Interactions (SMART v2)

Код Операция
c create
r read
u update
d delete
s search

Примеры:

patient/Observation.rs       — читать и искать наблюдения текущего пациента
user/Patient.cruds           — полный доступ к пациентам в рамках прав пользователя
patient/MedicationRequest.r  — только чтение назначений текущего пациента
system/*.rs                  — backend-сервис читает и ищет все ресурсы

Специальные scopes

Scope Назначение
openid Получить id_token (идентификация пользователя через OpenID Connect)
fhirUser Получить ссылку на FHIR-ресурс пользователя (Practitioner/123 или Patient/456)
launch Принять EHR launch контекст
launch/patient Запросить выбор пациента при standalone launch
launch/encounter Запросить контекст encounter
offline_access Получить refresh token для длительного доступа

Granular Scopes (SMART v2)

SMART v2 добавил возможность фильтрации внутри scope:

patient/Observation.rs?category=http://terminology.hl7.org/CodeSystem/observation-category|vital-signs

Это означает: "читать и искать только витальные показатели пациента, а не все Observation".


6. Типы клиентов

Public Client (публичный)

  • Не может хранить секреты (JavaScript SPA, мобильное приложение)
  • Обязательно использует PKCE (Proof Key for Code Exchange)
  • Не имеет client_secret
  • Получает короткоживущие токены

Confidential Client — Symmetric

  • Серверное приложение, хранит client_secret
  • Аутентифицируется через client_secret_post или client_secret_basic

Confidential Client — Asymmetric (рекомендуемый в v2)

  • Использует JSON Web Key пару (RSA или EC)
  • Аутентифицируется через client_assertion (JWT, подписанный приватным ключом)
  • Публичный ключ регистрируется при регистрации приложения (или через JWKS URL)
  • Наиболее безопасный вариант — секрет никогда не передаётся по сети
POST /auth/token
  grant_type=authorization_code&
  code=...&
  client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
  client_assertion=eyJhbGciOiJSUzM4NCIsImtpZCI6Im15LWtleSJ9...

7. Backend Services (system-to-system)

Для серверных интеграций без участия пользователя (ETL, аналитика, CDS Hooks) используется SMART Backend Services:

1. Сервис создаёт JWT assertion:
   {
     "iss": "client_id",
     "sub": "client_id",
     "aud": "https://ehr.example.com/auth/token",
     "exp": 1616239022,
     "jti": "unique-id"
   }
   → подписывает приватным ключом

2. Запрос токена:
   POST /auth/token
   grant_type=urn:ietf:params:oauth:grant-type:client-credentials&
   scope=system/Patient.rs system/Observation.rs&
   client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
   client_assertion=eyJ...

3. Ответ:
   {
     "access_token": "eyJ...",
     "token_type": "Bearer",
     "expires_in": 300,
     "scope": "system/Patient.rs system/Observation.rs"
   }

Нет redirect, нет пользователя, нет refresh token — только короткоживущий access token.


8. OpenID Connect — идентификация пользователя

Когда приложение запрашивает scopes openid и fhirUser, оно получает id_token — JWT, содержащий:

{
  "iss": "https://ehr.example.com",
  "sub": "user-unique-id",
  "aud": "my-app-client-id",
  "exp": 1616239022,
  "iat": 1616235422,
  "fhirUser": "https://ehr.example.com/fhir/Practitioner/789"
}

Поле fhirUser — ссылка на FHIR-ресурс пользователя. Это позволяет приложению:

  • Узнать имя, роль, специальность врача
  • Различать поведение для врачей, медсестёр, пациентов
  • Привязать действия к конкретному пользователю для аудита

9. Token Introspection и Revocation

Introspection — Resource Server может проверить токен, не расшифровывая его:

POST /auth/introspect
  token=eyJ...

Ответ:
{
  "active": true,
  "scope": "patient/Observation.rs",
  "patient": "Patient/123",
  "exp": 1616239022
}

Revocation — приложение может отозвать токен (при logout):

POST /auth/revoke
  token=eyJ...

10. SMART App Launch v1 vs v2

Аспект v1 v2
Scopes patient/Observation.read patient/Observation.rs (гранулярные)
PKCE Рекомендуется Обязателен для public clients
Асимметричная auth Не стандартизирована Полностью описана
Granular scopes Нет Фильтрация по category, code и др.
Token introspection Не описана Стандартизирована
FHIR версия DSTU2, STU3, R4 R4+
Статус Устарел Текущий стандарт (STU 2.0)

11. Регуляторный контекст (США)

SMART on FHIR стал юридически значимым:

  • 21st Century Cures Act (2016) — закон, запрещающий information blocking
  • ONC Final Rule (2020) — требует от сертифицированных EHR поддержку FHIR R4 API с авторизацией SMART on FHIR
  • CMS Interoperability Rules — страховые компании (payers) обязаны предоставлять Patient Access API на основе FHIR + SMART

Практически: начиная с 2022-2023 года все крупные EHR в США (Epic, Cerner/Oracle Health, Allscripts, MEDITECH) обязаны поддерживать SMART on FHIR.


12. CDS Hooks — расширение экосистемы

CDS Hooks (Clinical Decision Support Hooks) — связанный стандарт, который использует SMART on FHIR для авторизации:

1. Врач открывает назначение лекарства в EHR
2. EHR отправляет webhook на CDS-сервис:
   POST https://cds.example.com/cds-services/drug-interactions
   {
     "hook": "medication-prescribe",
     "fhirServer": "https://ehr.example.com/fhir",
     "fhirAuthorization": { "access_token": "eyJ..." },
     "context": { "patientId": "123", "medications": [...] }
   }

3. CDS-сервис анализирует, возвращает "cards":
   {
     "cards": [{
       "summary": "Взаимодействие: Варфарин + Ибупрофен",
       "indicator": "critical",
       "detail": "Повышенный риск кровотечения...",
       "suggestions": [{ "label": "Заменить на Парацетамол" }]
     }]
   }

4. EHR показывает предупреждение врачу

13. Практические реализации

Крупнейшие EHR с поддержкой SMART:

  • Epic — App Orchard / Open Epic, крупнейшая экосистема SMART-приложений
  • Cerner (Oracle Health) — Code Console, полная поддержка v1 и v2
  • Allscripts — Open API
  • MEDITECH — Greenfield platform

Open-source FHIR-серверы с поддержкой SMART:

  • HAPI FHIR (Java) — с модулем авторизации
  • Microsoft FHIR Server (C#) — Azure Health Data Services
  • IBM FHIR Server (Java, сейчас LinuxForHealth)
  • Google Cloud Healthcare API — managed FHIR с SMART

Тестовые песочницы:

  • SMART Health IT Sandbox (launch.smarthealthit.org) — для разработки и тестирования
  • Logica Health (ранее HSPC) — открытые песочницы
  • Epic FHIR Sandbox — тестовые данные Epic

14. SMART Health Cards (бонус)

Отдельная, но связанная инициатива — SMART Health Cards (использовались для COVID-сертификатов):

  • Verifiable Credential в формате JWT, сжатый и упакованный в QR-код
  • Содержит FHIR Bundle (Patient + Immunization)
  • Подписан ключом организации (проверяемый через JWKS)
  • Не требует онлайн-подключения для верификации

Это не SMART on FHIR в классическом смысле, но разделяет философию: FHIR как формат данных + криптография для доверия.


15. Ограничения и критика

  • Сложность внедрения — OAuth 2.0 в медицинском контексте нетривиален, много edge cases
  • Неконсистентность реализаций — каждый EHR реализует стандарт с нюансами (особенно scopes и launch)
  • Производительность — REST/JSON не оптимален для bulk-операций (решается через FHIR Bulk Data Access)
  • Безопасность — токены с широкими scopes (patient/*.cruds) дают слишком много доступа; granular scopes v2 решают это частично
  • Тестирование — сложно тестировать launch flow без реального EHR

Резюме

SMART on FHIR = OAuth 2.0 (авторизация) + FHIR (данные) + Launch Framework (контекст). Это превратило медицинские системы из закрытых монолитов в платформы с экосистемой приложений, аналогично тому, как App Store изменил мобильные телефоны. Стандарт стал регуляторным требованием в США и де-факто международным подходом к интеграции клинических приложений.

SMART on FHIR — полный рассказ

Что это и зачем

SMART (Substitutable Medical Applications, Reusable Technologies) on FHIR — это спецификация, которая решает одну фундаментальную проблему: как стороннее приложение может безопасно получить доступ к медицинским данным в EHR-системе?

До SMART каждая EHR-система имела свой проприетарный способ интеграции приложений. Врач или пациент, желающий использовать стороннее приложение (визуализация данных, поддержка принятия решений, мобильный портал), был привязан к экосистеме конкретного вендора. SMART создаёт единый стандарт, позволяющий написать приложение один раз и запускать его на любой совместимой EHR-системе — по аналогии с тем, как веб-приложения работают в любом браузере.

Технически SMART — это профиль поверх OAuth 2.0, адаптированный для здравоохранения. Он не изобретает авторизацию с нуля, а берёт промышленный стандарт и добавляет к нему то, что нужно в медицинском контексте: передачу контекста пациента, гранулярные права на FHIR-ресурсы, стандартизированный запуск приложений.


Четыре сценария использования

SMART App Launch Framework поддерживает четыре ключевых сценария:

  1. Приложение пациента, запускаемое самостоятельно — пациент открывает мобильное приложение на своём телефоне
  2. Приложение пациента, запускаемое из портала — пациент кликает на приложение внутри Patient Portal
  3. Приложение врача, запускаемое самостоятельно — врач открывает аналитическое приложение на своём компьютере
  4. Приложение врача, запускаемое из EHR — врач кликает на приложение внутри интерфейса электронной карты

Эти сценарии покрывают визуализацию данных, сбор данных, клиническую поддержку решений (CDS), обмен данными, эпидемиологическую отчётность и многое другое.


Два режима запуска

EHR Launch

В режиме EHR Launch пользователь уже работает в EHR-системе и решает запустить приложение. EHR открывает новое окно браузера (или iframe), указывающее на зарегистрированный URL приложения, и передаёт два параметра:

  • iss — FHIR-эндпоинт EHR-системы
  • launch — непрозрачный идентификатор текущего контекста (какой пациент открыт, какой encounter активен)
https://app/launch?iss=https%3A%2F%2Fehr%2Ffhir&launch=xyz123

Приложение потом включает этот launch-токен в запрос авторизации, и сервер возвращает контекст (ID пациента, encounter и т.д.) вместе с access token.

Standalone Launch

В режиме Standalone Launch приложение запускается извне — пациент открывает мобильное приложение или врач переходит по закладке. Контекст не передаётся заранее; вместо этого приложение запрашивает у сервера установить контекст в процессе авторизации. Например, scope launch/patient просит сервер предложить пользователю выбрать пациента.


Пошаговый flow авторизации

Полный процесс SMART App Launch состоит из семи шагов:

Шаг 1. Регистрация приложения (однократно, вне полосы)

Приложение регистрируется в авторизационном сервере EHR: фиксирует launch URL, redirect URI, тип клиента. Для confidential-клиентов устанавливается секрет или набор ключей (JWKS).

Шаг 2. Запуск

EHR Launch (с iss + launch) или Standalone Launch (приложение запускается само).

Шаг 3. Discovery

Приложение запрашивает GET {fhir-base}/.well-known/smart-configuration и получает JSON с ключевыми URL:

{
  "authorization_endpoint": "https://ehr.example.com/auth/authorize",
  "token_endpoint": "https://ehr.example.com/auth/token",
  "introspection_endpoint": "https://ehr.example.com/user/introspect",
  "scopes_supported": ["openid", "launch", "patient/*.rs", "offline_access"],
  "capabilities": ["launch-ehr", "client-public", "permission-v2"],
  "code_challenge_methods_supported": ["S256"]
}

Шаг 4. Получение authorization code

Приложение перенаправляет браузер на authorization_endpoint с параметрами:

  • response_type=code
  • client_id — идентификатор приложения
  • redirect_uri — куда вернуть пользователя
  • scope — запрашиваемые права (например, launch/patient patient/Observation.rs openid fhirUser)
  • state — случайное значение для защиты от CSRF (не менее 122 бит энтропии)
  • aud — URL FHIR-сервера (защита от утечки токена на поддельный сервер)
  • code_challenge + code_challenge_method=S256 — PKCE обязателен

Сервер показывает пользователю экран согласия и, при одобрении, редиректит обратно с code.

Шаг 5. Обмен code на access token

Приложение делает POST на token_endpoint:

POST /auth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=abc123&
redirect_uri=https://app/callback&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Для confidential-клиентов добавляется аутентификация (Basic Auth с client_secret или JWT assertion).

Шаг 6. Доступ к FHIR API

С полученным access token приложение обращается к ресурсам:

GET /fhir/Patient/123
Authorization: Bearer eyJ0eXAi...

Шаг 7. Обновление токена

Если приложение запросило offline_access или online_access, оно получает refresh token и может обновлять access token без повторного входа пользователя.


Система скоупов (Scopes)

SMART Scopes — это сердце системы прав доступа. Скоупы делятся на три категории:

1. Скоупы для FHIR-ресурсов

Синтаксис: {контекст}/{Ресурс}.{операции}[?параметры]

Контексты:

  • patient/ — доступ в контексте конкретного пациента
  • user/ — доступ в контексте текущего пользователя (с учётом его привилегий)
  • system/ — системный доступ (для backend-сервисов)

Операции (CRUDS):

  • c — create
  • r — read, vread, history (instance)
  • u — update, patch
  • d — delete
  • s — search, history (type/system level)

Примеры:

Скоуп Что даёт
patient/Observation.rs Чтение и поиск Observation для текущего пациента
user/Patient.cruds Полный доступ к Patient-ресурсам в рамках привилегий пользователя
patient/Observation.rs?category=laboratory Только лабораторные наблюдения (гранулярный v2)
system/Patient.rs Системное чтение всех пациентов (для backend-сервисов)

Для обратной совместимости с v1: .read.rs, .write.cud, .*.cruds.

2. Скоупы для контекста запуска

  • launch — получить контекст при EHR Launch
  • launch/patient — попросить сервер установить контекст пациента
  • launch/encounter — попросить установить контекст encounter

3. Скоупы для identity

  • openid fhirUser — получить информацию о текущем залогиненном пользователе
  • offline_access — refresh token, работающий после выхода пользователя
  • online_access — refresh token, работающий пока пользователь онлайн

Важный принцип: скоупы делегируют права, но не расширяют их. Если у пользователя нет доступа к определённым данным, скоуп user/*.cruds не даст приложению больше прав, чем имеет сам пользователь.


Типы клиентов и аутентификация

Public app

Приложения, которые не могут хранить секрет: браузерные SPA, мобильные приложения. Секрет, встроенный в код, может быть извлечён пользователем. Такие приложения:

  • Не передают client_secret
  • Обязательно используют PKCE для защиты authorization code
  • Пример: HTML5/JS приложение, iOS-приложение

Confidential app — symmetric

Серверные приложения с shared secret. Аутентификация — HTTP Basic Auth:

Authorization: Basic bXktYXBwOm15LWFwcC1zZWNyZXQtMTIz

Подходит для App Launch, но не рекомендуется для Backend Services.

Confidential app — asymmetric

Предпочтительный способ для серьёзных интеграций. Клиент регистрирует свой публичный ключ (через JWKS URL или напрямую) и аутентифицируется подписанным JWT:

JWT Header:

{"typ": "JWT", "alg": "RS384", "kid": "eee9f17a3b598fd86417a980b591fbe6"}

JWT Payload:

{
  "iss": "https://bili-monitor.example.com",
  "sub": "https://bili-monitor.example.com",
  "aud": "https://authorize.smarthealthit.org/token",
  "exp": 1422568860,
  "jti": "random-non-reusable-jwt-id-123"
}

Клиент подписывает JWT своим приватным ключом и передаёт в client_assertion. Сервер верифицирует подпись по зарегистрированному публичному ключу. Обязательная поддержка алгоритмов: RS384 и ES384.


Backend Services

SMART Backend Services — отдельный профиль для серверных (machine-to-machine) интеграций без участия пользователя. Примеры:

  • Аналитическая платформа, периодически импортирующая bulk data из EHR
  • Лабораторный мониторинг, проверяющий результаты и генерирующий алерты
  • Интеграционный сервис, синхронизирующий новых пациентов с внешней базой
  • Система мониторинга загруженности палат, обновляющая данные каждую минуту

Отличия от App Launch:

  • Нет пользовательской авторизации — клиент предварительно авторизован сервером
  • Используется client_credentials grant (не authorization_code)
  • Только asymmetric аутентификация (подписанный JWT)
  • Скоупы обычно system/ (например, system/Observation.rs)
  • Токены короткоживущие (рекомендуется 5 минут), refresh tokens не выдаются

Процесс:

  1. Регистрация клиента + передача публичного ключа
  2. Discovery (/.well-known/smart-configuration)
  3. POST на token endpoint с JWT assertion → получение access token
  4. Использование access token для доступа к FHIR API

Token Introspection

Token Introspection (RFC 7662) позволяет resource-серверам проверять валидность токенов. Это важно в архитектурах, где авторизационный сервер и ресурсный сервер разделены.

Ответ introspection включает:

  • active — жив ли токен
  • scope — выданные права
  • client_id — кому выдан
  • exp — время истечения
  • launch context (patient, intent) — если был передан при выдаче
  • fhirUser, iss, sub — если был выдан id_token
{
  "active": true,
  "client_id": "07a89bd2-52ce-4576-8b85-71698efa8328",
  "scope": "patient/*.read openid fhirUser",
  "patient": "456",
  "fhirUser": "https://ehr.example.org/fhir/Patient/123",
  "exp": 1597678964
}

Conformance и Capability Sets

Сервер объявляет свои возможности через SMART Configuration в /.well-known/smart-configuration. Capabilities группируются в Capability Sets для типовых сценариев:

Patient Access for Standalone Apps:

  • launch-standalone + client-public/client-confidential-symmetric + context-standalone-patient + permission-patient

Patient Access for EHR Launch (из портала):

  • launch-ehr + client-public/client-confidential-symmetric + context-ehr-patient + permission-patient

Clinician Access for Standalone:

  • launch-standalone + клиент + permission-user + permission-patient

Clinician Access for EHR Launch:

  • launch-ehr + клиент + context-ehr-patient + context-ehr-encounter + permission-user + permission-patient

Отдельные capabilities включают: режимы запуска (launch-ehr, launch-standalone), типы клиентов (client-public, client-confidential-symmetric, client-confidential-asymmetric), контексты, разрешения (permission-v1, permission-v2, permission-offline), SSO (sso-openid-connect).


User Access Brands

Одна из новейших частей спецификации — Brands. Она решает проблему UX: когда пациент открывает приложение и хочет подключиться к своему провайдеру, он должен найти его в списке. Brands стандартизирует, как организации публикуют свою «визитку»:

  • Brand (Organization) — название, логотип, адреса, категории (клиника, лаборатория, страховая)
  • Portal — пользовательский портал с названием, URL, описанием
  • Endpoint — FHIR API endpoint, привязанный к порталу

Иерархия гибкая: один бренд может иметь несколько порталов, один портал — несколько FHIR-эндпоинтов (R2 и R4), несколько брендов могут вести к одному порталу (региональная сеть клиник).

Brands публикуются в виде FHIR Bundle и указываются в user_access_brand_bundle в SMART configuration.


Task Launch

Для интеграции с CDS (Clinical Decision Support) определены профили FHIR Task, которые запрашивают запуск SMART-приложения:

  • task-ehr-launch — запрос на EHR Launch с опциональным appContext (аналогично CDS Hooks Link)
  • task-standalone-launch — запрос на Standalone Launch

Это позволяет, например, CDS-движку создать Task «запустите приложение калькулятора дозировки билирубина» с контекстом пациента и encounter.


App State (экспериментальная возможность)

SMART App State позволяет приложениям сохранять небольшой объём конфигурации в EHR через FHIR API (Basic ресурс):

  • Пользовательские настройки — экран по умолчанию, цветовая тема, shortcuts
  • Ключи шифрования — для доступа к внешним зашифрованным данным

Ограничения:

  • Нельзя хранить клинические данные (для них есть FHIR-ресурсы)
  • EHR не обязана интерпретировать хранимые данные
  • Максимум 256KB на ресурс
  • Обязательное управление конкурентностью через If-Match / ETag

Контроль доступа — через SMART scopes: user/Basic.cuds (для пользовательских настроек), patient/Basic.cuds (для данных пациента), с возможностью гранулярного ограничения по state code.


Best Practices

Спецификация выделяет рекомендации для обеих сторон:

Для серверов:

  • Напоминать пользователям, какие приложения имеют offline-доступ
  • Отзывать авторизацию при повторном использовании refresh token (защита от кражи)
  • Рассматривать привязку refresh token к аппаратным ключам (DPoP, UDAP)

Для приложений:

  • Никогда не использовать refresh token повторно
  • При пользовательском присутствии — защищать секреты биометрией
  • Публично документировать политику обращения с данными

Для UX согласия:

  • Описывать скоупы простым языком, не медицинским жаргоном
  • Не пугать пациента словом «Encounters» — объяснять понятно
  • Публиковать скриншоты экранов согласия для разработчиков

Архитектура целиком

                    ┌─────────────────────────────────────┐
                    │         SMART on FHIR                │
                    │      (профиль OAuth 2.0)             │
                    └───────────────┬─────────────────────┘
                                    │
            ┌───────────────────────┼───────────────────────┐
            │                       │                       │
     ┌──────┴──────┐        ┌──────┴──────┐         ┌──────┴──────┐
     │  App Launch  │        │  Backend    │         │  Вспомога-  │
     │  Framework   │        │  Services   │         │  тельные    │
     └──────┬──────┘        └──────┬──────┘         └──────┬──────┘
            │                       │                       │
     ┌──────┴──────┐        ┌──────┴──────┐         ┌──────┴──────┐
     │ EHR Launch   │        │ client_     │         │ Brands      │
     │ Standalone   │        │ credentials │         │ App State   │
     │ Launch       │        │ + JWT       │         │ Task Launch │
     └──────┬──────┘        └─────────────┘         │ Introspect  │
            │                                        └─────────────┘
     ┌──────┴──────┐
     │ Public      │
     │ Confidential│
     │  symmetric  │
     │  asymmetric │
     └─────────────┘

SMART on FHIR — это не просто протокол авторизации. Это целая платформенная модель, позволяющая создать экосистему медицинских приложений: от простого просмотрщика лабораторных результатов на телефоне пациента до enterprise-интеграции, ежеминутно синхронизирующей данные между системами. Спецификация покрывает весь жизненный цикл: discovery, авторизация, доступ к данным, обновление токенов, обнаружение провайдеров, хранение состояния приложений.


Составлено на основе FHIR Knowledge Graph (2939 концептов, 5785 связей, 408 страниц спецификации) — 20 страниц SMART App Launch IG.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment