# Как реализовать согласие пациента с SMART on FHIR

> Учебное пособие: SMART on FHIR с OAuth 2.1 и PKCE шаг за шагом — обнаружение, регистрация приложения, экран согласия, обмен токенами, принудительное применение областей действия, ротация обновления и отзыв — и что реализует Anpheros.

Source: https://developers.anpheros.com/ru/guides/smart-on-fhir-backend

**SMART on FHIR — это стандартный способ получения приложением разрешения человека на доступ к его FHIR-записи: OAuth 2.1 с PKCE для авторизации, области действия (scopes), которые точно указывают, что можно читать или записывать, и документ обнаружения (discovery document), который сообщает приложению, куда обращаться.** Это учебное пособие пошагово демонстрирует весь процесс работы с Anpheros, включая `curl`: обнаружение, регистрацию приложения, отправку человека на экран согласия, обмен кодом, вызов API в рамках предоставленных областей действия, обновление и действия при отзыве доступа. Anpheros — это бэкенд SMART on FHIR: он размещает сервер авторизации, экран согласия и сервер FHIR R4, поэтому вам не нужно писать собственный интерфейс входа или согласия.

## Почему согласие, а не просто API-ключ

API-ключ предоставляет доступ к данным пациентов, созданным вашим проектом. Существующие записи человека (его собственные данные, собранные другими приложениями, клиниками и лабораториями) он может предоставлять для совместного использования. С помощью SMART on FHIR:

- человек видит на своем языке, какое приложение запрашивает доступ, что оно хочет прочитать или записать, для какой цели и на какой срок;
- ваше приложение получает только те типы ресурсов и действия, и только для этой одной записи;
- каждый доступ регистрируется и виден человеку, который может отозвать его в любое время;
- разрешение также хранится как ресурс FHIR `Consent`.

