下拉重新整理
medical 公開 smart-on-fhir oauth2 hl7 fhir pkce ehr

SMART on FHIR App Launch -- 簡明指南

SMART on FHIR 應用程式啟動框架的完整流程解析,涵蓋 EHR Launch、Standalone Launch、OAuth2 授權、PKCE、Token 交換等核心機制

| 匯入於 2026-04-06 |

概述

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_endpointtoken_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 支援瀏覽器端應用

© 2025-2026 Nickle Cheng Built with Ruby Ruby on Rails