# SMART on FHIR을 사용한 환자 동의 구현 방법

> 튜토리얼: SMART on FHIR과 OAuth 2.1 및 PKCE 단계별 안내 — 검색, 앱 등록, 동의 화면, 토큰 교환, 범위 적용, 새로고침 회전 및 해지 — 그리고 Anpheros가 구현하는 내용.

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

**SMART on FHIR은 애플리케이션이 개인의 FHIR 기록에 접근할 수 있도록 허가를 받는 표준 방식입니다. 인증을 위한 OAuth 2.1과 PKCE, 읽거나 쓸 수 있는 내용을 정확히 명시하는 범위(scopes), 그리고 애플리케이션이 어디로 가야 하는지 알려주는 검색 문서(discovery document)를 사용합니다.** 이 튜토리얼은 `curl`와 함께 전체 흐름을 Anpheros를 대상으로 단계별로 진행합니다: 검색, 애플리케이션 등록, 동의 화면으로 사용자 전송, 코드 교환, 부여된 범위 내에서 API 호출, 새로고침, 그리고 사용자가 접근을 해지했을 때 발생하는 일. Anpheros는 SMART on FHIR 백엔드입니다. 인증 서버, 동의 화면, FHIR R4 서버를 호스팅하므로 자체적인 로그인 또는 동의 UI를 작성할 필요가 없습니다.

## 동의가 필요한 이유 (API 키만으로는 부족한 이유)

API 키는 사용자의 프로젝트에서 생성된 환자에게 도달합니다. 개인의 기존 기록(다른 앱, 병원, 실험실에서 수집된 자신의 데이터)은 공유할 수 있는 대상입니다. SMART on FHIR을 사용하면 다음과 같은 이점이 있습니다.

- 사용자는 자신의 언어로 어떤 애플리케이션이 무엇을 읽거나 쓰려고 하는지, 어떤 목적으로, 얼마나 오랫동안 요청하는지 볼 수 있습니다.
- 사용자의 애플리케이션은 해당 리소스 유형 및 작업에 대해서만, 그리고 해당 한 개의 기록에 대해서만 접근 권한을 얻습니다.
- 모든 접근은 기록되며 사용자에게 표시되고, 사용자는 언제든지 이를 철회할 수 있습니다.
- 이 권한 부여는 FHIR `Consent` 리소스로도 저장됩니다.