Модель, лежащая в основе этого, описана в [Healthcare API with patient consent](https://developers.anpheros.com/ru/guides/healthcare-api-patient-consent) и [Consent and access model](https://developers.anpheros.com/ru/guides/consent).

## Шаг 1: обнаружение

```bash
curl https://platform.anpheros.com/fhir/R4/.well-known/smart-configuration
```

Ответ содержит список конечных точек и информацию о том, что поддерживает сервер (сокращенно):

```json
{"issuer": "https://platform.anpheros.com",
 "authorization_endpoint": "https://platform.anpheros.com/oauth/authorize",
 "token_endpoint": "https://platform.anpheros.com/oauth/token",
 "revocation_endpoint": "https://platform.anpheros.com/oauth/revoke",
 "code_challenge_methods_supported": ["S256"],
 "scopes_supported": ["patient/*.rs", "patient/Patient.r", "patient/Observation.rs", "patient/Observation.rs?category=laboratory",
                      "patient/Observation.rs?category=vital-signs", "patient/Observation.c", "patient/Condition.rs",
                      "patient/MedicationStatement.rs", "patient/DocumentReference.rs", "patient/AllergyIntolerance.rs",
                      "offline_access", "launch/patient", "openid", "fhirUser"],
 "capabilities": ["launch-standalone", "client-public", "client-confidential-symmetric", "permission-patient",
                  "permission-offline", "context-standalone-patient", "sso-openid-connect"]}
```

Клиентские библиотеки SMART считывают этот документ и не требуют дополнительной настройки.

## Шаг 2: регистрация вашего приложения

На [панели управления](https://platform.anpheros.com/dashboard/) откройте ваш проект и добавьте приложение с его URI перенаправления. Или через Admin API:

```bash
curl -X POST https://platform.anpheros.com/admin/projects/$PROJECT/applications \
     -H "Authorization: Bearer $FIREBASE_ID_TOKEN" -H "Content-Type: application/json" -d '{
  "name": "CardioAI", "client_type": "public",
  "redirect_uris": ["https://cardio.example/callback"],
  "purpose": "cardiology follow-up", "privacy_url": "https://cardio.example/privacy"
}'
```

```json
{"id": "app_…", "name": "CardioAI", "client_type": "public", "client_id": "client_Hg6CknNkYlMgN190", "client_secret": null,
 "redirect_uris": ["https://cardio.example/callback"], "purpose": "cardiology follow-up", "status": "active", …}
```

Используйте `public` для мобильных и браузерных приложений, которые не могут хранить секрет и полагаются на PKCE. Используйте `confidential` для сервера; его `client_secret` отображается один раз. Название, назначение и ссылка на политику конфиденциальности — это то, что пользователь видит на экране согласия.

## Шаг 3: Перенаправление пользователя на экран согласия

Создайте пару PKCE. Верификатор остается у вас; передается только его хеш:

```bash
VERIFIER=$(openssl rand -base64 64 | tr -d '=+/\n' | cut -c1-64)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
```

Затем откройте этот адрес в браузере пользователя (переносы строк добавлены для удобства чтения):

```
https://platform.anpheros.com/oauth/authorize?response_type=code
  &client_id=client_Hg6CknNkYlMgN190
  &redirect_uri=https://cardio.example/callback
  &scope=openid fhirUser patient/Observation.rs?category=laboratory patient/Condition.rs offline_access
  &state=8c1e…               ← a random value you check on return
  &code_challenge=$CHALLENGE&code_challenge_method=S256
  &purpose=treatment&lang=en
```

Запрашивайте только самое необходимое. Это приложение считывает результаты лабораторных исследований и диагнозы, ничего более. `offline_access` выдает токен обновления, поэтому пользователю не придется подтверждать согласие каждые 30 минут.

Пользователь входит в Anpheros, читает запрос на понятном языке, выбирает, какую запись предоставить (свою собственную, запись члена семьи, за которым он ухаживает, или запись, созданную вашим приложением для него), и на какой срок предоставляется доступ, затем разрешает или запрещает. Пользователь возвращается по вашему URI перенаправления:

```
https://cardio.example/callback?code=…&state=8c1e…
```

Убедитесь, что `state` соответствует значению, которое вы отправили ранее, прежде чем использовать код.

## Шаг 4: обмен кода на токены

```bash
curl -X POST https://platform.anpheros.com/oauth/token \
  -d grant_type=authorization_code -d client_id=client_Hg6CknNkYlMgN190 \
  -d code=$CODE -d code_verifier=$VERIFIER -d redirect_uri=https://cardio.example/callback
```

```json
{"access_token": "at_…", "token_type": "Bearer", "expires_in": 1800,
 "scope": "patient/Observation.rs?category=laboratory patient/Condition.rs openid fhirUser offline_access",
 "patient": "82bfd321-6a4d-4aea-943a-a62f4d81d419", "grant_id": "grant_…",
 "id_token": "eyJ…", "refresh_token": "rt_…"}
```

- `scope` перечисляет предоставленные разрешения. Сохраните их вместе с токенами и отобразите только те функции, которые они охватывают.
- `patient` — это идентификатор записи для вашего приложения. Он является парным: другое приложение видит того же человека под другим идентификатором.
- `id_token` — это токен OpenID Connect, поле `fhirUser` которого содержит имя ресурса пациента.
- Код работает один раз и только с соответствующим `code_verifier`.

## Шаг 5: вызов API в рамках разрешений

```bash
curl "https://platform.anpheros.com/fhir/R4/Observation?patient=$PATIENT&category=laboratory" -H "Authorization: Bearer $ACCESS_TOKEN"
```

Результаты лабораторных исследований возвращаются как обычно. Запросите что-то, выходящее за рамки предоставленных разрешений, например, лекарства:

```bash
curl "https://platform.anpheros.com/fhir/R4/MedicationStatement?patient=$PATIENT" -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{"resourceType": "OperationOutcome", "issue": [{"severity": "error", "code": "forbidden",
  "diagnostics": "The consent behind this token does not allow 's' on MedicationStatement."}]}
```

Это `403`. Поиски в рамках разрешений также фильтруются: с `Observation.rs?category=laboratory` поиск всех наблюдений возвращает только лабораторные. Любой другой пациент не существует для этого токена (`404`). REST-представление (`/v1`) и [контекстный API](https://developers.anpheros.com/ru/guides/llm-healthcare-data) применяют те же разрешения.

## Шаг 6: обновление

Токены доступа действительны в течение 30 минут. Токены обновления действительны в течение 30 дней и ротируются при каждом использовании:

```bash
curl -X POST https://platform.anpheros.com/oauth/token \
  -d grant_type=refresh_token -d client_id=client_Hg6CknNkYlMgN190 -d refresh_token=$REFRESH_TOKEN
```

Вы получаете новый токен доступа и новый токен обновления. Сохраните новый немедленно. Повторное использование старого токена обновления рассматривается как кража: оно завершается ошибкой `400` и отзывает весь грант.

## Шаг 7: Отзыв доступа

Пользователь может отозвать ваш доступ из Anpheros в любое время, и вы также можете отозвать его самостоятельно, например, когда пользователь отключается в вашем приложении:

```bash
curl -X POST https://platform.anpheros.com/oauth/revoke -d token=$REFRESH_TOKEN -d client_id=client_Hg6CknNkYlMgN190
```

С этого момента каждый вызов с использованием этого grant будет отвечать `401`. Обработайте это, показав пользователю, что соединение завершено, и предложив подключиться снова. Подпишитесь на [вебхук](https://developers.anpheros.com/ru/guides/webhooks) `consent.revoked`, чтобы узнать об этом, не дожидаясь неудачного вызова.

## С помощью SDK

[TypeScript и Dart SDK](https://developers.anpheros.com/ru/guides/sdks) выполняют шаги с 3 по 6 за вас: `OAuthFlow.authorizeUrl` с сгенерированной парой PKCE, `exchange` при обратном вызове и автоматическое обновление с ротацией. Полные примеры: [Как создать приложение для здравоохранения с ИИ и бэкендом FHIR](https://developers.anpheros.com/ru/guides/build-ai-healthcare-app) (TypeScript) и [Как создать приложение для здравоохранения с Flutter](https://developers.anpheros.com/ru/guides/healthcare-app-flutter) (Dart).

## Что реализует Anpheros

| Часть SMART | В Anpheros |
|---|---|
| Discovery | `/.well-known/smart-configuration` на базе FHIR, с поддерживаемыми конечными точками, областями действия (scopes) и методами PKCE |
| Launch | автономный запуск (приложение запускается, затем запрашивает доступ); запуск из EHR в рамках клинической системы не предлагается |
| Authorisation | OAuth 2.1 с обязательным PKCE, поток кода авторизации |
| Scopes | пациентские области действия SMART v2, `patient/<Resource>.<cruds>` с фильтрами по категориям, а также `openid` и `fhirUser` |
| Identity | OpenID Connect `id_token`, называющий пациента как ресурс FHIR |
| Consent | размещенный экран согласия на языке пациента, с продолжительностью, выбранной пациентом, отраженный как FHIR `Consent` |
| Tokens | 30-минутные токены доступа, ротируемые 30-дневные токены обновления, привязанные к согласию, отзыв |
| Conformance | заявление о возможностях объявляет службу безопасности SMART; группа "Standalone Launch" набора тестов Inferno SMART App Launch STU2 проходит 22 из 22 тестов (запуск от 28 сентября 2026 г.); другие группы и наборы тестов Inferno не запускались |

Запись работает так же: запись областей действия (`patient/Observation.c`, `.u`, `.d`) позволяет вашему приложению создавать, обновлять или удалять данные в записи. Все, что записано, несет ваше приложение как автора, повторные попытки с ключом идемпотентности никогда не дублируют измерение, а удаления являются мягкими и сохраняют историю ([Medical data API](https://developers.anpheros.com/ru/guides/medical-data-api)).

## Чего еще нет

- Запуск из EHR (приложение, открываемое из внутренней системы больницы с контекстом запуска).
- `system/` областей действия (scopes) для серверных служб; организации используют вместо них API-ключи.
- Экспорт больших объемов данных (`$export`); экспорт `$everything` по пациенту доступен.

Эти пункты перечислены вместе с остальными в разделе [Версионирование и устаревание](https://developers.anpheros.com/ru/guides/versioning), а заявление о возможностях (capability statement) всегда является авторитетным источником.

## Часто задаваемые вопросы

### Что такое SMART on FHIR?
Открытый стандарт, основанный на OAuth 2.0/2.1 и OpenID Connect, который определяет, как приложение запрашивает доступ к записи FHIR, какие области действия описывают этот доступ и как сервер объявляет свои конечные точки. Любая клиентская библиотека с поддержкой SMART может использовать любой сервер с поддержкой SMART.

### Нужен ли мне PKCE?
Да. Anpheros требует PKCE с `S256` для каждого потока кода авторизации, как для публичных, так и для конфиденциальных клиентов. Это предотвращает использование перехваченного кода авторизации кем-либо другим.

### Что произойдет, если пользователь откажет?
Пользователь вернется на ваш URI перенаправления с `error=access_denied` и вашим `state`, но без кода. Объясните, что требуется для этой функции, и позвольте пользователю попробовать снова; вызовы вне предоставленного разрешения всегда возвращают `403`.

### Может ли родитель дать согласие за ребенка?
Да. На экране согласия пользователь может выбрать запись, о которой он заботится, например, запись ребенка, если она связана с его учетной записью. Предоставленное разрешение применяется только к этой записи.

## Связанные материалы

- [Аутентификация и OAuth](https://developers.anpheros.com/ru/guides/authentication): API-ключи, токены и время их действия подробно
- [API для здравоохранения с согласием пациента](https://developers.anpheros.com/ru/guides/healthcare-api-patient-consent): как согласие влияет на дизайн вашего приложения
- [Модель согласия и доступа](https://developers.anpheros.com/ru/guides/consent)
- [Платформа FHIR](https://developers.anpheros.com/ru/guides/fhir)
- [Создание приложения для здравоохранения](https://developers.anpheros.com/ru/guides/build-a-healthcare-app)
