feat: APIトークン管理API(/api/dashboard/api-keys・QR)の実装 #36

Closed
opened 2026-09-16 13:04:14 +00:00 by joe · 1 comment
Owner

概要

APIトークン機構(v50)のうち、ダッシュボードからのトークン管理APIが未実装です。設計は docs/superpowers/specs/2026-09-16-api-token-design.md の「2. API トークン管理 API」に定義済みですが、src/o2/api/ にルートが存在しません(rg -n 'api-keys' src/o2/api → 0件)。

現状

  • ApiKey モデル・ApiKeyService(CRUD/validate)は実装済み
  • POST /api/auth/api-key-login は #35 で実装済み
  • ダッシュボード UI(issue #29)が利用する管理APIが未実装

未実装エンドポイント(設計より)

メソッド パス 説明
GET /api/dashboard/api-keys 一覧取得
POST /api/dashboard/api-keys 新規生成
PUT /api/dashboard/api-keys/{id} 説明・種別・権限更新
DELETE /api/dashboard/api-keys/{id} 削除
PATCH /api/dashboard/api-keys/{id}/toggle 有効/無効切替
POST /api/dashboard/api-keys/{id}/reissue 再発行
GET /api/dashboard/api-keys/{id}/qr QR 画像 (PNG)

実装時の要件

  • 権限: 管理操作は require_manage 以上、一覧/生成は本人のキーに限定するか要検討
  • テナント/ユーザー境界: 他ユーザーのキーを操作できないこと
  • トークン値は生成時のみ返却(一覧では非表示)
  • QR は o2://connect?base_url=<URL>&jwt=<JWT> 相当の情報を PNG で返す(qrcode_generator.py を利用可能か確認)
  • モデル ApiKey.name は UNIQUE。生成時の衝突をハンドリング

受け入れ条件

  • 上記7エンドポイントが実装され、テストが緑
  • 他ユーザー/他テナントのキーを操作できない
  • トークン値が生成時以外は返らない
  • ダッシュボード(#29)から利用可能

関連

  • Issue #28 / #29(web dashboard / APIトークンUI)
  • Issue #35(api-key-login・sync/upsert 契約統一。完了)
  • 設計: docs/superpowers/specs/2026-09-16-api-token-design.md
## 概要 APIトークン機構(v50)のうち、ダッシュボードからのトークン管理APIが未実装です。設計は `docs/superpowers/specs/2026-09-16-api-token-design.md` の「2. API トークン管理 API」に定義済みですが、`src/o2/api/` にルートが存在しません(`rg -n 'api-keys' src/o2/api` → 0件)。 ## 現状 - `ApiKey` モデル・`ApiKeyService`(CRUD/validate)は実装済み - `POST /api/auth/api-key-login` は #35 で実装済み - ダッシュボード UI(issue #29)が利用する管理APIが未実装 ## 未実装エンドポイント(設計より) | メソッド | パス | 説明 | |---|---|---| | GET | `/api/dashboard/api-keys` | 一覧取得 | | POST | `/api/dashboard/api-keys` | 新規生成 | | PUT | `/api/dashboard/api-keys/{id}` | 説明・種別・権限更新 | | DELETE | `/api/dashboard/api-keys/{id}` | 削除 | | PATCH | `/api/dashboard/api-keys/{id}/toggle` | 有効/無効切替 | | POST | `/api/dashboard/api-keys/{id}/reissue` | 再発行 | | GET | `/api/dashboard/api-keys/{id}/qr` | QR 画像 (PNG) | ## 実装時の要件 - 権限: 管理操作は `require_manage` 以上、一覧/生成は本人のキーに限定するか要検討 - テナント/ユーザー境界: 他ユーザーのキーを操作できないこと - トークン値は**生成時のみ**返却(一覧では非表示) - QR は `o2://connect?base_url=<URL>&jwt=<JWT>` 相当の情報を PNG で返す(`qrcode_generator.py` を利用可能か確認) - モデル `ApiKey.name` は UNIQUE。生成時の衝突をハンドリング ## 受け入れ条件 - [ ] 上記7エンドポイントが実装され、テストが緑 - [ ] 他ユーザー/他テナントのキーを操作できない - [ ] トークン値が生成時以外は返らない - [ ] ダッシュボード(#29)から利用可能 ## 関連 - Issue #28 / #29(web dashboard / APIトークンUI) - Issue #35(api-key-login・sync/upsert 契約統一。完了) - 設計: `docs/superpowers/specs/2026-09-16-api-token-design.md`
Author
Owner

対応完了

APIトークン管理API(バックエンド)を実装しました。

実装エンドポイント

メソッド パス 内容
GET /api/dashboard/api-keys 一覧(トークン値は返さない)
POST /api/dashboard/api-keys 新規生成(トークン値はこの時だけ返す)
PUT /api/dashboard/api-keys/{id} description / type / permission 更新
DELETE /api/dashboard/api-keys/{id} 削除(204)
PATCH /api/dashboard/api-keys/{id}/toggle 有効/無効切替
POST /api/dashboard/api-keys/{id}/reissue 再発行(新トークン値を返す)
GET /api/dashboard/api-keys/{id}/qr 連携用QR(PNG)

セキュリティ / 境界

  • require_manage で保護(未認証401 / VIEWER・ACCOUNTANT 403 / ADMIN・PLATFORM_ADMIN 可)
  • 所有者スコープ: 通常ユーザーは自分のキーのみ。他人のキーは存在を漏らさないよう 404
  • Role.PLATFORM_ADMIN は全件の参照・操作が可能
  • トークン値は生成/再発行レスポンスにのみ含み、一覧では非露出
  • QR のJWTは purpose="api_key" + api_key_id 付き。_get_current_user が毎回キーの有効性を検証するため、無効化・失効が即時反映

QR の内容

o2://connect?base_url=<base_url>&jwt=<JWT>(JWTは1時間有効)

長寿命の連携QRが必要な場合は token 直埋め方式を検討(モジュールdocstringに注記)

変更ファイル

  • src/o2/api/api_key.py(新規)/ tests/test_api_key_management.py(新規・11件)
  • src/o2/api/__init__.py(ルーター登録)/ src/o2/api/auth.py(JWT発行ロジックを issue_token_for_api_key に抽出し共用)
  • pyproject.toml / uv.lock(qrcode[pil] を追加)

テスト結果

tests/test_api_key_management.py 他 対象: 67 passed
フルコアスイート: 6304 passed, 1 xpassed, 0 failed

成果物

  • コミット: 63cf684
  • main マージ: ab07e07(push 済)

残課題

  • ダッシュボードUI「APIトークン」タブ(一覧・生成/編集ダイアログ・生成完了モーダル・QR <img>)は #29 で対応
  • ApiKey.name 重複時は現状500(必要なら409ハンドリング)
  • トークン平文保存のハッシュ化/KMS化(設計上のfuture項目)

上記のとおりバックエンド要件は満たしたためクローズします。

## 対応完了 APIトークン管理API(バックエンド)を実装しました。 ### 実装エンドポイント | メソッド | パス | 内容 | |---|---|---| | GET | `/api/dashboard/api-keys` | 一覧(**トークン値は返さない**) | | POST | `/api/dashboard/api-keys` | 新規生成(トークン値はこの時だけ返す) | | PUT | `/api/dashboard/api-keys/{id}` | description / type / permission 更新 | | DELETE | `/api/dashboard/api-keys/{id}` | 削除(204) | | PATCH | `/api/dashboard/api-keys/{id}/toggle` | 有効/無効切替 | | POST | `/api/dashboard/api-keys/{id}/reissue` | 再発行(新トークン値を返す) | | GET | `/api/dashboard/api-keys/{id}/qr` | 連携用QR(PNG) | ### セキュリティ / 境界 - `require_manage` で保護(未認証401 / VIEWER・ACCOUNTANT 403 / ADMIN・PLATFORM_ADMIN 可) - **所有者スコープ**: 通常ユーザーは自分のキーのみ。他人のキーは存在を漏らさないよう **404** - `Role.PLATFORM_ADMIN` は全件の参照・操作が可能 - トークン値は生成/再発行レスポンスにのみ含み、一覧では非露出 - QR のJWTは `purpose="api_key"` + `api_key_id` 付き。`_get_current_user` が毎回キーの有効性を検証するため、無効化・失効が即時反映 ### QR の内容 `o2://connect?base_url=<base_url>&jwt=<JWT>`(JWTは1時間有効) > 長寿命の連携QRが必要な場合は token 直埋め方式を検討(モジュールdocstringに注記) ### 変更ファイル - `src/o2/api/api_key.py`(新規)/ `tests/test_api_key_management.py`(新規・11件) - `src/o2/api/__init__.py`(ルーター登録)/ `src/o2/api/auth.py`(JWT発行ロジックを `issue_token_for_api_key` に抽出し共用) - `pyproject.toml` / `uv.lock`(`qrcode[pil]` を追加) ### テスト結果 ``` tests/test_api_key_management.py 他 対象: 67 passed フルコアスイート: 6304 passed, 1 xpassed, 0 failed ``` ### 成果物 - コミット: `63cf684` - main マージ: `ab07e07`(push 済) ### 残課題 - ダッシュボードUI「APIトークン」タブ(一覧・生成/編集ダイアログ・生成完了モーダル・QR `<img>`)は **#29** で対応 - `ApiKey.name` 重複時は現状500(必要なら409ハンドリング) - トークン平文保存のハッシュ化/KMS化(設計上のfuture項目) 上記のとおりバックエンド要件は満たしたためクローズします。
joe closed this issue 2026-09-16 13:33:06 +00:00
Sign in to join this conversation.
No labels
h1-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joe/o2#36
No description provided.