이 모델에 대한 자세한 내용은 [환자 동의 기반 의료 API](https://developers.anpheros.com/ko/guides/healthcare-api-patient-consent) 및 [동의 및 접근 모델](https://developers.anpheros.com/ko/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와 함께 애플리케이션을 추가합니다. 또는 관리자 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", …}
```

시크릿을 안전하게 보관할 수 없고 PKCE에 의존하는 모바일 및 브라우저 앱에는 `public`을 사용합니다. 서버에는 `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입니다. 이는 쌍별로 작동합니다. 즉, 다른 애플리케이션은 다른 ID로 동일한 사용자를 인식합니다.
- `id_token`는 `fhirUser` 클레임이 환자 리소스를 명명하는 OpenID Connect 토큰입니다.
- 코드는 한 번만 작동하며 일치하는 `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/ko/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`를 반환합니다. 사용자가 연결이 종료되었음을 알리고 다시 연결할 수 있는 옵션을 제공하여 이를 처리하십시오. `consent.revoked` [웹훅](https://developers.anpheros.com/ko/guides/webhooks)을 구독하여 실패한 호출을 기다리지 않고 이를 파악하십시오.

## SDK 사용 시

[TypeScript 및 Dart SDK](https://developers.anpheros.com/ko/guides/sdks)는 3단계부터 6단계까지 자동으로 처리합니다: `OAuthFlow.authorizeUrl`는 생성된 PKCE 쌍으로, `exchange`는 콜백 시 자동으로 처리되며, 자동 갱신 및 로테이션 기능도 제공합니다. 전체 예제는 다음과 같습니다: [FHIR 백엔드를 사용한 AI 헬스케어 앱 구축 방법](https://developers.anpheros.com/ko/guides/build-ai-healthcare-app) (TypeScript) 및 [Flutter를 사용한 헬스케어 앱 구축 방법](https://developers.anpheros.com/ko/guides/healthcare-app-flutter) (Dart).

## Anpheros 구현 내용

| SMART 구성 요소 | Anpheros 구현 내용 |
|---|---|
| Discovery | FHIR 기본 API에 대한 `/.well-known/smart-configuration` 구현. 지원되는 엔드포인트, 스코프 및 PKCE 메서드 포함 |
| Launch | 독립 실행형(Standalone) 런치 (애플리케이션 시작 후 액세스 요청); EHR 런치(임상 시스템 내에서 시작)는 제공되지 않음 |
| Authorisation | OAuth 2.1 및 PKCE 필수, Authorization Code Flow 사용 |
| Scopes | SMART v2 환자 스코프, 카테고리 필터가 있는 `patient/<Resource>.<cruds>`, `openid` 및 `fhirUser` 추가 |
| Identity | 환자를 FHIR 리소스로 명명하는 OpenID Connect `id_token` |
| Consent | 환자 언어로 제공되는 호스팅된 동의 화면. 환자가 선택한 기간이 FHIR `Consent`로 미러링됨 |
| Tokens | 30분 액세스 토큰, 동의에 바인딩된 30일 회전 갱신 토큰, 취소 기능 |
| Conformance | Capability Statement에 SMART 보안 서비스 선언. Inferno SMART App Launch STU2 테스트 키트의 "Standalone Launch" 그룹은 22개 테스트 중 22개 통과 (2026년 9월 28일 실행). 다른 Inferno 그룹 및 테스트 키트는 실행되지 않음 |

쓰기 작업도 동일하게 작동합니다. 스코프(`patient/Observation.c`, `.u`, `.d`)를 사용하면 애플리케이션이 레코드 내에서 생성, 업데이트 또는 삭제할 수 있습니다. 작성된 모든 내용은 애플리케이션을 작성자로 기록하며, Idempotency Key를 사용한 재시도는 측정을 중복하지 않습니다. 삭제는 소프트 삭제이며 기록을 유지합니다 ([Medical data API](https://developers.anpheros.com/ko/guides/medical-data-api)).

## 아직 지원되지 않는 기능

- EHR 런치 (병원 시스템 내부에서 런치 컨텍스트와 함께 애플리케이션을 여는 기능).
- 백엔드 서비스용 `system/` 스코프; 대신 API 키를 사용합니다.
- 대량 데이터 내보내기 (`$export`); 환자별 `$everything`은 가능합니다.

이러한 기능들은 [버전 관리 및 사용 중단](https://developers.anpheros.com/ko/guides/versioning)에 다른 기능들과 함께 나열되어 있으며, 기능 명세서(capability statement)가 항상 권위 있는 출처입니다.

## 자주 묻는 질문

### SMART on FHIR이란 무엇인가요?
OAuth 2.0/2.1 및 OpenID Connect를 기반으로 하는 개방형 표준으로, 애플리케이션이 FHIR 레코드에 대한 액세스를 요청하는 방법, 해당 액세스를 설명하는 스코프, 서버가 엔드포인트를 광고하는 방법을 정의합니다. SMART 기능을 갖춘 클라이언트 라이브러리는 SMART 기능을 갖춘 서버를 사용할 수 있습니다.

### PKCE가 필요한가요?
네. Anpheros는 공개 및 기밀 클라이언트 모두에 대해 모든 권한 부여 코드 흐름에 `S256`과 함께 PKCE를 요구합니다. 이는 가로챈 권한 부여 코드가 다른 사람이 사용하지 못하도록 합니다.

### 사용자가 거부하면 어떻게 되나요?
사용자는 `error=access_denied`와 `state`을 가지고 리디렉션 URI로 돌아오며 코드는 없습니다. 해당 기능이 무엇을 필요로 하는지 설명하고 다시 시도하도록 하세요. 권한 부여 외부의 호출은 항상 `403`을 반환합니다.

### 부모가 자녀를 대신하여 동의할 수 있나요?
네. 동의 화면에서 사용자는 계정에 연결된 경우 자녀와 같이 자신이 관리하는 레코드를 선택할 수 있습니다. 권한 부여는 해당 레코드에만 적용됩니다.

## 관련 자료

- [인증 및 OAuth](https://developers.anpheros.com/ko/guides/authentication): API 키, 토큰 및 수명 주기 상세 설명
- [환자 동의를 통한 헬스케어 API](https://developers.anpheros.com/ko/guides/healthcare-api-patient-consent): 애플리케이션 설계에서 동의 변경 사항
- [동의 및 액세스 모델](https://developers.anpheros.com/ko/guides/consent)
- [FHIR 플랫폼](https://developers.anpheros.com/ko/guides/fhir)
- [헬스케어 앱 구축](https://developers.anpheros.com/guides/build-a-healthcare-app)
