From 2cbef03ef7221ecad5cd7da0e9fb63c79e2845b8 Mon Sep 17 00:00:00 2001 From: warrenchen Date: Mon, 13 Apr 2026 17:00:42 +0900 Subject: [PATCH] feat: Add file access authorization and related scopes to OpenAPI documentation --- docs/DESIGN.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ docs/FLOWS.md | 18 ++++++++++++++++++ docs/OPENAPI.md | 42 ++++++++++++++++++++++++++++++++++++++++++ docs/openapi.yaml | 10 ++++++++++ 4 files changed, 114 insertions(+) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index a53870f..4cbc364 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -12,6 +12,7 @@ ## 2. 架構原則 - OAuth2 + OIDC(Authorization Code + PKCE) - 會員中心只管理 Email 與訂閱狀態 +- 檔案存取授權沿用 OAuth2/JWT/JWKS,並以 scope + claim 做資源邊界控制 - Double Opt-in - 各站自行設計 UI,主要走 API;少數狀況使用 redirect - 多租戶為邏輯隔離,但會員資料跨站共享 @@ -131,6 +132,42 @@ 4) 系統驗證目前密碼正確後更新 password hash 5) 更新成功後刷新目前 session +### 6.9 檔案存取授權(File Access) +1) Upload 採 S2S: + - `A service` 以 `client_credentials` 向 Member Center 取 token + - token 帶 file upload 專用 scope + - `A service` 帶 token 呼叫 access agent / file space + - access agent 以 JWKS 驗簽 JWT,並驗 `iss/aud/exp/scope/tenant_id` +2) Download 採 delegated short-lived token: + - client 向 `A service` 要求下載 + - `A service` 驗商業規則與檔案權限 + - `A service` 取得或簽發短效 download token + - client 帶短效 token 直接向 access agent / file space 請求檔案 + - access agent 驗 token 後放行 + +規則: +- 不直接將一般 S2S access token 暴露給 client 作為下載 token +- download token 應至少帶: + - `aud=file_access_api` + - `scope=files:download.read` + - `tenant_id` + - `file_id` 或 `object_key` + - `method=GET` + - 短效 `exp` + - 建議帶 `jti` +- upload token 應至少帶: + - `aud=file_access_api` + - `scope=files:upload.write` + - `tenant_id` +- access agent 應驗: + - JWT signature(JWKS) + - `iss` + - `aud` + - `exp` + - `scope` + - `tenant_id` + - 檔案識別與 method 是否與 token 一致 + ## 7. API 介面(草案) - GET `/oauth/authorize` - POST `/oauth/token` @@ -164,6 +201,13 @@ - 新增 scope:`newsletter:list.read`、`newsletter:send.write`、`newsletter:send.read`、`newsletter:events.read` - 新增 scope:`newsletter:events.write` - 新增 scope:`newsletter:events.write.global` +- 規劃新增 file access scopes: + - `files:upload.write` + - `files:download.read` + - `files:download.delegate` + - `files:delete` + - `files:metadata.read` +- 規劃新增 audience:`file_access_api` - JWT Access Token 已改為 JWS(`DisableAccessTokenEncryption`),供 Send Engine 以 JWKS 驗簽 ### 租戶端取 Token(Client Credentials) diff --git a/docs/FLOWS.md b/docs/FLOWS.md index 100805c..13163c9 100644 --- a/docs/FLOWS.md +++ b/docs/FLOWS.md @@ -20,6 +20,24 @@ - [API] Send Engine 以 Member Center JWKS 驗簽 token - [API] 驗證 `scope/tenant_id/exp` 通過後才受理任務 +## F-02c 檔案上傳(A service -> File Space) +- [API] `A service` 以 `client_credentials` 向 Member Center 取得 access token +- [API] token 需包含 `files:upload.write` +- [API] token 應包含 `tenant_id`,並以 `aud=file_access_api` 為目標資源 +- [API] `A service` 帶 token 呼叫 access agent / file space 上傳檔案 +- [API] access agent 以 JWKS 驗簽 JWT,並驗 `iss/aud/exp/scope/tenant_id` + +## F-02d 檔案下載(A service -> client -> File Space) +- [API] client 向 `A service` 請求下載檔案 +- [API] `A service` 驗證該 request 是否可讀取指定檔案 +- [API] `A service` 取得或簽發短效 download token +- [API] download token 需至少綁定 `tenant_id + file_id/object_key + method=GET + exp` +- [UI/API] `A service` 將帶短效 token 的下載 URL 回給 client +- [UI/API] client 直接向 access agent / file space 請求檔案 +- [API] access agent 驗 token 後放行 + +註記:下載流程不直接暴露一般 S2S token 給 client。 + ## F-03 忘記密碼 / 重設密碼 - [API] 站點送出 `POST /auth/password/forgot` - [UI] 會員中心頁提交 email 並發送重設信 diff --git a/docs/OPENAPI.md b/docs/OPENAPI.md index 7e5c7be..c7c564a 100644 --- a/docs/OPENAPI.md +++ b/docs/OPENAPI.md @@ -107,6 +107,14 @@ - `newsletter:events.read` - `newsletter:events.write` - `newsletter:events.write.global` +- 規劃新增 file access scopes: + - `files:upload.write` + - `files:download.read` + - `files:download.delegate` + - `files:delete` + - `files:metadata.read` +- 規劃新增 audience: + - `file_access_api` - 發送引擎僅能用上述 scope,禁止 admin 權限 - `POST /subscriptions/disable` 需 Bearer token 且包含下列其一: - `newsletter:events.write`(tenant-scoped) @@ -117,6 +125,40 @@ - `aud` 預設: - Send Engine 流程:`send_engine_api`(可用 `Auth:SendEngineAudience` 覆寫) - Member Center API 流程:`member_center_api`(可用 `Auth:MemberCenterAudience` 覆寫) + - File access 流程:`file_access_api`(建議新增設定) + +### File Access Auth(A service / client / access agent) + +用途: +- `A service` 上傳檔案到 file space +- `A service` 對 client 發放短效下載 URL +- access agent 依 token 決定是否放行上傳或下載 + +規則: +- Upload 採 S2S: + - `A service` 使用 `client_credentials` + - token 至少需帶 `files:upload.write` + - token 應帶 `tenant_id` + - access agent 驗 `iss/aud/exp/scope/tenant_id` +- Download 採 delegated short-lived token: + - 不直接將一般 S2S token 暴露給 client + - 下載 token 至少需帶: + - `aud=file_access_api` + - `scope=files:download.read` + - `tenant_id` + - `file_id` 或 `object_key` + - `method=GET` + - 短效 `exp` + - 建議 `jti` +- access agent 至少應驗: + - JWT signature(JWKS) + - `iss` + - `aud` + - `exp` + - `scope` + - `tenant_id` + - token 內檔案識別與實際 request 是否一致 + - token 內 method 與實際 request 是否一致 ### 回寫原因碼(Send Engine -> Member Center) - `hard_bounce` diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 9293ae7..ddc917d 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -828,6 +828,11 @@ components: newsletter:events.read: Read newsletter events newsletter:events.write: Write newsletter events (tenant scoped) newsletter:events.write.global: Write newsletter events (platform scoped) + files:upload.write: Upload files to file space via access agent + files:download.read: Read file content via delegated short-lived token + files:download.delegate: Delegate short-lived file download authorization + files:delete: Delete files from file space + files:metadata.read: Read file metadata clientCredentials: tokenUrl: /oauth/token scopes: @@ -837,6 +842,11 @@ components: newsletter:events.read: Read newsletter events newsletter:events.write: Write newsletter events (tenant scoped) newsletter:events.write.global: Write newsletter events (platform scoped) + files:upload.write: Upload files to file space via access agent + files:download.read: Read file content via delegated short-lived token + files:download.delegate: Delegate short-lived file download authorization + files:delete: Delete files from file space + files:metadata.read: Read file metadata BearerAuth: type: http scheme: bearer