medical
Public
smart-on-fhir
oauth2
hl7
fhir
pkce
ehr
SMART on FHIR App Launch -- 簡明指南
SMART on FHIR 應用程式啟動框架的完整流程解析,涵蓋 EHR Launch、Standalone Launch、OAuth2 授權、PKCE、Token 交換等核心機制
|
Ingested 2026-04-06
|
Link copied
概述
SMART on FHIR App Launch 是讓第三方應用程式安全存取電子病歷(EHR)資料的標準框架。基於 OAuth 2.0,支援 FHIR R4 及更新版本。與 fhir-exchange-architecture 中的 Keycloak OAuth2 認證機制密切相關。
四種主要使用情境:
1. 病患 App 獨立啟動(Standalone)
2. 病患 App 從入口網站啟動(EHR Launch)
3. 醫療人員 App 獨立啟動
4. 醫療人員 App 從入口網站啟動
完整流程(七個步驟)
graph TD
A[1. 註冊 App] --> B{2. 啟動方式?}
B -->|EHR Launch| C1[EHR 傳入 iss + launch]
B -->|Standalone| C2[App 自行啟動]
C1 --> D[3. 取得 SMART Configuration]
C2 --> D
D --> E[4. 取得 Authorization Code]
E --> F[5. 交換 Access Token]
F --> G[6. 存取 FHIR API]
G --> H[7. Refresh Token]
H --> G
Step 1: 註冊 App(一次性)
向 EHR 授權伺服器註冊以下資訊:
| 項目 | 說明 |
|---|---|
| Launch URL | App 啟動網址(零或多個) |
| Redirect URI | OAuth 回呼網址(一或多個) |
| Client ID | 由 EHR 發給 App 的識別碼 |
| 認證方式 | Confidential: JWKS 或 Client Secret; Public: 無 |
Step 2: 啟動 App
EHR Launch vs Standalone Launch
| 面向 | EHR Launch | Standalone Launch |
|---|---|---|
| 啟動方式 | 使用者從 EHR 內部啟動 | 使用者獨立開啟 App |
| 參數 | iss(FHIR endpoint)+ launch(不透明識別碼) |
無啟動參數 |
| 上下文 | EHR 預先設定(如當前病患) | 授權時選擇(如病患選擇器) |
| Scope | 包含 launch |
使用 launch/patient 等 |
EHR Launch 範例:
https://app/launch?iss=https%3A%2F%2Fehr%2Ffhir&launch=xyz123
Step 3: 取得 SMART Configuration
向 FHIR Base URL 的 .well-known/smart-configuration 發送 GET 請求:
GET https://ehr/fhir/.well-known/smart-configuration
Accept: application/json
回應包含 authorization_endpoint、token_endpoint 等 OAuth 端點。
Step 4: 取得 Authorization Code
sequenceDiagram
participant App
participant Browser as 瀏覽器
participant AuthServer as 授權伺服器
App->>App: 產生 code_verifier + code_challenge (PKCE)
App->>Browser: 重導到授權端點
Browser->>AuthServer: 授權請求(含 scope, state, code_challenge)
AuthServer->>Browser: 顯示登入/同意頁面
Browser->>AuthServer: 使用者同意
AuthServer->>Browser: 重導回 redirect_uri
Browser->>App: 回傳 code + state
App->>App: 驗證 state 是否匹配
關鍵參數
| 參數 | 說明 |
|---|---|
response_type |
固定值 code |
client_id |
App 識別碼 |
redirect_uri |
與註冊時相同的回呼網址 |
scope |
請求的存取範圍 |
state |
隨機值(至少 122 bits),防 CSRF |
aud |
EHR FHIR 資源伺服器 URL |
code_challenge |
PKCE: BASE64URL(SHA256(code_verifier)) |
code_challenge_method |
固定值 S256 |
常用 Scope
| Scope | 說明 |
|---|---|
patient/Patient.rs |
讀取/搜尋病患資料 |
patient/Observation.rs |
讀取/搜尋觀察記錄 |
patient/*.rs |
讀取/搜尋所有病患資源 |
openid fhirUser |
取得已認證使用者身份 |
launch |
取得 EHR 預設的上下文 |
launch/patient |
請求選擇病患上下文 |
offline_access |
請求離線 Refresh Token |
Step 5: 交換 Access Token
sequenceDiagram
participant App
participant TokenEndpoint as Token 端點
App->>TokenEndpoint: POST(code + code_verifier + redirect_uri)
TokenEndpoint->>TokenEndpoint: 驗證 code_verifier 與 code_challenge
TokenEndpoint->>App: access_token + refresh_token + patient context
Request
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=123abc
&redirect_uri=https://app/after-auth
&code_verifier=original_random_string
&client_id=app-client-id
Response
{
"access_token": "eyJhbGc...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "launch/patient patient/Observation.rs patient/Patient.rs openid fhirUser",
"patient": "123",
"refresh_token": "some-long-refresh-token"
}
重要:Access Token 有效期限建議不超過 1 小時。
Step 6: 存取 FHIR API
GET https://ehr/fhir/Patient/123
Authorization: Bearer eyJhbGc...
資源伺服器驗證:
1. Token 未過期
2. Scope 涵蓋所請求的資源
3. aud 參數匹配
Step 7: Refresh Token
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=some-long-refresh-token
&scope=patient/Observation.rs
回應與 Step 5 相同結構,可能包含新的 refresh_token(取代舊的)。
PKCE(Proof Key for Code Exchange)
PKCE 防止授權碼被攔截後被第三方使用:
graph LR
A[產生隨機 code_verifier] --> B[計算 code_challenge = SHA256 of verifier]
B --> C[授權請求帶 code_challenge]
C --> D[取得 authorization code]
D --> E[Token 請求帶 code_verifier]
E --> F[伺服器驗證 SHA256 of verifier == challenge]
- 所有 SMART App 都必須支援 PKCE
- 伺服器必須支援
S256,禁止支援plain
Public vs Confidential App
| 類型 | 說明 | 範例 |
|---|---|---|
| Confidential | 能保護密鑰的應用 | 伺服器端應用、使用動態註冊的原生 App |
| Public | 無法保護密鑰的應用 | 瀏覽器 SPA、散佈靜態密鑰的原生 App |
安全要點
- 所有敏感資訊只能透過 TLS 傳輸到已認證的伺服器
state參數至少 122 bits 熵值,防止 CSRF- Bearer Token 不可存放在明文 Cookie 中
- Token 存放在 App 專屬儲存區,非系統全域可探索位置
- 跨伺服器資源引用時,不可自動帶入 Access Token(防止 Token 外洩)
- 伺服器應啟用 CORS 支援瀏覽器端應用